验证损失(val_loss)更稳定,适合大多数场景;验证准确率(val_acc)波动大,尤其在类别不均衡或小验证集时易误触发早停;推荐默认用val_loss并设mode="min",仅当明确关注业务指标且验证集足够大时才换用val_acc。

早停判断该用验证损失还是验证准确率?
验证损失(val_loss)更稳定,适合大多数场景;验证准确率(val_acc)波动大,尤其在类别不均衡或小验证集时容易误触发早停。PyTorch本身不内置早停,得自己写逻辑,核心是监控一个指标并比较历史最优值。
推荐默认用 val_loss,因为它是连续可微、梯度友好的标量,且模型优化目标本就是最小化损失。只有当你明确关注业务指标(如分类准确率、F1)且验证集足够大时,才考虑换用 val_acc。
- 用
val_loss:设mode="min",越小越好 - 用
val_acc:设mode="max",越大越好 - 避免混用——比如训练用
CELoss,早停却盯top_k_accuracy,二者优化方向可能不一致
如何写一个轻量、可复用的EarlyStopping类?
别依赖第三方包(如 pytorch-lightning 的 EarlyStopping),自己写 20 行以内就能覆盖 95% 场景。关键是保存「当前最优指标」、「连续未改善轮数」和「是否触发停止」三个状态。
下面是一个生产可用的最小实现:
立即学习“Python免费学习笔记(深入)”;
class EarlyStopping:
def __init__(self, patience=7, min_delta=0.001, mode="min"):
self.patience = patience
self.min_delta = min_delta
self.mode = mode
self.best_score = float('inf') if mode == "min" else float('-inf')
self.counter = 0
self.early_stop = False
<pre class="brush:php;toolbar:false;">def __call__(self, score):
if self.mode == "min":
improved = score < (self.best_score - self.min_delta)
else:
improved = score > (self.best_score + self.min_delta)
if improved:
self.best_score = score
self.counter = 0
else:
self.counter += 1
if self.counter >= self.patience:
self.early_stop = True-
min_delta防止因浮点抖动误判——比如val_loss从 0.12345 降到 0.12344,不算实质提升 -
patience=7是常用起点,但实际要结合训练总 epoch 数调整:总 epoch=50 时,patience=3更合理 - 每次调用
early_stopping(val_loss)后,检查early_stopping.early_stop即可决定是否 break
在训练循环中插入早停的正确位置和时机
早停必须在验证阶段之后、优化器更新之前执行。错放到 epoch 开头或 loss.backward() 后,会导致模型多训一轮或漏保存最佳权重。
典型错误顺序:
→ 计算 train_loss → backward → step → 再验证 → 再早停(已更新参数,无法回退)
正确顺序:
for epoch in range(max_epochs):
# 训练一个 epoch...
train_one_epoch(model, train_loader, optimizer)
<pre class="brush:php;toolbar:false;"># 验证一个 epoch,得到 val_loss
val_loss = validate(model, val_loader)
# ✅ 此处调用早停,尚未修改模型参数
early_stopping(val_loss)
if early_stopping.early_stop:
print(f"Early stopping at epoch {epoch}")
break
# ✅ 只有没停止,才保存当前最佳模型
if val_loss < best_val_loss:
best_val_loss = val_loss
torch.save(model.state_dict(), "best_model.pth")
- 务必在
validate()后立刻调用早停,不要穿插其他逻辑 - 保存模型和早停判断要解耦:早停是决策,保存是动作,两者条件可以不同(例如只在验证指标提升时保存)
- 注意
model.eval()和torch.no_grad()必须在验证时启用,否则影响val_loss稳定性,间接导致早停误触发
为什么加载最佳模型后推理结果反而变差?
常见原因是验证时没关 dropout / batch norm,或者保存/加载时遗漏了 model.eval() 状态。早停本身不负责模型状态管理,它只管“什么时候停”,但“停在哪一刻的权重”得你手动保证。
最容易被忽略的一点:早停触发时,当前 epoch 的模型参数未必是最优的——因为你在验证后才判断,而验证用的是上一轮保存的权重。所以必须在验证提升时就保存,而不是等早停再保存。
- 永远在验证指标优于历史最佳时立即保存(
torch.save()),而非在early_stop==True时保存 - 加载时记得调用
model.eval(),否则 dropout 仍生效,输出不稳定 - 如果用了
torch.compile()或混合精度(amp),保存前需先model = model.float()再保存,避免加载时报 dtype 不匹配


















