Machine Learning Academy · Aula

Impureza de Gini e ganho de informação

Calcule a impureza de Gini e a entropia para divisões de amostras e entenda por que a árvore escolhe a divisão que maximiza o ganho de informação.

Aula 2 de 413 etapas

Impureza de Gini e ganho de informação é uma aula grátis de Machine Learning Academy no CoddyKit. Esta é a aula 2 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Machine Learning Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Machine Learning Academy inclui 4 aulas no total.

O problema da divisão: sobre qual característica perguntar?

Ao construir uma árvore de decisão, em cada nó precisamos escolher qual característica e qual limiar produzem a divisão mais útil. O objetivo é criar nós filhos nos quais as amostras sejam o mais puras possível — idealmente, cada filho contém apenas uma classe. Precisamos de uma medida matemática de impureza que indique quão misturadas estão as classes em um nó. Uma impureza menor é melhor: um nó com todas as amostras pertencentes a uma única classe tem impureza zero (pureza perfeita). Duas medidas de impureza amplamente utilizadas são a impureza de Gini e a entropia.

# Impurity measures how mixed the classes are in a node
# Perfect purity: all samples belong to one class -> impurity = 0
# Maximum impurity: classes are equally distributed

import numpy as np

# Node A: all class 0 -> pure
node_a = [0, 0, 0, 0]  # impurity = 0

# Node B: 50/50 mix -> maximally impure
node_b = [0, 0, 1, 1]  # impurity = maximum

# Node C: mostly one class
node_c = [0, 0, 0, 1]  # impurity = low

for name, node in [('A', node_a), ('B', node_b), ('C', node_c)]:
    print(f'Node {name}: classes = {node}')

Impureza de Gini: o critério padrão

A impureza de Gini mede a probabilidade de que uma amostra escolhida aleatoriamente de um nó receba um rótulo incorreto se for rotulada aleatoriamente de acordo com a distribuição de classes desse nó. A fórmula é: Gini = 1 - sum(p_i^2), em que p_i é a proporção da classe i. A impureza de Gini varia de 0 (pura) a 0,5 (divisão igual entre duas classes). Para K classes, o máximo é 1 - 1/K. A impureza de Gini é o critério padrão no DecisionTreeClassifier do scikit-learn porque é computacionalmente eficiente (não usa logaritmos).

import numpy as np

def gini_impurity(y):
    classes, counts = np.unique(y, return_counts=True)
    probabilities = counts / len(y)
    return 1 - np.sum(probabilities ** 2)

# Pure node
print('Pure [0,0,0,0]:', gini_impurity([0,0,0,0]))       # 0.0

# 50/50 split
print('50/50 [0,0,1,1]:', gini_impurity([0,0,1,1]))      # 0.5

# 75/25 split
print('75/25 [0,0,0,1]:', gini_impurity([0,0,0,1]))      # 0.375

# Three classes equal
print('3-class equal:', gini_impurity([0,1,2,0,1,2]))    # ~0.667

Entropia e teoria da informação

Entropia é um conceito emprestado da teoria da informação: ela mede a incerteza ou o conteúdo de informação de uma distribuição. Fórmula: H = -sum(p_i * log2(p_i)). Um nó puro tem entropia 0 (nenhuma incerteza). Uma divisão 50/50 tem entropia 1 (um bit de incerteza — é necessária uma pergunta para determinar a classe). Na prática, entropia e Gini produzem árvores muito semelhantes. A entropia é um pouco mais lenta de calcular (exige log), mas pode gerar divisões melhores quando as distribuições de classes são assimétricas. Use criterion='entropy' no scikit-learn para alternar.

import numpy as np

def entropy(y):
    classes, counts = np.unique(y, return_counts=True)
    probabilities = counts / len(y)
    # Avoid log(0) by filtering zero probabilities
    probs = probabilities[probabilities > 0]
    return -np.sum(probs * np.log2(probs))

print('Pure [0,0,0,0]:', entropy([0,0,0,0]))         # 0.0
print('50/50 [0,0,1,1]:', entropy([0,0,1,1]))        # 1.0 (1 bit)
print('75/25 [0,0,0,1]:', entropy([0,0,0,1]).round(3))  # 0.811
print('3-class equal:', entropy([0,1,2,0,1,2]).round(3)) # 1.585

