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.
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.667Entropia 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.585Ganho 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 choiceGini 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.
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
- Construção de uma árvore: divisões, nós e folhas
- Impureza de Gini e ganho de informação
- Controle da profundidade da árvore para evitar sobreajuste
- Visualização e interpretação de árvores de decisão