Árvores de Decisão

Árvores de decisão (decision trees) são uma família de modelos de aprendizado supervisionado (supervised learning) usados tanto para classificação (classification) quanto para regressão (regression). Elas aprendem um conjunto de regras “se–então” legíveis por humanos ao dividir recursivamente (recursively splitting) o espaço de atributos (feature space) em regiões que são cada vez mais puras (para classificação) ou que têm baixa variância (para regressão). Árvores são populares porque são interpretáveis, funcionam bem em dados tabulares (tabular data), exigem pouco pré-processamento e modelam naturalmente interações não lineares (nonlinear interactions).

Ao mesmo tempo, uma única árvore de decisão costuma ter alta variância (high variance) (sensível a ruído nos dados), então métodos modernos de alto desempenho em dados tabulares frequentemente usam árvores como aprendizes base (base learners) em ensembles como Florestas Aleatórias (Random Forests) e Boosting por Gradiente (Gradient Boosting).

O que uma árvore de decisão aprende

Uma árvore de decisão particiona o espaço de entrada (X) em regiões disjuntas ao aplicar uma sequência de testes:

  • Nós internos (internal nodes): uma regra de divisão como (x_j \le t) (para o atributo numérico (j) com limiar (t)), ou (x_j \in S) (para conjuntos categóricos (S)).
  • Folhas (leaves): uma predição.
    • Classificação: tipicamente a classe majoritária (majority class) (ou probabilidades de classe baseadas nas frequências de classe).
    • Regressão: tipicamente a média (ou às vezes a mediana) dos valores-alvo na folha.

Conceitualmente, a árvore aproxima uma função (f(x)) com um modelo constante por partes (piecewise-constant).

Um pequeno exemplo intuitivo

Suponha que você queira prever se um cliente vai cancelar (Sim/Não) usando atributos como tenure_months e has_contract.

Uma árvore simples poderia aprender regras como:

  • Se has_contract = No:
    • Se tenure_months <= 3 → prever Churn
    • Caso contrário → prever No Churn
  • Caso contrário (has_contract = Yes) → prever No Churn

Isso é fácil de inspecionar, mas se generaliza ou não depende fortemente de como a árvore foi restringida (profundidade, mínimo de amostras por folha etc.).

Divisão recursiva (como as árvores são construídas)

A maioria dos algoritmos de árvore de decisão usados na prática (notadamente Árvores de Classificação e Regressão (CART), usado pelo scikit-learn) constrói árvores com um procedimento recursivo guloso chamado divisão binária recursiva (recursive binary splitting):

  1. Comece com todos os dados de treinamento na raiz.
  2. Procure por divisões candidatas e escolha a divisão que melhor melhora um critério (reduz impureza / perda).
  3. Particione os dados em nós filhos com base nessa divisão.
  4. Faça recursão em cada nó filho até que uma condição de parada seja atingida.
  5. Atribua a cada nó terminal (folha) uma predição.

Isso é guloso: o algoritmo escolhe a melhor divisão agora, e não a árvore globalmente ótima. Encontrar a árvore de decisão ótima é computacionalmente difícil (NP-hard em muitas formulações), então o aprendizado guloso é a abordagem padrão.

Divisões candidatas

Para um atributo numérico (x_j), os limiares (thresholds) candidatos frequentemente são escolhidos a partir de valores únicos ordenados (ou pontos médios entre valores consecutivos). Para atributos categóricos, divisões candidatas podem ser:

  • “Uma categoria vs o resto” (binária)
  • “Subconjunto de categorias vs complemento” (mais caro; muitos subconjuntos possíveis)

Como divisões categóricas são tratadas varia por biblioteca e algoritmo (mais sobre isso adiante).

Critérios de divisão (classificação e regressão)

A “melhor” divisão é aquela que mais reduz uma medida de impureza ou perda do nó pai para os nós filhos.

Classificação: impureza de Gini (Gini impurity)

Impureza de Gini (Gini impurity) para um nó com proporções de classe (p_k) é:

[ G = 1 - \sum_{k=1}^{K} p_k^2 ]

  • (G = 0) significa que o nó é perfeitamente puro (todas as amostras são de uma única classe).
  • Uma divisão é avaliada pela impureza ponderada dos filhos:

[ \Delta G = G_{\text{parent}} - \left(\frac{n_L}{n}G_L + \frac{n_R}{n}G_R\right) ]

O algoritmo seleciona a divisão que maximiza a redução de impureza.

Classificação: entropia (entropy) e ganho de informação (information gain)

Entropia (entropy):

[ H = -\sum_{k=1}^{K} p_k \log_2 p_k ]

Ganho de informação (information gain) é a redução de entropia após dividir, análoga a (\Delta G). A entropia está intimamente ligada à teoria da informação e, na prática, frequentemente produz divisões similares às de Gini.

Alguns algoritmos clássicos (por exemplo, C4.5) também usam razão de ganho (gain ratio) para reduzir o viés em direção a atributos com alta cardinalidade.

Regressão: erro quadrático médio (mean squared error, MSE) (redução de variância)

Para regressão, um objetivo comum é minimizar o erro quadrático médio (mean squared error, MSE). Se uma folha prevê o valor médio do alvo, a melhor divisão é aquela que minimiza a variância dentro do nó (equivalentemente, reduz o erro quadrático):

[ \text{MSE(node)} = \frac{1}{n}\sum_{i=1}^{n}(y_i - \bar{y})^2 ]

Escolha a divisão que maximiza:

[ \Delta \text{MSE} = \text{MSE(parent)} - \left(\frac{n_L}{n}\text{MSE(L)} + \frac{n_R}{n}\text{MSE(R)}\right) ]

Muitas implementações também suportam alternativas como MAE (baseado na mediana) em alguns cenários, mas redução de MSE/variância é o critério clássico de regressão do CART.

Hiperparâmetros (hyperparameters) chave e o trade-off viés–variância (bias–variance tradeoff)

Árvores de decisão podem representar fronteiras de decisão muito complexas, o que as torna propensas a sobreajuste (overfitting). Os principais hiperparâmetros controlam o quanto a árvore pode crescer, equilibrando viés (bias) e variância (variance) (veja Trade-off Viés–Variância e Sobreajuste).

Controles de profundidade e tamanho

Hiperparâmetros comuns (os nomes podem variar por biblioteca):

  • max_depth: profundidade máxima da árvore.
    • Profundidade pequena → maior viés, menor variância.
    • Profundidade grande → menor viés, maior variância (risco de memorização).
  • min_samples_split: número mínimo de amostras necessário para dividir um nó interno.
    • Valores maiores evitam divisões em conjuntos minúsculos de amostras.
  • min_samples_leaf: número mínimo de amostras em uma folha.
    • Muito efetivo para suavizar o modelo; força folhas a representar “dados suficientes”.
  • max_leaf_nodes: limita o número de folhas (outra forma de limitar a complexidade).
  • min_impurity_decrease: exige uma redução mínima de impureza para aceitar uma divisão.

Um bom modelo mental: folhas representam “regras”. Restringir folhas evita regras extremamente específicas que só se ajustam ao ruído.

Subamostragem de atributos (feature subsampling) (às vezes)

  • max_features: número (ou fração) de atributos considerados em cada divisão.
    • Em uma única árvore, restringir atributos pode reduzir variância, mas pode aumentar viés.
    • Em ensembles, é um ingrediente central (especialmente em florestas aleatórias).

Orientação prática

  • Se sua árvore está sobreajustando: reduza max_depth, aumente min_samples_leaf ou faça poda.
  • Se sua árvore está subajustando: permita árvores mais profundas, reduza min_samples_leaf ou faça engenharia de atributos (veja Engenharia de Atributos).

Sempre ajuste com validação adequada, idealmente Validação Cruzada, especialmente em conjuntos de dados menores.

Tratando atributos categóricos

Árvores de decisão são conceitualmente adequadas para variáveis categóricas, mas detalhes de implementação importam.

Codificação one-hot (one-hot encoding) (mais comum e amplamente suportada)

Se sua biblioteca não oferece suporte nativo a divisões categóricas, você pode aplicar codificação one-hot às categorias. Isso funciona, mas tem trade-offs:

  • Prós: simples, compatível com a maior parte do ferramental.
  • Contras:
    • Categorias de alta cardinalidade criam muitos atributos esparsos.
    • As divisões podem ficar menos “naturais” (uma única divisão categórica vira múltiplas divisões binárias).

Codificação ordinal (ordinal encoding) (use com cuidado)

Mapear categorias para inteiros (por exemplo, A=0, B=1, C=2) pode introduzir uma ordenação falsa. Uma árvore ainda pode dividir por limiares (por exemplo, (\le 1)), o que pode não corresponder a agrupamentos significativos de categorias.

A codificação ordinal é razoável apenas se a categoria realmente tiver ordem (por exemplo, “baixo/médio/alto”).

Divisões categóricas nativas (native categorical splits) (em algumas bibliotecas)

Algumas bibliotecas modernas de boosting por gradiente (e algumas implementações de árvores) suportam divisões categóricas de forma mais direta, frequentemente usando estratégias como:

  • Ordenar categorias por estatísticas do alvo e então fazer uma divisão por limiar sobre essa ordenação.
  • Buscar subconjuntos com heurísticas.

Se você tem muitos atributos categóricos com alta cardinalidade, considere uma biblioteca de ensemble com bom tratamento de categorias (e tome precauções contra vazamento).

Codificação pelo alvo (target encoding) e risco de vazamento de dados (data leakage)

Codificação pelo alvo (target encoding) substitui categorias por uma estatística do alvo (por exemplo, média do alvo por categoria). Isso pode ajudar, mas também pode causar vazamento de dados (data leakage) se for calculada usando o conjunto de dados completo. Use codificação fora da dobra (out-of-fold) e práticas fortes de validação (veja Validação Cruzada).

Poda (pruning) e regularização (regularization)

Árvores podem ser regularizadas de duas formas gerais:

  1. Pré-poda (pre-pruning) (parada antecipada): interromper o crescimento usando restrições como max_depth e min_samples_leaf.
  2. Pós-poda (post-pruning): crescer uma árvore grande e depois podá-la.

Poda por custo-complexidade (cost-complexity pruning) (CART)

Uma abordagem padrão é a poda por custo-complexidade (cost-complexity pruning), que otimiza:

[ R_\alpha(T) = R(T) + \alpha \cdot |T| ]

  • (R(T)): perda de treinamento (impureza / erro)
  • (|T|): número de folhas (complexidade do modelo)
  • (\alpha): força da regularização

À medida que (\alpha) aumenta, o algoritmo prefere árvores menores. No scikit-learn, isso é controlado por ccp_alpha.

Poda por erro reduzido (reduced-error pruning) (conceitual)

Outra ideia clássica é podar nós se removê-los não piorar o desempenho no conjunto de validação. Isso é intuitivo, mas depende de ter um conjunto de poda separado (ou de usar validação cruzada).

Por que a poda importa

Mesmo que uma árvore profunda atinja erro de treinamento quase zero, ela frequentemente captura ruído. A poda converte uma árvore que “memoriza” em um modelo mais simples que generaliza melhor, melhorando estabilidade e interpretabilidade.

Exemplos práticos (scikit-learn)

A seguir estão exemplos mínimos para classificação e regressão. Eles ilustram botões típicos de regularização; em projetos reais, você ajustaria hiperparâmetros com validação cruzada.

Exemplo de classificação

from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score

X, y = load_breast_cancer(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y
)

clf = DecisionTreeClassifier(
    criterion="gini",
    max_depth=4,
    min_samples_leaf=10,
    random_state=42
)
clf.fit(X_train, y_train)

pred = clf.predict(X_test)
print("accuracy:", accuracy_score(y_test, pred))

Notas:

  • Árvores não exigem escalonamento de atributos (feature scaling).
  • min_samples_leaf costuma ser um regularizador padrão forte para conjuntos de dados tabulares.

Exemplo de regressão

from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeRegressor
from sklearn.metrics import mean_squared_error

X, y = fetch_california_housing(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)

reg = DecisionTreeRegressor(
    criterion="squared_error",
    max_depth=6,
    min_samples_leaf=20,
    random_state=42
)
reg.fit(X_train, y_train)

pred = reg.predict(X_test)
print("rmse:", mean_squared_error(y_test, pred, squared=False))

Ressalva para regressão: regressão com árvores é constante por partes e, em geral, não consegue extrapolar além do intervalo de alvos observados nas folhas de treinamento.

Interpretação e depuração

Uma vantagem-chave das árvores de decisão é a interpretabilidade, mas a utilidade depende do tamanho da árvore:

  • Árvores pequenas (profundidade rasa) podem ser lidas como regras e usadas para insights do domínio.
  • Árvores grandes ficam difíceis de interpretar diretamente, embora ainda possam ser analisadas com importância de atributos e explicações locais.

Ferramentas comuns de interpretação:

  • Plotar a árvore (bom para árvores pequenas).
  • Extrair regras de decisão a partir dos caminhos até as folhas.
  • Importância de atributos baseada na redução total de impureza (rápida, mas pode ser enviesada em direção a atributos com alta cardinalidade).
  • Importância por permutação (permutation importance) (mais confiável, agnóstica ao modelo; veja Avaliação de Modelos se sua wiki incluir, ou Métricas de Avaliação para escolha de métricas).

Pontos fortes, limitações e armadilhas comuns

Pontos fortes

  • Interpretável para árvores pequenas.
  • Lida automaticamente com não linearidades e interações entre atributos.
  • Funciona com tipos mistos de atributos (com codificação apropriada).
  • Pré-processamento mínimo; robusta a transformações monotônicas.
  • Inferência rápida (apenas uma sequência de comparações).

Limitações

  • Alta variância: pequenas mudanças nos dados podem gerar árvores diferentes.
  • Divisão gulosa pode perder estruturas globalmente melhores.
  • Importância baseada em impureza pode ser enganosa com atributos correlacionados ou de alta cardinalidade.
  • Para regressão, as previsões são constantes por partes, o que pode parecer “em blocos”.

Armadilhas

  • Árvores profundas podem sobreajustar muito, especialmente com muitos atributos ou rótulos ruidosos.
  • Se variáveis categóricas forem mal codificadas (por exemplo, codificação ordinal ingênua), as divisões podem ser sem sentido.
  • Desbalanceamento de classes (class imbalance) pode causar viés para a classe majoritária; considere class_weight="balanced" e métricas apropriadas (veja Métricas de Avaliação).

Por que árvores são aprendizes base em florestas aleatórias e boosting por gradiente

Árvores individuais são úteis, mas seu verdadeiro poder no ML moderno frequentemente vem de ensembles (veja Métodos de Ensemble).

Florestas aleatórias: reduzir variância com bagging + aleatoriedade

Um modelo de Florestas Aleatórias treina muitas árvores em amostras bootstrap (bootstrap samples) (bagging) e faz a média de suas predições.

Ideias-chave:

  • Cada árvore sobreajusta à sua própria maneira, mas a média reduz a variância.
  • Subamostragem de atributos (max_features) decorrela as árvores, melhorando a redução de variância.

Resultado: forte desempenho padrão em muitas tarefas tabulares com pouco ajuste.

Boosting por gradiente: reduzir viés ajustando resíduos sequencialmente

Boosting por Gradiente constrói árvores sequencialmente, onde cada nova árvore (geralmente rasa) corrige erros cometidos pelo ensemble existente.

Ideias-chave:

  • As árvores frequentemente são pequenas (por exemplo, profundidade 2–8), atuando como “aprendizes fracos (weak learners)”.
  • O ensemble otimiza uma perda diferenciável usando informação de gradiente.
  • Forte desempenho, mas mais sensível a hiperparâmetros (taxa de aprendizado (learning rate), número de árvores, profundidade, regularização).

Resultado: frequentemente desempenho de ponta em dados estruturados/tabulares quando ajustado com cuidado.

Quando usar uma árvore de decisão (na prática)

Uma única árvore de decisão é uma boa escolha quando:

  • Você precisa de um modelo de baseline transparente ou regras legíveis por humanos.
  • Você quer um modelo que lide com interações não lineares entre atributos sem pré-processamento pesado.
  • Você precisa de inferência rápida e implantação simples.

Se você se importa principalmente com desempenho preditivo em dados tabulares, considere ensembles de árvores:

Resumo

Árvores de decisão aprendem uma hierarquia de divisões que particionam recursivamente os dados em regiões cada vez mais homogêneas. Elas se baseiam em critérios de divisão como Gini ou entropia (classificação) e redução de MSE (regressão). Sua capacidade é controlada por hiperparâmetros como profundidade máxima e mínimo de amostras por folha, que gerenciam o trade-off viés–variância e combatem o sobreajuste. O uso prático exige tratamento cuidadoso de atributos categóricos e frequentemente se beneficia de poda ou outra regularização.

Por fim, como árvores individuais são flexíveis porém instáveis, elas servem como blocos de construção ideais para ensembles poderosos — especialmente florestas aleatórias (redução de variância) e boosting por gradiente (redução de viés) — que estão entre os métodos mais eficazes para aprendizado de máquina moderno em dados tabulares.