Ganho de informação: a métrica de qualidade da divisão

O ganho de informação mede quanto uma divisão reduz a impureza. Ele é calculado como a impureza do nó pai menos a impureza média ponderada dos nós filhos: IG = impurity(parent) - (N_left/N * impurity(left) + N_right/N * impurity(right)). A melhor divisão maximiza o ganho de informação: ela produz filhos tão puros quanto possível, ponderados pelo tamanho (portanto, filhos maiores têm mais peso). O construtor da árvore avalia o ganho de informação para cada característica e cada limiar e, em seguida, escolhe a combinação com o maior ganho.

import numpy as np

def gini_impurity(y):
    _, counts = np.unique(y, return_counts=True)
    p = counts / len(y)
    return 1 - np.sum(p**2)

def information_gain(y_parent, y_left, y_right):
    n = len(y_parent)
    n_l, n_r = len(y_left), len(y_right)
    parent_impurity = gini_impurity(y_parent)
    weighted_child  = (n_l/n)*gini_impurity(y_left) + (n_r/n)*gini_impurity(y_right)
    return parent_impurity - weighted_child

y_parent = [0,0,0,1,1,1]  # 50/50 parent
y_left   = [0,0,0]         # pure left
y_right  = [1,1,1]         # pure right

print('IG:', information_gain(y_parent, y_left, y_right))  # 0.5 (perfect split)

Avaliando várias divisões

Para encontrar a melhor divisão, o algoritmo avalia todas as combinações candidatas de característica e limiar e escolhe aquela com o maior ganho de informação. Para um conjunto de dados com N amostras e d características, a árvore avalia até N-1 limiares por característica (pontos médios entre valores consecutivos únicos), resultando em O(N * d) divisões a serem avaliadas por nó. Veja a seguir um exemplo simplificado que mostra como diferentes limiares produzem diferentes ganhos de informação na mesma característica.

import numpy as np

X_feature = np.array([1, 2, 3, 4, 5, 6])
y = np.array([0, 0, 0, 1, 1, 1])

best_threshold, best_ig = None, -1

for threshold in [1.5, 2.5, 3.5, 4.5, 5.5]:
    left_mask  = X_feature <= threshold
    right_mask = ~left_mask
    y_l, y_r = y[left_mask], y[right_mask]
    
    _, cnt_p = np.unique(y, return_counts=True)
    _, cnt_l = np.unique(y_l, return_counts=True) if len(y_l) else (None, [1])
    _, cnt_r = np.unique(y_r, return_counts=True) if len(y_r) else (None, [1])
    
    ig = information_gain(y, y_l, y_r)
    print(f'Threshold {threshold}: IG = {ig:.3f}')
    if ig > best_ig:
        best_ig, best_threshold = ig, threshold

print('Best threshold:', best_threshold, 'with IG:', best_ig)

Gini versus entropia: diferença prática

A impureza de Gini e a entropia produzem árvores quase idênticas na maioria das vezes. As principais diferenças são sutis: a entropia tende a produzir árvores mais equilibradas (ela penaliza mais fortemente as divisões desequilibradas devido ao logaritmo), enquanto Gini tende a isolar a classe mais frequente em um ramo. Computacionalmente, Gini é mais rápido porque evita o cálculo do logaritmo. Na prática, a escolha entre os dois é um hiperparâmetro a ser ajustado — experimente ambos com validação cruzada e escolha o que tiver melhor desempenho no seu conjunto de dados específico.

from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import cross_val_score
from sklearn.datasets import load_breast_cancer

X, y = load_breast_cancer(return_X_y=True)

for criterion in ['gini', 'entropy']:
    tree = DecisionTreeClassifier(criterion=criterion, max_depth=5, random_state=42)
    score = cross_val_score(tree, X, y, cv=10).mean()
    print(f'criterion={criterion}: CV accuracy = {score:.3f}')

# Usually within 0.5% of each other -- not the critical choice

Gini ponderado para problemas multiclasse

