W3docs

Árvore de Decisão

Aprenda como funcionam as árvores de decisão, como construir árvores de classificação e regressão em Python com scikit-learn e visualizá-las.

Uma árvore de decisão é um algoritmo supervisionado de aprendizado de máquina que faz previsões aprendendo uma hierarquia de regras if-then-else a partir de dados de treinamento. Cada nó interno testa uma característica, cada ramo representa um resultado desse teste, e cada nó folha contém uma previsão (um rótulo de classe para classificação, ou um valor numérico para regressão).

Este capítulo aborda:

  • Como as árvores de decisão dividem dados usando medidas de impureza (Gini e entropia)
  • Como construir uma árvore de classificação e uma árvore de regressão em Python com scikit-learn
  • Como controlar a profundidade da árvore e evitar overfitting com hiperparâmetros
  • Como visualizar e inspecionar uma árvore treinada
  • Vantagens, limitações e quando usar árvores de decisão

Como uma Árvore de Decisão Divide os Dados

Durante o treinamento, o algoritmo pesquisa cada característica e cada limiar possível para encontrar a divisão que mais reduz a impureza — uma medida de quão misturadas as classes estão em um nó.

Duas medidas de impureza são comuns no scikit-learn:

Impureza de Gini

A impureza de Gini mede a probabilidade de classificar incorretamente uma amostra escolhida aleatoriamente caso ela fosse rotulada de acordo com a distribuição de classes no nó.

Gini(node) = 1 - Σ pᵢ²

Um nó puro (todas as amostras pertencem a uma classe) tem Gini = 0. Um nó com máxima mistura tem Gini próximo de 0,5 para classificação binária.

Entropia e Ganho de Informação

A entropia vem da teoria da informação. É maximizada quando as classes estão igualmente distribuídas e zero quando o nó é puro.

Entropy(node) = -Σ pᵢ log₂(pᵢ)

O ganho de informação é a queda na entropia após uma divisão. O algoritmo escolhe a divisão que produz o maior ganho de informação. No scikit-learn, você escolhe entre as duas medidas pelo parâmetro criterion ("gini" é o padrão).

Divisão Recursiva

A divisão se repete recursivamente em cada nó filho até que uma condição de parada seja atingida: o nó está puro, nenhuma característica melhora a impureza, ou um limite de profundidade/tamanho é alcançado. Isso produz a estrutura de árvore binária.

Árvore de Classificação em Python

O conjunto de dados Iris tem 150 amostras e 4 características numéricas. O objetivo é prever uma das três espécies de flores.

from sklearn.tree import DecisionTreeClassifier
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, classification_report

# Load dataset
data = load_iris()
X, y = data.data, data.target

# Split: 80 % train, 20 % test
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)

# Train — limit depth to 3 to keep the tree readable
clf = DecisionTreeClassifier(criterion="gini", max_depth=3, random_state=42)
clf.fit(X_train, y_train)

# Evaluate
y_pred = clf.predict(X_test)
print(f"Accuracy: {accuracy_score(y_test, y_pred):.2f}")
print(classification_report(y_test, y_pred, target_names=data.target_names))

Saída esperada:

Accuracy: 1.00
              precision    recall  f1-score   support

      setosa       1.00      1.00      1.00        10
  versicolor       1.00      1.00      1.00         9
   virginica       1.00      1.00      1.00        11

    accuracy                           1.00        30
   macro avg       1.00      1.00      1.00        30
weighted avg       1.00      1.00      1.00        30

O conjunto de dados Iris é linearmente separável com profundidade 3, por isso a árvore atinge acurácia perfeita no teste. Conjuntos de dados reais serão mais complexos.

Prevendo Novas Amostras

Após o treinamento, chame predict() para classificar novas observações e predict_proba() para obter as probabilidades de cada classe:

import numpy as np

# A new flower: sepal length 5.1, sepal width 3.5, petal length 1.4, petal width 0.2
new_sample = np.array([[5.1, 3.5, 1.4, 0.2]])

predicted_class = clf.predict(new_sample)
predicted_proba = clf.predict_proba(new_sample)

print("Predicted class:", data.target_names[predicted_class[0]])
print("Class probabilities:", predicted_proba)

Saída esperada:

Predicted class: setosa
Class probabilities: [[1. 0. 0.]]

Árvore de Regressão em Python

Árvores de decisão também lidam com alvos contínuos. Use DecisionTreeRegressor em vez de DecisionTreeClassifier.

from sklearn.tree import DecisionTreeRegressor
from sklearn.datasets import make_regression
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error, r2_score
import numpy as np

# Synthetic regression dataset
X_reg, y_reg = make_regression(
    n_samples=300, n_features=5, noise=20, random_state=42
)

X_train_r, X_test_r, y_train_r, y_test_r = train_test_split(
    X_reg, y_reg, test_size=0.2, random_state=42
)

reg = DecisionTreeRegressor(max_depth=5, random_state=42)
reg.fit(X_train_r, y_train_r)

y_pred_r = reg.predict(X_test_r)

mse = mean_squared_error(y_test_r, y_pred_r)
r2 = r2_score(y_test_r, y_pred_r)
print(f"MSE : {mse:.2f}")
print(f"R²  : {r2:.2f}")

Uma árvore de regressão divide os dados minimizando o erro quadrático médio (MSE) dentro de cada nó e prevê o valor médio do alvo de todas as amostras de treinamento que chegam a uma folha.

Ajustando Hiperparâmetros

Sem limites, uma árvore de decisão crescerá até que cada folha seja pura, memorizando perfeitamente o conjunto de treinamento (overfitting). Os hiperparâmetros controlam a complexidade da árvore:

ParâmetroPadrãoEfeito
max_depthNoneNúmero máximo de níveis. Menor = árvore mais simples.
min_samples_split2Mínimo de amostras necessárias para dividir um nó. Maior = menos divisões.
min_samples_leaf1Mínimo de amostras necessárias em uma folha. Maior = fronteiras mais suaves.
max_featuresNoneNúmero de características a considerar em cada divisão (útil para seleção de características).
criterion"gini"Medida de impureza: "gini" ou "entropy" para classificadores; "squared_error" para regressores.

Use validação cruzada e busca em grade para encontrar a melhor combinação:

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

data = load_iris()
X, y = data.data, data.target

param_grid = {
    "max_depth": [2, 3, 4, 5, None],
    "min_samples_split": [2, 5, 10],
    "criterion": ["gini", "entropy"],
}

grid_search = GridSearchCV(
    DecisionTreeClassifier(random_state=42),
    param_grid,
    cv=5,
    scoring="accuracy",
)
grid_search.fit(X, y)

print("Best params :", grid_search.best_params_)
print(f"Best CV score: {grid_search.best_score_:.3f}")

Saída esperada (os valores podem variar ligeiramente entre versões do scikit-learn):

Best params : {'criterion': 'gini', 'max_depth': 3, 'min_samples_split': 2}
Best CV score: 0.973

Tratando Características Categóricas

Árvores de decisão do scikit-learn exigem entrada numérica. Codifique as colunas categóricas antes do treinamento:

  • Categorias ordinais (ex.: tamanho: pequeno < médio < grande): use OrdinalEncoder.
  • Categorias nominais (ex.: cor: vermelho, verde, azul): use OneHotEncoder para evitar implicar uma ordem.
from sklearn.preprocessing import OrdinalEncoder
import numpy as np

# Encode only the categorical column; keep the numeric column as-is
sizes = np.array([["small"], ["large"], ["medium"], ["large"]])
weights = np.array([1.2, 3.4, 2.1, 4.0])

# Explicit category order: large=0, medium=1, small=2
enc = OrdinalEncoder(categories=[["large", "medium", "small"]])
sizes_encoded = enc.fit_transform(sizes)

X_encoded = np.column_stack([sizes_encoded, weights])
print(X_encoded)

Saída esperada:

[[2.  1.2]
 [0.  3.4]
 [1.  2.1]
 [0.  4. ]]

Consulte o capítulo Dados Categóricos para um passo a passo completo.

Visualizando uma Árvore de Decisão

Inspecionar a estrutura da árvore revela quais características impulsionam mais as divisões e torna o modelo auditável.

Representação em Texto

from sklearn.tree import DecisionTreeClassifier, export_text
from sklearn.datasets import load_iris

data = load_iris()
clf = DecisionTreeClassifier(max_depth=2, random_state=42)
clf.fit(data.data, data.target)

print(export_text(clf, feature_names=list(data.feature_names)))

Saída esperada:

|--- petal length (cm) <= 2.45
|   |--- class: 0
|--- petal length (cm) >  2.45
|   |--- petal width (cm) <= 1.75
|   |   |--- class: 1
|   |--- petal width (cm) >  1.75
|   |   |--- class: 2

Gráfico Visual

import matplotlib.pyplot as plt
from sklearn.tree import DecisionTreeClassifier, plot_tree
from sklearn.datasets import load_iris

data = load_iris()
clf = DecisionTreeClassifier(max_depth=2, random_state=42)
clf.fit(data.data, data.target)

plt.figure(figsize=(10, 5))
plot_tree(
    clf,
    feature_names=data.feature_names,
    class_names=data.target_names,
    filled=True,
    rounded=True,
)
plt.title("Iris Decision Tree (max_depth=2)")
plt.tight_layout()
plt.savefig("iris_tree.png", dpi=150)
plt.show()

filled=True colore cada nó pela sua classe majoritária; tonalidades mais escuras indicam maior pureza de classe.

Importância das Características

Após o treinamento, feature_importances_ atribui a cada característica uma pontuação entre 0 e 1, onde valores mais altos indicam que a característica contribuiu mais para reduzir a impureza em todas as divisões:

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

data = load_iris()
clf = DecisionTreeClassifier(max_depth=3, random_state=42)
clf.fit(data.data, data.target)

importances = clf.feature_importances_
for name, imp in sorted(
    zip(data.feature_names, importances), key=lambda x: x[1], reverse=True
):
    print(f"{name:30s}: {imp:.4f}")

Saída esperada:

petal length (cm)             : 0.5856
petal width (cm)              : 0.4144
sepal length (cm)             : 0.0000
sepal width (cm)              : 0.0000

Características com importância 0 nunca foram usadas em nenhuma divisão e poderiam ser removidas para simplificar o modelo.

Vantagens e Limitações

Quando usar árvores de decisão

  • Você precisa de um modelo interpretável — as regras podem ser impressas em linguagem simples.
  • Seu conjunto de dados contém uma mistura de características numéricas e categóricas (após codificação).
  • Você quer uma baseline rápida antes de experimentar métodos de ensemble.
  • A relação entre as características e o alvo é não linear ou envolve interações.

Limitações

LimitaçãoMitigação
Tende ao overfitting sem ajusteRestrinja max_depth, min_samples_leaf; use validação cruzada
Alta variância (pequenas mudanças nos dados → árvore diferente)Use métodos de ensemble: Random Forest / Bootstrap Aggregation
Viés em direção a características com mais valores únicosUse max_features ou normalize os critérios de divisão
Fraca extrapolação além do intervalo dos dados de treinamentoPrefira modelos lineares para tarefas de extrapolação
Apenas divisões alinhadas aos eixosÁrvores oblíquas existem, mas não estão no scikit-learn

Árvores de Decisão vs. Algoritmos Relacionados

AlgoritmoDiferença principal
Regressão LogísticaFronteira linear; melhor para dados linearmente separáveis; não lida com interações automaticamente
K-Nearest NeighborsBaseado em instâncias; sem modelo explícito; requer escalonamento de características
Árvore de DecisãoNão linear; sem necessidade de escalonamento; altamente interpretável
Random Forest (ver Bootstrap Aggregation)Ensemble de muitas árvores; variância muito menor; menos interpretável

Principais Conclusões

  • As árvores de decisão dividem os dados maximizando o ganho de informação (ou minimizando a impureza de Gini) em cada nó; o processo se repete recursivamente.
  • DecisionTreeClassifier e DecisionTreeRegressor no scikit-learn compartilham a mesma API e os mesmos nomes de hiperparâmetros.
  • Sempre defina max_depth ou min_samples_leaf para evitar overfitting; ajuste-os com busca em grade e validação cruzada.
  • feature_importances_ revela em quais características a árvore se baseia mais — útil para seleção de características.
  • Árvores individuais são uma boa baseline interpretável, mas métodos de ensemble como Random Forest quase sempre superam o desempenho delas em dados do mundo real.
Was this page helpful?