Bagging e boosting combinam os modelos de um comitê sempre da mesma forma: a média, o voto, os pesos aprendidos no treino. A regra de combinação é fixa — vale igualmente para todas as amostras que chegarem.

A seleção dinâmica parte de uma observação incômoda sobre essa escolha: nenhum modelo é bom em todo lugar. Um classificador pode ser excelente numa região do espaço de atributos e péssimo em outra, e outro pode ter exatamente o perfil complementar. A média dos dois é medíocre nas duas regiões, quando poderia ser ótima em ambas — bastaria usar o modelo certo em cada uma.

A ideia central é simples de enunciar: escolher qual modelo do comitê usar no momento da predição, em função de onde a amostra está.

Compreender a diferença entre combinação estática e dinâmica, implementar seleção dinâmica de classificador (DCS) e de conjunto (DES) via região de competência, conhecer as principais famílias de critério, e avaliar quando a técnica compensa seu custo.

O conceito de região de competência

Para decidir qual modelo é bom aqui, é preciso definir o que é "aqui". A região de competência é a vizinhança da amostra de consulta num conjunto de referência — chamado conjunto de validação dinâmica (DSEL, na literatura) — do qual se conhecem os rótulos.

O procedimento na predição:

  1. Recebe a amostra x.
  2. Encontra os k vizinhos mais próximos de x no DSEL.
  3. Mede o desempenho de cada modelo do comitê nesses k vizinhos.
  4. Seleciona um modelo (DCS) ou um subconjunto (DES) com base nessa medida.
  5. Aplica o selecionado a x.

O ponto crítico, e a causa de metade dos problemas na prática: o DSEL precisa ser um conjunto separado, que não foi usado para treinar os modelos-base. Usar o próprio treino faz cada modelo parecer competente em toda parte — afinal, ele já viu aqueles pontos —, e a seleção perde o sentido.

from sklearn.model_selection import train_test_split

X_treino, X_temp, y_treino, y_temp = train_test_split(X, y, test_size=0.4, stratify=y, random_state=42)
X_dsel, X_teste, y_dsel, y_teste   = train_test_split(X_temp, y_temp, test_size=0.5, stratify=y_temp, random_state=42)

A partição típica fica em 60% treino, 20% DSEL, 20% teste. Com pouco dado, esse é justamente o problema da técnica: tirar 20% para o DSEL pode custar mais em qualidade dos modelos-base do que a seleção dinâmica devolve.

DCS: escolher um

A Acurácia Local Global (OLA) é o critério mais direto: vence o modelo com maior acurácia na região de competência.

import numpy as np
from sklearn.neighbors import NearestNeighbors

class SelecaoDinamicaOLA:
    """DCS por acurácia local — a versão didática, para ver o mecanismo."""

    def __init__(self, modelos, k=7):
        self.modelos = modelos
        self.k = k

    def fit(self, X_dsel, y_dsel):
        self.X_dsel, self.y_dsel = X_dsel, y_dsel
        self.vizinhos = NearestNeighbors(n_neighbors=self.k).fit(X_dsel)
        # pré-computa os acertos de cada modelo em cada ponto do DSEL:
        # sem isso, cada predição reavaliaria todos os modelos
        self.acertos = np.array([m.predict(X_dsel) == y_dsel for m in self.modelos])
        return self

    def predict(self, X):
        _, idx = self.vizinhos.kneighbors(X)
        saida = np.empty(len(X), dtype=self.y_dsel.dtype)

        for i, vizinhanca in enumerate(idx):
            competencia = self.acertos[:, vizinhanca].mean(axis=1)
            escolhido = self.modelos[int(np.argmax(competencia))]
            saida[i] = escolhido.predict(X[i].reshape(1, -1))[0]
        return saida

A pré-computação dos acertos no fit é o que torna isso viável: sem ela, cada predição exigiria rodar todos os modelos sobre os k vizinhos.

A LCA (Acurácia Local de Classe) refina o critério: em vez da acurácia geral na vizinhança, mede a acurácia restrita à classe que o modelo prevê para a consulta. Se o modelo diz "fraude", o que importa é quantas vezes ele acertou ao dizer "fraude" naquela região — o que é mais informativo quando as classes são desbalanceadas.

DES: escolher vários

Confiar num único modelo por amostra é frágil: se a estimativa de competência estiver errada — e com k pequeno ela é ruidosa —, não há nada para compensar. As abordagens DES selecionam um subconjunto competente e combinam por voto.

KNORA-Eliminate é o critério mais conhecido: seleciona os modelos que acertam todos os k vizinhos. Se nenhum consegue, reduz k progressivamente até que algum sobreviva.

KNORA-Union é mais permissivo: inclui todo modelo que acerte pelo menos um vizinho, ponderando o voto pelo número de acertos.

from deslib.des import KNORAE, KNORAU, METADES
from deslib.dcs import OLA, LCA
from sklearn.ensemble import BaggingClassifier
from sklearn.tree import DecisionTreeClassifier

# o comitê precisa ser DIVERSO: modelos idênticos tornam a seleção inútil
comite = BaggingClassifier(
    DecisionTreeClassifier(max_depth=6),
    n_estimators=100, random_state=42,
).fit(X_treino, y_treino)

for nome, tecnica in [("OLA", OLA), ("LCA", LCA),
                      ("KNORA-E", KNORAE), ("KNORA-U", KNORAU), ("META-DES", METADES)]:
    modelo = tecnica(comite.estimators_, k=7, random_state=42).fit(X_dsel, y_dsel)
    print(f"{nome:10s} {modelo.score(X_teste, y_teste):.4f}")

print(f"{'Bagging':10s} {comite.score(X_teste, y_teste):.4f}")   # a linha de base honesta

META-DES troca a heurística por aprendizado: treina um meta-classificador que recebe características da região de competência (acurácia local, grau de consenso, confiança da saída) e prevê se um dado modelo é competente para aquela consulta. Costuma ser a técnica mais forte da família, ao custo de mais uma camada a treinar e ajustar.

Diversidade é pré-requisito, não detalhe

Toda a família depende de uma condição: os modelos precisam errar em lugares diferentes. Se o comitê é homogêneo — cem árvores quase idênticas —, a competência local é praticamente a mesma para todos, a seleção vira sorteio e o método não tem o que explorar.

Fontes de diversidade, em ordem crescente de efeito:

  • Reamostragem (bagging): a mais comum, diversidade moderada;
  • Subespaços aleatórios: cada modelo vê um subconjunto de atributos — costuma gerar mais diversidade que o bagging para este fim;
  • Heterogeneidade de algoritmo: árvore, kNN, Naive Bayes, SVM e regressão logística juntos. Eles têm vieses indutivos genuinamente diferentes, e é o cenário em que a seleção dinâmica mais brilha.
from sklearn.linear_model import LogisticRegression
from sklearn.naive_bayes import GaussianNB
from sklearn.neighbors import KNeighborsClassifier
from sklearn.svm import SVC
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler

comite = [
    DecisionTreeClassifier(max_depth=8, random_state=42),
    make_pipeline(StandardScaler(), LogisticRegression(max_iter=2000)),
    GaussianNB(),
    make_pipeline(StandardScaler(), KNeighborsClassifier(n_neighbors=5)),
    make_pipeline(StandardScaler(), SVC(probability=True, random_state=42)),
]
for m in comite:
    m.fit(X_treino, y_treino)

Vale medir a diversidade antes de investir na técnica — o Q de Yule e a taxa de desacordo entre pares são as medidas usuais. Comitê com desacordo baixo é sinal de que seleção dinâmica não vai ajudar.

O parâmetro k e a região de indecisão

O k da região de competência governa o compromisso de sempre:

  • k pequeno (3 a 5): região muito local, estimativa de competência ruidosa. Um vizinho atípico muda a escolha.
  • k grande (15 a 30): estimativa estável, mas a região deixa de ser local — no limite, a competência vira o desempenho global e a seleção perde o propósito.

Valores entre 5 e 10 funcionam na maioria dos casos, e k merece entrar na validação cruzada como qualquer hiperparâmetro.

Uma otimização que a literatura recomenda e que muda o custo: aplicar seleção dinâmica apenas na região de indecisão. Se todos os modelos concordam sobre uma amostra — o que acontece na maioria delas —, não há o que selecionar. Reserve o procedimento caro para os casos em que o comitê discorda:

def prever_com_selecao_seletiva(X, comite, seletor):
    votos = np.array([m.predict(X) for m in comite])
    consenso = (votos == votos[0]).all(axis=0)

    saida = votos[0].copy()
    if (~consenso).any():
        saida[~consenso] = seletor.predict(X[~consenso])
    return saida

Em conjuntos com bom consenso, isso corta a maior parte do custo sem afetar o resultado.

O custo, dito com franqueza

Seleção dinâmica não é barata:

  • Na predição, cada amostra exige uma busca de vizinhos no DSEL, o que é ordens de grandeza mais lento que uma votação simples. Para latência baixa em produção, é frequentemente inviável.
  • Em memória, o DSEL inteiro precisa ficar disponível junto com o modelo. Um comitê que caberia em poucos megabytes passa a carregar o conjunto de validação inteiro.
  • Em dados, os 20% do DSEL saem do treino dos modelos-base.

Some-se a isso o resultado empírico consistente: em conjuntos grandes e bem comportados, um gradient boosting bem ajustado costuma igualar ou superar qualquer técnica de seleção dinâmica, com uma fração da complexidade de implantação.

Onde ela realmente compensa: conjuntos pequenos a médios, com estrutura local heterogênea (regiões do espaço com comportamentos diferentes), e com um comitê genuinamente diverso. Fora disso, é complexidade sem retorno — e a decisão correta é medir contra a linha de base antes de adotar.

A combinação estática aplica a mesma regra a todas as amostras; a seleção dinâmica escolhe o modelo no momento da predição, conforme a região em que a amostra caiu. Essa região de competência são os k vizinhos da consulta num conjunto de validação dinâmica, o DSEL, que precisa ser separado do treino — usar o treino como DSEL invalida a técnica inteira. A família se divide em duas: o DCS escolhe um modelo, com o OLA olhando acurácia local e o LCA restringindo à classe prevista; o DES escolhe um subconjunto, com o KNORA-E exigindo acerto em todos os vizinhos e o KNORA-U aceitando quem acerta ao menos um. O META-DES vai além e aprende a prever competência com um meta-classificador, sendo em geral o mais forte do grupo. Nada disso funciona sem diversidade: num comitê homogêneo a seleção vira sorteio, e comitês heterogêneos, de algoritmos diferentes, são o melhor cenário. O k costuma ficar entre 5 e 10 — pequeno demais é ruidoso, grande demais dissolve a localidade. Aplicar a seleção só na região de indecisão, onde o comitê discorda, corta a maior parte do custo. E há custo: em predição, em memória e em dados. Em conjuntos grandes, um boosting bem ajustado costuma empatar com muito menos complexidade — meça contra essa linha de base antes de adotar.

Exercícios

  1. Por que o DSEL não pode ser o mesmo conjunto usado para treinar os modelos-base?

    ✓ Resposta:

    Porque os modelos já viram aqueles pontos e tendem a acertá-los, muitas vezes por memorização — especialmente modelos flexíveis como árvores profundas ou kNN com k=1, que podem acertar 100% do treino. A estimativa de competência local ficaria uniformemente alta para todos os modelos e em todas as regiões, sem relação com a capacidade real de generalizar naquela vizinhança. Na prática a seleção passaria a escolher o modelo que mais sobreajustou, não o mais competente, e o desempenho em dados novos seria pior que a combinação estática. O DSEL precisa ser um conjunto separado, em que os acertos observados indiquem generalização.

  2. Qual a diferença entre KNORA-Eliminate e KNORA-Union, e quando cada um é preferível?

    ✓ Resposta:

    O KNORA-E é restritivo: seleciona apenas os modelos que acertam todos os k vizinhos, reduzindo k progressivamente se nenhum qualificar. Tende a escolher poucos modelos, muito confiáveis naquela região — bom quando a estrutura local é nítida e há modelos realmente especializados. O KNORA-U é permissivo: inclui todo modelo que acerte pelo menos um vizinho, ponderando o voto pelo número de acertos. Produz comitês maiores e, por isso, mais estáveis. Prefira o E quando o comitê for grande e diverso, com especialistas claros por região; prefira o U quando o comitê for pequeno, quando k for baixo (estimativa ruidosa) ou quando as regiões forem mal definidas — casos em que a robustez do voto compensa a menor especialização.

  3. Seu comitê são 200 árvores de um Random Forest e a seleção dinâmica não melhorou nada. Explique.

    ✓ Resposta:

    Falta diversidade. As árvores de um Random Forest são geradas pelo mesmo algoritmo, com o mesmo viés indutivo, sobre reamostragens do mesmo conjunto — elas tendem a errar nos mesmos lugares, tipicamente perto de fronteiras de decisão complexas ou em regiões com poucos dados. Como a competência local fica praticamente idêntica para todas, a seleção não tem o que distinguir: escolher "a melhor" vira um sorteio entre modelos equivalentes, e o resultado converge para o da votação padrão. A correção é aumentar a diversidade: usar subespaços aleatórios (cada modelo com um subconjunto de atributos) ou, melhor, montar um comitê heterogêneo — árvore, regressão logística, Naive Bayes, kNN, SVM —, cujos vieses genuinamente diferentes produzem perfis de erro complementares. Vale medir o desacordo entre pares antes de investir.

  4. Como o k afeta a seleção, e qual o sintoma de cada extremo?

    ✓ Resposta:

    O k define o tamanho da região de competência e governa o compromisso entre viés e variância na estimativa de competência. Com k pequeno (3 a 5), a região é bem local mas a estimativa é ruidosa: um único vizinho atípico ou mal rotulado muda qual modelo é escolhido, e o sintoma é instabilidade — o resultado varia bastante entre execuções, e amostras muito próximas recebem modelos diferentes sem razão aparente. Com k grande (20 ou mais), a estimativa fica estável mas a região deixa de ser local: no limite, a competência medida tende ao desempenho global de cada modelo, o mesmo modelo é escolhido quase sempre e o método degenera na seleção estática do melhor. O sintoma aqui é o desempenho convergir para o da melhor base isolada. Trate k como hiperparâmetro, com validação cruzada; 5 a 10 é o ponto de partida.

  5. Em que cenários a seleção dinâmica compensa seu custo, e em quais não?

    ✓ Resposta:

    Compensa em conjuntos pequenos a médios, com estrutura local heterogênea — regiões do espaço em que o comportamento das classes muda de natureza — e com um comitê genuinamente diverso, idealmente heterogêneo em algoritmo. Nesses casos a especialização por região captura algo que nenhuma regra fixa de combinação alcança. Não compensa quando: (a) há muito dado e o problema é bem comportado, cenário em que um gradient boosting ajustado costuma igualar ou superar com muito menos complexidade; (b) a latência de predição importa, já que cada amostra exige busca de vizinhos no DSEL; (c) os dados são escassos, porque os 20% reservados ao DSEL custam mais em qualidade dos modelos-base do que a seleção devolve; (d) o comitê é homogêneo. A conduta correta é sempre medir contra as linhas de base — votação simples, stacking e boosting — antes de assumir a complexidade adicional em produção.

Fontes e leituras recomendadas