Machine learning do zero · Aula 3.2 · 15 de 33 aulas prontas · 10 min
Validação e validação cruzada
Em resumo
Escolher configurações com o conjunto de teste estraga o teste. A validação cruzada avalia o modelo em cada parte dos dados e mostra se a nota é estável.
Aquecimento Duas perguntas de aulas anteriores. Um minuto, e a aula nunca fica bloqueada.
Na lição anterior, escolhemos o melhor grau do polinômio dando uma espiada no conjunto de teste. Pareceu inofensivo, mas pense no que isso significa: testamos vários modelos e ficamos com o que o conjunto de teste mais gostou. O teste deixou de ser uma prova inédita — virou um juiz que consultamos durante a preparação. Como escolher configurações sem estragar a nossa única medida honesta?
Escolha uma resposta e o seu grau de certeza.
Responda para liberar o experimento.
Experimento interativo
Este experimento interativo funciona em um navegador com JavaScript ativado. O texto ao redor descreve o que ele mostra.
Ver a resposta
Resposta certa: Aumenta, porque cada fold de teste tem menos vinhos
Para k = 2, o pior fold tem 94,4% e o melhor 97,8% (diferença de 3,4 pontos percentuais). Para k = 10, a amplitude vai de 83,3% a 100,0% (16,7 pontos), porque um fold de teste tem 17 ou 18 vinhos e um único erro pesa mais de 5 pontos. A média quase não muda (96,1% e 96,7%).
Experimente
- Coloque o controle de k no menor valor e leia a acurácia de cada fold. Quão diferentes os folds são entre si?
- Aumente o controle de k passo a passo. Observe a média e a dispersão, e repare que cada fold agora tem menos vinhos.
- Com um k grande, procure folds com acurácia muito baixa ou muito alta. Com só um punhado de vinhos num fold, um único erro mexe muito na nota desse fold.
- Compare a dispersão com as diferenças entre configurações que você talvez queira ajustar. Um pequeno ganho na média é maior que a dispersão?
A ideia em poucas palavras
Vamos ver agora, em palavras simples, o que você acabou de manipular.
A primeira solução é uma terceira pilha de dados. Divida as linhas em conjuntos de treino, validação e teste. Treine os modelos candidatos no conjunto de treino, compare-os no de validação, escolha o vencedor e só então abra o conjunto de teste, uma vez, para relatar a nota final. O conjunto de validação absorve todas as espiadas para que o de teste não precise.
O problema: cada divisão custa dados. Com um conjunto de dados pequeno, um conjunto de validação grande o bastante para ser confiável deixa pouco demais para treinar, e a lição 2 mostrou que um conjunto pequeno dá uma nota que pula. A validação cruzada resolve isso reaproveitando os dados com esperteza.
Imagine avaliar uma cozinheira nova. Provar um prato só diz pouco: talvez tenha sido o melhor dela, talvez o pior. Então você pede vários pratos em dias diferentes e olha a média e o quanto eles variam. A validação cruzada faz o mesmo com um modelo.
Por que funciona
Na validação cruzada k-fold, os dados (sem contar o conjunto de teste final) são cortados em k partes iguais chamadas folds. O modelo é treinado k vezes. Em cada uma, um fold fica de fora para validação e os k − 1 restantes são usados no treino. No fim, cada linha foi usada exatamente uma vez para validação e k − 1 vezes para treino. Cada treinamento vê esta fração dos dados:
fração de treino = (k − 1) / k
O resultado não é uma nota, e sim k notas, uma por fold. A média delas é uma estimativa mais confiável que qualquer divisão única, porque não depende de uma divisão de linhas com sorte ou azar. A dispersão é igualmente valiosa: mostra o quanto a nota depende de quais linhas o modelo recebeu. Dois modelos cujas médias diferem menos que essa dispersão não podem ser realmente distinguidos.
A escolha de k é um dilema. Um k pequeno (digamos 2) treina cada modelo com só metade dos dados, então cada modelo é mais fraco que o que você vai construir no fim, e a estimativa é pessimista. Um k grande treina com quase todos os dados, mas cada fold é pequeno, então as notas de cada fold ficam mais ruidosas, e o cálculo leva k vezes o tempo de um único ajuste. Valores 5 ou 10 são um meio-termo comum.
O exemplo desta lição é o conjunto de dados Wine: 178 vinhos da mesma região da Itália, feitos com três cultivares de uva diferentes, cada um descrito por 13 resultados de análise química. A tarefa é distinguir os três cultivares.
Erro comum
A validação cruzada só protege você se tudo o que é aprendido dos dados acontecer dentro de cada fold. Se você padroniza os atributos ou seleciona os “melhores” atributos usando todas as linhas primeiro e só depois faz a validação cruzada, informação dos folds de validação já vazou para o treino, e as notas ficam otimistas demais. O segundo erro é ajustar dezenas de configurações com validação cruzada e relatar a melhor nota da validação cruzada como resultado final. Depois de tantas comparações, o vencedor tem uma parte de sorte; por isso um conjunto de teste separado, usado uma única vez, continua importando.
Com avaliação honesta na caixa de ferramentas, podemos explorar modelos mais poderosos sem nos enganar. Na próxima lição conhecemos o mais legível deles: a árvore de decisão.
Quiz
Três perguntas para você se testar. Escolha uma resposta ou abra a resposta embaixo de cada pergunta. No teclado: as setas escolhem, Enter ou Espaço confirma.
Pontos-chave
- Escolher configurações com o conjunto de teste o estraga; use um conjunto de validação ou validação cruzada.
- Na validação cruzada k-fold, cada linha é usada para validação exatamente uma vez.
- A média das notas dos folds estima o desempenho; a dispersão mostra o quanto ele depende da divisão.
- Toda preparação que aprende com os dados deve acontecer dentro de cada fold para evitar vazamento.
Mais a fundo: fórmula, código, prática
A matemática
Na validação cruzada de k dobras, as n linhas são cortadas em k dobras. Cada ajuste treina com n · (k − 1) / k linhas e testa nas n / k restantes. A pontuação é a média das dobras: CV = (1/k) · Σ score_f. A dispersão é o desvio padrão delas, s = √( Σ (score_f − CV)² / (k − 1) ).
Uma dobra de m linhas de teste dá uma acurácia que se move em saltos de 1 / m. Se a acurácia verdadeira é p, seu erro padrão é de cerca de √( p · (1 − p) / m ). Isso cresce quando m diminui, e por isso cada dobra isolada fica mais ruidosa quando k sobe, embora a média fique menos pessimista. Com 178 vinhos e k = 10, um erro em uma dobra de 17 ou 18 vinhos custa de 5,6 a 5,9 pontos.
As pontuações das dobras não são totalmente independentes: os conjuntos de treino se sobrepõem. Por isso s / √k subestima a incerteza real da média.
No código (Python)
Os 178 vinhos com 5 vizinhos mais próximos e variáveis padronizadas. Para ver o padrão por trás do embaralhamento único do lab, a diferença entre a pior e a melhor dobra é calculada em média sobre 20 embaralhamentos. As dobras são estratificadas e embaralhadas com uma semente, então os números não são os 3,4 e 16,7 pontos do lab:
import numpy as np
from sklearn.datasets import load_wine
from sklearn.model_selection import StratifiedKFold, cross_val_score
from sklearn.neighbors import KNeighborsClassifier
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
X, y = load_wine(return_X_y=True)
model = make_pipeline(StandardScaler(), KNeighborsClassifier(n_neighbors=5)) # o escalonador é reajustado dentro de cada dobra
for k in (2, 10):
runs = [cross_val_score(model, X, y, cv=StratifiedKFold(k, shuffle=True, random_state=s)) for s in range(20)]
gap = np.mean([a.max() - a.min() for a in runs])
print(f"k={k:2d}: mean accuracy {np.mean(runs):.3f}, average worst-to-best gap {gap:.3f}")
Saída
k= 2: mean accuracy 0.956, average worst-to-best gap 0.020
k=10: mean accuracy 0.966, average worst-to-best gap 0.113
A média quase não se move (0,956 e 0,966), mas a diferença média entre a pior e a melhor dobra cresce de 0,020 para 0,113 quando k vai de 2 para 10.
Baixar o notebook (.ipynb). Abra no Jupyter ou envie para o Google Colab; ele baixa sozinho os dados que faltarem.
Para quem pratica
- Ajuste cada etapa aprendida (escalonamento, imputação, seleção de variáveis) dentro de cada dobra. No scikit-learn, coloque-as em um
Pipelinee passe-o paracross_val_score. - Na classificação use dobras estratificadas, para que cada dobra tenha todas as classes.
cross_val_scorefaz isso por padrão nos classificadores quandocvé um número, mas esse padrão não embaralha. - Embaralhe antes de cortar as dobras se as linhas estiverem ordenadas, por exemplo por classe ou por data.
load_wineestá ordenado por classe, então passeStratifiedKFold(k, shuffle=True, random_state=0), como acima. Em séries temporais não embaralhe: use uma divisão sequencial para a frente. - Se as linhas pertencem a grupos (várias linhas por paciente), mantenha cada grupo em uma só dobra com
GroupKFold. Caso contrário, o mesmo paciente fica no treino e na validação. - Informe a média e a dispersão. Se dois modelos diferem menos que a dispersão, não diga que um deles é melhor.
Isto foi útil?
Obrigado! Isso nos ajuda a melhorar.
Vá mais fundo no Atlas
Verbetes da enciclopédia sobre as ideias desta aula: