首页 / 资讯中心 / 文章详情

图解MatConvNet训练日志:objective、top1error与top5error的区别与实战解读

图解MatConvNet训练日志:objective、top1error与top5error的区别与实战解读 ★ FEATURED ARTICLE
前两天有个同学发了一张训练日志截图给我说自己的模型 objective 已经从 3.2 降到了 0.8但 top1error 几乎没怎么动问我是不是哪一行代码写错了。这种情况在深度学习调参里非常典型很多人一开始都会把 objective、top1error、top5error 这三行数字当成同一回事其实它们背后对应的是完全不同的评估逻辑。这三个指标也是我用 MatConvNet 做图像分类时最常盯着的输出项这篇笔记就把它们彻底掰开揉碎讲一遍包括定义、在框架里的真实计算方式、训练日志应该怎么读以及我踩过的一些坑。如果你正准备用 MatConvNet 训练自己的分类模型或者已经跑起来了但对这几行指标似懂非懂这篇文章应该能帮你省下不少瞎猜的时间。标题里的 matcovnet其实就是 MatConvNet 这个基于 MATLAB 的卷积神经网络工具箱下面统称 MatConvNet。1. 这三个指标到底是什么它们之间有什么区别1.1 一句话先记住objective 是“损失”error 是“错误率”很多教程把这三个名字混着说这是最容易误导新手的地方。先给一个能一直用下去的结论objective 是模型在训练时真正被优化的目标函数值也就是损失函数loss在当个 batch 上的平均值top1error 是模型预测的最可能类别不正确所占的比例top5error 是真实类别没有落在模型预测概率最高的前五名里所占的比例。用考试来类比最直观。objective 相当于这次考试的“总分距离满分还有多远”它是一个平滑的连续分数模型每多答对一点分数就会变一点top1error 相当于“只有第一名预测完全正确才算对”的严格判卷top5error 相当于“允许你猜五次只要有一次蒙对就放行”的宽松判卷。MatConvNet 在训练时之所以把这三个数字放在一起打印是因为它们分别从两个维度描述模型状态objective 告诉你模型在“内部学习”上进行得顺不顺error 告诉你模型在“外部评价”上表现得好不好。两者有相关性但并不是一一对应的关系。1.2 模型训练里为什么同时需要两套指标如果只优化 top1error 本身这个指标是一个不可导的离散函数你没法对它求梯度去更新网络参数。所以我们必须找一个连续的、可导的替代品来当训练目标这就是 objective 存在的意义。反过来objective 是一个数学上的抽象数值模型部署到实际场景后用户感知不到 loss 是什么他们只知道“你猜对了没有”所以 error 才是真正反映业务效果的指标。这就是为什么训练日志里总是同时出现 objective 和 error 的原因objective 负责驱动梯度error 负责汇报战果。MatConvNet 自带的cnn_train.m脚本也是这个思路每个 epoch 结束后都会统计训练集和验证集的objective、error、topFiveError并保存下来方便你画曲线判断模型状态。1.3 top1 和 top5 的严格程度完全不同top1error 要求模型预测概率最大的那个类别正好等于真实标签真实场景中大多以这个为准。top5error 则宽松得多它允许模型给出五个候选答案真实标签只要在这五个里面就算对这最初是从大规模图像分类比赛里沿用下来的规则——因为类别数上千的时候人眼都很难一眼区分某些相似类别所以允许“五次机会”来评估模型到底有没有学到东西。这两个口径很容易出现数值差别很大的情况一个模型 top1error 是 25%top5error 可能只有 8%。这说明模型虽然经常不能把正确类别排到第一但它通常能把正确类别排进前五。对很多实际产品来说这种“大范围候选正确”是有意义的可以配合后续的二次筛选流程使用。2. objective 详解损失函数到底在优化什么2.1 分类网络的最后一层和损失是怎么接上的在 MatConvNet 里一个典型的图像分类网络最后一层是全连接层输出一个长度为 K 的向量K 就是类别总数。假设输入一张图网络最后给出的原始得分叫 scores这个得分本身不是概率只是一组实数。要让这个得分能用于训练通常会在全连接层后面接一个 loss 层框架内部对 scores 做 softmax 归一化把它变成“属于各类别”的概率分布。这个过程的代码非常简洁。如果使用 MatConvNet 的 SimpleNN 风格网络最后一层定义可能长这样net.layers{end1} struct(type, conv, ... weights, {init_weight(1, 1, 4096, K)}, ... stride, 1, ... pad, 0); net.layers{end1} struct(type, softmaxloss); % 等价于 vl_nnloss前向传播拿到结果后损失层自己会完成 three 件事计算 softmax、计算交叉熵损失、计算 top1error。2.2 softmaxlogloss 的公式和直觉MatConvNet 里最常用的是softmaxlogloss它的数学形式是一个带 log 的交叉熵。对第 n 张样本假设真实类别是 c_n模型对类别 c_n 的 softmax 概率是 p_n那么单张样本的损失是L_n -log(p_n)这个公式看起来简单但背后的直觉很深刻。我们想要的不是“让正确类别的得分尽可能高”而是“让正确类别的概率尽量接近 1”。log 函数在输入接近 1 的时候斜率比较大在输入接近 0 的时候斜率也比较大它把概率空间里的小差距放大成梯度上可感知的变化让模型能更快地把正确类别的概率推高。如果不用 log直接用 softmax 概率当作损失比如 L 1 - p_n那么模型在 p_n 0.7 和 p_n 0.9 时损失差异是 0.2感觉差不多但用 log 之后这两个状态的损失差异是 0.28 对 0.10差距明显得多。更重要的是log 能把“几乎全错”的情况给出无限大的损失这会让训练过程自动集中火力在最难分的样本上。2.3 一个 batch 的 objective 是怎么算出来的训练时不可能一张图一张图地更新参数通常是取一个 batch 的样本一起算损失。假设 batch 大小是 N那么 MatConvNet 的vl_nnloss会对 N 张样本的平均损失返回结果[loss, top1err] vl_nnloss(scores, labels);这里 scores 就是网络最后一层输出的原始得分通常是一个 1×1×K×N 的四维数组labels 是 N 个真实类别编号。返回的第一个值就是 objective第二个值是 top1error。有个细节需要提醒vl_nnloss默认会对 batch 内所有样本求平均但如果你某个 batch 里样本类别分布很不均匀可能导致损失被样本数多的类别主导。vl_nnloss支持传入instanceWeights参数来给每个样本加权这在处理类别不平衡数据集的时候很有用。还有一个normalize参数默认是 true它会让损失除以样本总数如果你在对比不同 batch size 下的 loss 值这个参数会直接影响数值的绝对值大小不太建议随便改。2.4 objective 不只是用来显示它还直接决定梯度objective 在训练里不是被动监控指标它同时决定了反向传播的梯度方向和大小。在 MatConvNet 里vl_nnloss同时实现了前向和反向两个功能。训练阶段你调用它时会传入 dzdy也就是上游梯度它会计算出对输入的导数并继续往前传[loss, top1err] vl_nnloss(scores, labels, dzdy);当你只做验证推理时不需要反向传播就不传 dzdy这样能省掉很多不必要的计算。很多人会在验证阶段误把训练模式的完整代码搬过来导致明明只想算指标结果却把整个网络反向传播也跑了一遍既浪费时间又容易出错。3. top1error 与 top5error 详解错误率的计算逻辑3.1 top1error 的本质是一个离散的决策结果不管模型输出多少复杂的概率分布最终 top1error 关心的只有一个结果模型给每个样本打出的最高得分对应的类别是不是就等于真实标签。如果不等这个样本就算错。在 MATLAB 里最朴素的 top1error 计算方式是这样的[~, pred] max(scores, [], 3); % scores: 1x1xKxN取类别维度最大值 pred squeeze(pred); % 变成 1xN 的预测类别 labels squeeze(labels); top1err mean(pred(:) ~ labels(:));这个指标看起来机械但它非常诚实。模型在概率 0.99 时预测正确和概率 0.51 时预测正确最终都算正确error 只看最终决策不管模型有没有底气。这也解释了为什么 top1error 是最终评估模型能力最常用的硬指标。3.2 top5error 是怎么手算出来的top5error 的逻辑是对每个样本把模型输出的 K 个类别得分从高到低排序取前五名然后看真实标签在不在这个集合里。不在就记错。如果你自己写训练循环需要手动算 top5代码也非常直白function err top5error(scores, labels) % scores: 1x1xKxNlabels: 1xN scores squeeze(scores); [~, idx] sort(scores, 1, descend); top5 idx(1:5, :); % 5xN 的候选类别 match any(top5 labels, 1); % N 个样本是否命中 err 1 - mean(match); end在 MatConvNet 里vl_nnloss的返回值中只自带 top1error官方示例脚本cnn_train.m会额外统计 topFiveError但如果你用的是自己搭的 DAG 网络训练流程就会发现 top5 真的需要自己写。不要嫌麻烦它值得你花两分钟加上后面读日志时能多个信息维度。3.3 top5 在什么样的场景下才有意义top5error 不是在所有任务里都有同等价值。如果你做的是 10 分类的 CIFAR-10 这样的数据集随机猜测的 top5 错误率是 50%模型随便学一学就很容易达到 90% 以上的 top5 准确率这个指标几乎没有区分度论文里也很少有人对这类任务报 top5。但如果你的任务有 1000 个类别比如常见的大规模图像分类数据集随机猜测 top5 错误率高达 99.5%这时候模型如果能达到 top5error 20%就是非常强的信号——说明在绝大多数样本上正确类别至少都在网络的前五个候选里面。再配合 top1error 使用就能看出模型是“刚好猜对第一名”还是“对正确类别有较强的排名能力”。3.4 为什么论文经常一起报两个指标大规模分类任务中类别之间存在天然的相似性比如某个类别是“狼犬”另一个是“狼”人眼有时都会犹豫。top1error 会把这种犹豫直接判成错误top5error 则宽容得多允许模型给出多个候选只要真实类别在里面就算对。所以论文里同时报 top1 和 top5 是在告诉读者这个模型既能在硬性指标上达到一个水平也对相似类别有不错的包容性。对实际产品设计来说top5 还意味着一种可能性如果你的产品允许展示多个推荐结果或者有后续模块对候选结果做再判断那么 top5error 就更接近真实用户体验。反过来如果产品只允许给一个最终答案那 top5 再好看也意义有限最终评价还是要回归 top1。4. 训练日志应该怎么读才不会越读越慌4.1 一张常见的训练日志表用 MatConvNet 训练时控制台或日志文件里通常会输出类似这样的一段记录我简化成表格方便你看epochtrain objectivetrain top1errtrain top5errval objectiveval top1errval top5err16.870.9990.9946.910.9980.99353.420.610.373.550.630.40101.250.250.091.500.310.12200.480.080.020.920.220.08300.120.010.0010.730.270.11这张表是我模拟的 1000 类任务的训练过程有几点非常典型训练集的 objective 和 error 都在持续下降验证集的指标也会下降但到后面会和训练集拉开差距top5err 始终明显低于 top1err。4.2 正常的“学习信号”长什么样判断训练是否正常最核心的一点是看验证集指标是否在稳定下降而不是看训练集数值有多漂亮。在训练初期trian objective 应该从接近log(K)的初始值开始往下降K 是类别数top1err 会从接近 99% 的水平开始往下掉top5err 比 top1err 掉得更快。这里有一个值得记住的数字如果网络初始化合理第一次迭代时 loss 应该大约等于log(K)。比如 1000 类的大约 6.910 类的大约 2.3。如果你第一次迭代的 loss 远大于这个值很可能是初始化有问题、标签没对齐或者学习率太大了。另一个正常现象是 loss 和 error 的下降节奏不完全同步。经常能看到 loss 已经从 6.9 降到 4.0但 top1err 还是 98% 没怎么动。这不一定是训练出问题了可能是模型还在学一些“低置信度”的特征概率分布虽然变化了但还没能把任何类别的得分明显推到最高位置。4.3 最经典的现象loss 在降error 却不动这是很多新手会慌的情况也正好是我同学问我的问题。解释起来其实不复杂objective 是连续平滑的error 是离散硬性的。模型对正确类别的概率从 0.25 提高到 0.40loss 会明显下降但如果它仍然是第二高top1error 就不会变。换句话说模型在对“正确答案是哪个”这件事上变自信了但最终决策排序还没有翻盘。等训练继续推进当正确类别的概率从 0.40 超过其他类别变成第一名时top1error 就会突然掉一截。所以你看到的曲线往往是 loss 平稳下降、error 阶梯式下降。这不是代码有 bug这是很正常的学习过程。反过来也要警惕一种少见的情况loss 下降但 error 持续上升或者 error 长期不动。那通常说明模型的“概率分布越来越尖锐但排序不对”常见于标签噪声过大、类别定义不清晰、或者网络容量不够。这时候与其盯着超参数调不如先抽样检查一批喂给网络的数据和标签是否对齐。4.4 训练集和验证集两套指标怎么配合着看MatConvNet 训练时会把训练集和验证集的指标分开统计。我最常用的方法是盯三个差值train objective 与 val objective 的差值、train top1err 与 val top1err 的差值、train top5err 与 val top5err 的差值。如果这个差值在训练后期不断变大而 val 指标本身已经不再下降甚至回升这就是过拟合的经典信号。出现这种情况后的处理顺序是先看 val top1err 是否还在下降如果还在下降就继续训练如果已经开始回升再考虑提高数据增强强度、增大 weight decay或者降低学习率做微调。不要一看到 train 好看就高兴train 的指标再漂亮也只是幻觉的起点真正决定模型能不能用的永远是 val 或者测试集上的指标。5. 实操经验踩过的坑、排查思路和调参心得5.1 常见问题速查表用 MatConvNet 调这些指标的过程中我整理了一张速查表遇到诡异情况可以先对照一下现象可能原因排查建议第一个 epoch 的 loss 远大于 log(K)初始化不当、标签对齐错位检查标签映射重新初始化网络loss 下降 but top1err 几乎不动模型在提升置信度但排名未变化继续训练不要急着改结构val 的 top5err 比 top1err 高代码写反了或者标签有严重错误检查 top5 计算逻辑train 指标很好val 指标差很多过拟合增加数据增强、增大正则、早停objective 出现 NaN学习率过大、数值不稳定降低学习率检查是否有 NaN 梯度top5err 一直很高不下降类别数特别少top5 本来就没有区分度改用 top1 或 top3 观察每个 epoch 的误差数值波动很大batch size 太小增大 batch size或看平滑后的曲线5.2 三个你可能没注意到的小细节第一个细节batch size 的大小会明显影响 objective 和 error 的波动幅度。batch size 越小平均损失越不稳定loss 曲线就像锯齿一样跳来跳去top1error 更是会在小 batch 上出现大范围震荡因为一个 batch 里多一两个错分样本错误率就能差好几个百分点。所以看训练趋势最好用滑动平均或者直接看每个 epoch 结束后的验证集指标而不是盯着单次迭代的输出。第二个细节类别数特别少的任务建议直接忽略 top5。之前说过10 分类任务的随机 top5 基线是 50%模型稍微学到点东西 top5 就能到 80% 以上看久了容易自我感觉良好反而掩盖了 top1 还很差的真相。这种情况更推荐直接看 top1 或 top2信息量更大。第三个细节当你比较两个模型的性能时不要只看最终的 top1 数字应该把 objective 曲线和 top5 曲线一起打出来看。有时候两个模型最终的 top1err 相同但其中一个的 top5err 明显更低这说明它在相似类别的区分上更有潜力后续接一个重排序模块时提升空间更大。5.3 类别数对 top5 有效性到底有多大影响我做一个直观的对比假设模型是随机猜测K 个类别下类别数 K随机 top1 错误率随机 top5 错误率top5 可用性1090%50%很低参考价值一般10099%95%有一定区分度100099.9%99.5%高常见用途1000099.99%99.95%非常高几乎必备从这个表就能看出为什么大规模图像分类任务几乎都报 top1 和 top5类别数越多top5 越能提供“模型对人类相似类别判断能力”的有效反馈。反过来如果你在只有 10 类的数据集上报 top5error给内行看到只会觉得你不懂评估指标的选择逻辑。5.4 调参时怎样利用这三个指标做决策我自己调参时有一套比较固定的判断流程。训练刚开始的几十个迭代我只关心 objective 是否在稳定下降如果这个阶段 loss 都不降那后面所有指标都没有意义。当 loss 开始正常下降后我转而关注 val top1err 的下降趋势它是最接近部署目标的硬指标。如果 val top1err 降到一定水平开始停滞我再去看 val objective 和 val top5err 的表现判断模型是“完全不行”还是“只差临门一脚”如果 val top1err 停住但 val top5err 还在下降说明模型其实在继续学习只是还没学会把正确类别排到第一位。这时候可以尝试调低学习率继续训练或对困难样本做挖掘。如果 val top5err 也同时停住说明模型能力接近上限这时候优先考虑的是数据量、网络结构、数据增强这些方向而不是继续调学习率。还有一点是关于观察周期不要看单个 epoch 就下结论尤其验证集指标在小数据集上会有明显波动。我一般至少连续看 4~5 个 epoch 的趋势才决定是继续训练、降低学习率、还是提前停止。完全跟着单次验证集误差走很容易做出错误的早停决定。最后再分享一点实际操作中的体会说实话跑过几次完整的训练之后我越来越觉得这三个指标里最值得盯着的就是 val top1error它是模型真实能力的“底线标准”而 objective 更像是给优化器看的内部反馈。我自己的习惯是每次训练打印都同时带上这三个指标每隔几轮瞄一眼趋势重点看两条objective 是否在下降、top1err 和 top5err 的差距是否合理。如果 loss 降了但两个 error 都不动大概率是学习率太小或者模型容量不够如果 top1err 慢但 top5err 降得很快说明模型已经在排序层面赢下大量样本了这时候可以稍微乐观一点给它更长的训练周期。另外一个小技巧训练时可以把 top1err 和 top5err 的差值一起打印出来。这个差值越大说明模型把大量样本排在第二到第五名之间离“正确答案第一”还有距离随着训练推进这个差值通常会被压缩。如果差值很久不缩大概率是相似的混淆类别太多这时候可以考虑损失函数里加入一些辅助监督或者检查类别定义是否需要合并。总之这三个数字单独看都只是一个数值组合起来读才是真正的诊断工具。
阅读完成 · 觉得有帮助?
咨询建站