Machine learning do zero · Aula 3.6 · 19 de 33 aulas prontas · 12 min
Parada antecipada
Em resumo
Uma rede treinada por tempo demais decora rótulos errados. Com 20% de rótulos aleatórios, 100 épocas sem parar baixam a acurácia de teste de 86,1% para 76,3%.
Aquecimento Duas perguntas de aulas anteriores. Um minuto, e a aula nunca fica bloqueada.
Uma rede neural aprende em épocas: uma época é uma passada por todos os exemplos de treino. Depois de cada época a perda de treino costuma cair. A pergunta da aula 3.1 volta de outra forma: quantas épocas são demais? A parada antecipada responde com o conjunto de validação: o treino para quando a perda de validação deixa de cair, e o modelo volta à melhor época.
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: Cai de 86,1% para 76,3%, porque a rede decora também os rótulos errados
Os pesos da época 28 têm 86,1% nas 997 imagens de teste; depois de 100 épocas, 76,3%: 9,7 pontos a menos. Enquanto isso a perda de validação sobe de 0,48 para 0,84, e dos 51 rótulos errados a rede acaba repetindo todos.
Experimente
- O lab abre com rótulos limpos. Mude para os rótulos aleatórios e deixe a paciência em 5. Em que época o treino para, quais pesos ficam e essa é a melhor época de todo o percurso?
- Aumente a paciência para 10. O que muda na época mantida e na acurácia de teste?
- Volte aos rótulos limpos. Quanto custa agora treinar até o fim?
Depois de conferir no lab, vamos dar nome ao que você vê.
A ideia em palavras simples
No começo do treino a rede aprende o que muitas imagens têm em comum: a forma de um zero, de um oito, de um sete. As perdas de treino e de validação caem juntas. Depois a rede começa a ajustar exemplos isolados, inclusive os de rótulo errado. A perda de treino continua caindo e a de validação começa a subir. É o sobreajuste da aula 3.1, só que ao longo das épocas em vez do número de parâmetros.
A parada antecipada vigia a perda de validação. Ela guarda a época com a menor perda e conta as épocas sem melhora. Quando elas chegam ao valor da paciência, o treino para e a rede volta aos pesos da melhor época. No lab, com paciência 10, o treino para na época 38 e ficam os pesos da época 28: 86,1% no teste em vez de 76,3% depois de 100 épocas.
Paciência de menos também atrapalha. A curva de validação oscila de uma época para outra. Com paciência 5 o treino para já na época 23 e fica com os pesos da época 18 (85,0% no teste), embora a melhor fosse a época 28.
Jak to policzono
- Dígitos: Optical Recognition of Handwritten Digits, UCI, licença CC BY 4.0. 1797 imagens de 8×8 pixels.
- Divisão estratificada: 299 imagens para aprender, 501 para validação, 997 para o teste. Na versão com ruído cada rótulo de treino é trocado, com probabilidade de 20%, por um dígito aleatório: mudaram 51 rótulos (um dígito aleatório às vezes acerta o verdadeiro).
- Rede 64 → 32 → 10, ReLU, entropia cruzada, SGD com momento 0,9, passo 0,05, lotes de 32 imagens, 100 épocas. Perda de validação calculada depois de cada época.
- A validação e o teste têm os rótulos verdadeiros. O ruído está só nos dados de treino.
Por que funciona
Um exemplo com rótulo errado é difícil para a rede: a imagem parece um três, mas o rótulo diz "sete". Para baixar a perda num exemplo assim, a rede precisa aprender uma regra que só serve para ele. Ela costuma fazer isso tarde, quando já domina os padrões fáceis e comuns. No lab a rede repete 10 dos 51 rótulos errados na época 28, e todos os 51 depois de 100 épocas. Nos próprios dados de treino ela tem então 99,7% de acurácia: concorda até com os rótulos que são falsos.
Regras ajustadas a rótulos errados estragam as respostas em imagens novas. O conjunto de validação tem os rótulos verdadeiros, então a perda dele sobe justamente quando a rede começa a aprender o ruído. Por isso é ela, e não a perda de treino, que diz quando parar.
Não é acaso de uma única execução. No código abaixo o mesmo experimento roda no scikit-learn em outras 10 divisões e sorteios de ruído: a melhor época cai entre 13 e 23, e treinar até 100 épocas baixa a acurácia de teste em todas as 10 execuções, em média de 87,2% para 76,4%. A melhor época do lab (28) fica fora dessa faixa porque também depende dos pesos iniciais e da ordem dos lotes: a mesma rede do lab com outras 9 sementes aleatórias se sai melhor entre as épocas 12 e 25, e treinar até 100 épocas baixa a acurácia de teste todas as vezes.
Erro comum
Verdadeiro ou falso: "treinar por mais tempo sempre dá um modelo melhor". Falso. Treinar por mais tempo sempre se ajusta melhor aos dados de treino, mas não aos novos. Com rótulos aleatórios, 100 épocas dão 99,7% de acurácia no treino e 76,3% no teste, enquanto parar na época 28 dá 86,1% no teste.
Uma observação no sentido contrário: com rótulos limpos, treinar até o fim quase não estraga nada. A menor perda de validação vem na época 30 (93,9% no teste), e depois de 100 épocas a acurácia é 94,5%. A parada antecipada nem sempre salva acurácia, mas sempre economiza tempo e protege de dados piores do que você imagina.
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
- Uma época é uma passada pelos dados de treino; a perda de treino quase sempre cai, a de validação não.
- A parada antecipada interrompe o treino quando a perda de validação não melhora por algumas épocas (a paciência) e volta aos melhores pesos.
- Treinar demais com rótulos errados ensina o ruído à rede: o teste cai de 86,1% para 76,3%.
- Paciência de menos para o treino numa oscilação casual da curva.
Mais a fundo: fórmula, código, prática
A matemática
Perda de validação depois da época e: L_val(e) = −(1/m) · Σᵢ log p_e(yᵢ | xᵢ) em m imagens de validação (entropia cruzada).
A regra com paciência P: e = argmin_{e ≤ t} L_val(e); o treino para na primeira época t com t − e ≥ P e devolve os pesos w(e). Se essa época não chegar, devolve w(e) de todo o percurso.
A parada antecipada age de forma parecida com a penalidade da aula 3.4. Com descida do gradiente e passo pequeno, os pesos começam perto de zero e se afastam dele a cada época. Menos épocas significam pesos menores: para um modelo linear com perda quadrática, parar depois de t passos tem mais ou menos o mesmo efeito que ridge com α da ordem de 1 / (passo · t).
No código (Python)
O mesmo experimento no scikit-learn: 10 divisões e sorteios de ruído diferentes, uma rede 64 → 32 → 10 treinada época a época, guardando os pesos da época com a menor perda de validação.
import copy
import numpy as np
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
from sklearn.neural_network import MLPClassifier
from sklearn.metrics import log_loss
X, y = load_digits(return_X_y=True)
X = X / 16
def run(seed): # 300 dígitos para aprender, 500 para validação, os outros 997 para o teste
used, te = train_test_split(np.arange(len(y)), train_size=800, stratify=y, random_state=seed)
tr, va = train_test_split(used, test_size=500, stratify=y[used], random_state=seed)
rng = np.random.default_rng(seed)
noisy = y[tr].copy()
flip = rng.random(len(tr)) < 0.2 # 20% dos rótulos de treino trocados por um dígito aleatório
noisy[flip] = rng.integers(0, 10, flip.sum())
net = MLPClassifier(hidden_layer_sizes=(32,), solver="sgd", learning_rate_init=0.05, momentum=0.9, batch_size=32, alpha=0, random_state=seed)
best, best_loss = None, np.inf
for epoch in range(1, 101):
net.partial_fit(X[tr], noisy, classes=np.arange(10))
loss = log_loss(y[va], net.predict_proba(X[va]), labels=np.arange(10))
if loss < best_loss:
best, best_loss, best_epoch = copy.deepcopy(net), loss, epoch # a parada antecipada guarda os pesos da melhor época
return best_epoch, best.score(X[te], y[te]), net.score(X[te], y[te])
r = np.array([run(s) for s in range(10)])
print(f"10 splits and noise draws: lowest validation loss at epoch {r[:, 0].min():.0f} to {r[:, 0].max():.0f}; test at that epoch {r[:, 1].mean():.1%}, after 100 epochs {r[:, 2].mean():.1%}; lower after 100 in {(r[:, 2] < r[:, 1]).sum()} of 10")
Saída
10 splits and noise draws: lowest validation loss at epoch 13 to 23; test at that epoch 87.2%, after 100 epochs 76.4%; lower after 100 in 10 of 10
As divisões, os pesos iniciais e o ruído são diferentes dos do lab, então os números também. O quadro é o mesmo: a melhor época vem cedo (entre 13 e 23), e treinar até 100 épocas baixa a acurácia de teste em todas as 10 execuções, em média de 87,2% para 76,4%.
Baixar o notebook (.ipynb). Abra no Jupyter ou envie para o Google Colab; ele baixa sozinho os dados que faltarem.
Para quem pratica
- No Keras:
EarlyStopping(monitor="val_loss", patience=10, restore_best_weights=True). Semrestore_best_weights=Trueficam os pesos da última época, não os da melhor. - No PyTorch você mesmo escreve o laço: depois de cada época calcule a perda de validação e guarde uma cópia dos pesos sempre que ela for a menor.
- No scikit-learn:
MLPClassifier(early_stopping=True, n_iter_no_change=...)separa uma parte dos dados de treino para validação; ele vigia a acurácia de validação, não a perda. No gradient boosting a mesma ideia se chaman_iter_no_changeouearly_stopping_rounds. - O número de épocas escolhido na validação também é um hiperparâmetro: o resultado de validação na melhor época é um pouco inflado (aula 3.5), então faça a avaliação final em um teste reservado.
Isto foi útil?
Obrigado! Isso nos ajuda a melhorar.
Vá mais fundo no Atlas
Verbetes da enciclopédia sobre as ideias desta aula: