Keras自定义损失下训练末轮与同数据评估损失差异大问题
问题根因排查与修复方案
训练集拟合到近0损失、同数据评估损失异常偏高,是自定义损失、训练逻辑、模型层配置三类问题共同导致的,按优先级排查如下:
1. 先修复自定义损失里的显性bug
- 分类损失笔误:当前分类损失项是拿预测值和预测值做MSE,这部分损失永远为0,分类分支完全没有监督信号,属于硬编码错误。
找到对应代码行:
修改为:class_loss = mse((exists_box * y_pred[..., 10:]), (exists_box * y_pred[..., 10:]))class_loss = mse((exists_box * y_pred[..., 10:]), (exists_box * y_true[..., 10:])) - 损失缩放逻辑冲突:损失内部实例化MSE时用了
SUM聚合,也就是已经把batch、网格、通道维度的误差全部求和成单个标量,但YoloLoss类初始化时没有指定reduction参数,默认用SUM_OVER_BATCH_SIZE,Keras会对已经聚合的标量再次做平均缩放,训练和评估阶段的缩放逻辑不一致会直接导致损失值量级差。
修改YoloLoss的初始化方法:def __init__(self, name="yolo_loss", **kwargs): super().__init__(name=name, reduction=tf.keras.losses.Reduction.SUM, **kwargs)
2. 排查训练流程的低级错误
- 回调干扰单批次拟合:测试过拟合时加了
stop_early、checkpoints回调,还传入了验证集,会直接破坏测试逻辑:- EarlyStopping会在验证集损失不下降时提前终止训练,根本跑不到500轮
- 如果ModelCheckpoint开了
save_best_only=True,保存的是验证集损失最低的早期权重,不是训练到最后拟合单批次的权重,评估时如果加载了这个权重,损失自然异常
单批次过拟合测试阶段,直接去掉所有回调和validation_data参数,确保训练跑完500轮,评估时直接用内存中训练完成的模型,不要加载checkpoint。
- 生成器不一致:fit传入的是
training_batch_generator,evaluate传入的是yolo_training_batch_generator,先确认这是同一个实例,且预处理逻辑完全一致:- 图像预处理必须统一做归一化(比如都除以255、都用相同的RGB/BGR通道顺序)
- 标签预处理传入的
grid_len和损失里的全局变量GRID_LEN必须完全相等,不然reshape逻辑会直接打乱标签维度。
3. 修复模型推理模式的数值偏差
这是单批次过拟合测试最常见的坑:
- 批归一化(BN)层滑动平均不匹配:BN层在训练模式下用当前输入batch的均值/方差做归一化,推理模式下用训练过程中滑动平均累积的全局均值/方差。默认BN的momentum是0.99,也就是每轮只更新1%的滑动值,即使单个batch训500轮,滑动平均和当前batch的真实统计量仍有偏差,推理时输出分布偏移直接导致损失飙升。
单批次测试时临时把所有BN层的trainable设为False,或者把momentum改成0,让推理时直接用当前batch的统计量,排除BN的干扰。 - 硬选择边界的跳变问题:用
K.argmax直接选IOU最高的预测框作为责任框属于硬选择,当两个预测框的IOU非常接近时,推理时哪怕输出只有极小的数值波动(比如BN带来的偏移),都会导致argmax切换选中的框,直接让损失跳变。过拟合测试阶段可以先把双预测框改成单预测框,排除硬选择的干扰。
内容的提问来源于stack exchange,提问作者Dav Bhaji
相关产品推荐
相关产品推荐