A impureza de Gini se estende naturalmente a problemas multiclasse sem modificações: Gini = 1 - sum(p_i^2) funciona para qualquer número de classes. O cálculo do ganho de informação também não muda — impureza ponderada dos filhos menos a impureza do pai. Em um problema com 3 classes, uma folha perfeitamente pura (todas as amostras da classe 2, por exemplo) tem Gini igual a 0. Um nó com proporções iguais das 3 classes tem Gini máximo igual a 2/3. As árvores de decisão são um dos poucos algoritmos que lidam com problemas multiclasse nativamente, sem qualquer modificação — diferentemente da regressão logística, que exige extensões um-contra-o-restante ou softmax.

import numpy as np

def gini_multiclass(y):
    _, counts = np.unique(y, return_counts=True)
    p = counts / len(y)
    return 1 - np.sum(p**2)

# 3-class examples
print('Pure [0,0,0]:', gini_multiclass([0,0,0]))               # 0.0
print('Equal [0,1,2]:', gini_multiclass([0,1,2]).round(3))    # 0.667
print('2 dominant [0,0,1,2]:', gini_multiclass([0,0,1,2]).round(3))  # 0.625

# Max Gini for K classes = 1 - 1/K
for K in [2, 3, 4, 5]:
    print(f'Max Gini for {K} classes: {1 - 1/K:.3f}')

Redução da impureza dentro do scikit-learn

Internamente, no scikit-learn, a árvore armazena em cada nó a impureza antes da divisão e a impureza de cada filho. A diferença, ponderada pela quantidade de amostras, é a redução da impureza (ganho de informação). Esse valor é somado por característica em todos os nós e normalizado para calcular feature_importances_ — a redução total da impureza atribuída a cada característica. Características que aparecem próximas à raiz e abrangem muitas amostras tendem a ter a maior importância, porque cada divisão afeta uma grande fração dos dados.

from sklearn.tree import DecisionTreeClassifier
from sklearn.datasets import load_iris
import numpy as np

X, y = load_iris(return_X_y=True)
tree = DecisionTreeClassifier(criterion='gini', max_depth=3, random_state=42)
tree.fit(X, y)

# Impurity at root and children
print('Root impurity (Gini):', tree.tree_.impurity[0].round(4))
print('Left child impurity:', tree.tree_.impurity[1].round(4))
print('Right child impurity:', tree.tree_.impurity[2].round(4))

# Feature importances = total weighted impurity reduction per feature
print('Feature importances:', tree.feature_importances_.round(3))

O papel de min_impurity_decrease

Por padrão, a árvore divide os nós enquanto houver qualquer redução na impureza e o nó tiver amostras suficientes. O parâmetro min_impurity_decrease adiciona um limite mínimo: uma divisão só é criada se reduzir a impureza em pelo menos esse valor. Isso impede que a árvore faça divisões trivialmente pequenas que memorizem o ruído. Definir min_impurity_decrease=0.01 significa que a árvore só fará uma divisão quando o ganho de informação exceder 0.01. Essa é uma alternativa útil de regularização ao controle direto da profundidade.

from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import cross_val_score
from sklearn.datasets import load_breast_cancer

X, y = load_breast_cancer(return_X_y=True)

for min_ig in [0.0, 0.001, 0.005, 0.01, 0.05]:
    tree = DecisionTreeClassifier(
        min_impurity_decrease=min_ig,
        random_state=42
    )
    score = cross_val_score(tree, X, y, cv=5).mean()
    depth = tree.fit(X, y).get_depth()
    print(f'min_impurity_decrease={min_ig}: depth={depth}, CV acc={score:.3f}')

Comparando divisões entre atributos

Um exemplo resolvido que mostra como a árvore escolhe o melhor atributo e o melhor limite. Dados dois atributos e um limite de divisão para cada um, a árvore calcula o ganho de informação para todas as opções e escolhe a vencedora. Isso ilustra por que as árvores realizam naturalmente uma seleção implícita de atributos: atributos que nunca produzem um ganho de informação alto em nenhum limite jamais serão escolhidos como atributos de divisão, sendo efetivamente ignorados. Isso torna as árvores de decisão robustas a atributos irrelevantes, ao contrário do KNN, que é prejudicado por eles.

import numpy as np

# Toy dataset: X[:,0]=income, X[:,1]=age; y=churn
X = np.array([[100, 25], [120, 30], [40, 22], [50, 28], [90, 35], [30, 40]])
y = np.array([0, 0, 1, 1, 0, 1])

# Try splitting on income at 75
left_y = y[X[:, 0] <= 75]   # [1,1,1]
right_y = y[X[:, 0] > 75]   # [0,0,0]
ig_income = information_gain(y, left_y, right_y)

# Try splitting on age at 30
left_y2 = y[X[:, 1] <= 30]  # [0,0,1,1]
right_y2 = y[X[:, 1] > 30]  # [0,1]
ig_age = information_gain(y, left_y2, right_y2)

print(f'IG(income<=75): {ig_income:.3f}')
print(f'IG(age<=30):    {ig_age:.3f}')
print('Best split:', 'income' if ig_income > ig_age else 'age')

Usando Gini em GridSearchCV

Ao ajustar uma árvore de decisão com GridSearchCV, você pode incluir o parâmetro criterion (gini ou entropia) na grade de parâmetros para permitir que a validação cruzada escolha a melhor opção para o seu conjunto de dados específico. Combine-o com max_depth, min_samples_split e min_samples_leaf na mesma busca. Pesquisar diferentes valores de criterion acrescenta um custo computacional mínimo — apenas duas configurações extras por combinação — e ocasionalmente produz uma melhoria significativa quando as distribuições das classes são muito assimétricas ou o conjunto de dados contém muitas divisões de qualidade semelhante.

from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import GridSearchCV
from sklearn.datasets import load_breast_cancer

X, y = load_breast_cancer(return_X_y=True)

param_grid = {
    'criterion': ['gini', 'entropy'],
    'max_depth': [3, 5, 7, None],
    'min_samples_leaf': [1, 5, 10]
}

grid = GridSearchCV(
    DecisionTreeClassifier(random_state=42),
    param_grid, cv=10, scoring='accuracy', n_jobs=-1
)
grid.fit(X, y)

print('Best criterion:', grid.best_params_['criterion'])
print('Best depth:', grid.best_params_['max_depth'])
print('Best CV accuracy:', grid.best_score_.round(4))

Verificação rápida

Teste sua compreensão dos conceitos de Aprendizado de Máquina com Python apresentados nesta lição.

Recapitulação da lição

Nesta lição, você aprendeu: a impureza de Gini mede o quanto as classes estão misturadas em um nó (fórmula: 1 - soma(p_i^2)), o ganho de informação mede quanto uma divisão reduz a impureza (impureza do nó pai menos a impureza ponderada dos nós filhos) e a árvore sempre escolhe a divisão que maximiza o ganho de informação entre todos os atributos e limites. A seguir, exploraremos o controle da profundidade da árvore para evitar o sobreajuste.

Grátis para começar

Aprenda Python com um tutor de IA — grátis

Escreva e execute código real no seu navegador, obtenha ajuda instantânea de um tutor de IA 24/7 e continue de onde parou na web ou no app.

Cursos
30
Aulas
120

Perguntas Frequentes

A aula “Impureza de Gini e ganho de informação” é grátis?

Sim — o texto completo de “Impureza de Gini e ganho de informação” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Machine Learning Academy, atualize para CoddyKit PRO. O curso de Machine Learning Academy inclui 4 aulas no total.

O que vou aprender em “Impureza de Gini e ganho de informação”?

Calcule a impureza de Gini e a entropia para divisões de amostras e entenda por que a árvore escolhe a divisão que maximiza o ganho de informação. Você pratica Machine Learning Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar Machine Learning Academy?

Nenhuma experiência prévia é necessária. Machine Learning Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 2 de 4.

Quanto tempo leva a aula “Impureza de Gini e ganho de informação”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de Machine Learning Academy?

Sim. Cada aula de Machine Learning Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Construção de uma árvore: divisões, nós e folhas
  2. Impureza de Gini e ganho de informação
  3. Controle da profundidade da árvore para evitar sobreajuste
  4. Visualização e interpretação de árvores de decisão
← Voltar para Machine Learning Academy