ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

训练集与测试集精度对比的误判陷阱与正确排查方法

训练集与测试集精度对比的误判陷阱与正确排查方法 我直接说结论训练集精度和测试集精度从来不是用来比大小的它们是用来判断模型到底处于什么状态的两盏指示灯。很多人拿到一组结果——训练集 0.98、测试集 0.63——第一反应是过拟合了换正则化、加 dropout但我见过太多人折腾两周发现根本不是这么回事。这篇文章想聊透一件事从这两条精度曲线的关系里你能读出什么、误判什么以及真实项目里该怎么一步步排查。读者对象是正在用 PyTorch、yolov8、halcon 或类似工具训练自己数据集的人不管你是刚开始学深度学习的入门选手还是被某个工业检测项目卡了好几周的工程师这套判断方法都适用。1. NaN? 不先从最容易被误读的精度说起1.1 训练精度测的是记忆测试精度测的是推演深度学习的本质矛盾就一句话模型在训练集上见过的东西换了一张没见过的图它还能不能给出正确的结果。这个能力叫泛化generalization。训练集精度衡量的是记住了多少——它对训练样本的拟合程度测试集精度衡量的是推演有多准——它对未知样本的判断能力。拿考试类比特别形象训练精度是你把同一套题反复做了二十遍之后模拟考的成绩测试精度是你拿到一套全新题目时的真实水平。这两个分数高相关但绝不相等。一个学生可以把题库背得滚瓜烂熟模拟考次次满分新题一出来照样抓瞎。模型也是一样理论上神经网络有足够的参数去背下整个训练集甚至背下训练集里的噪声。为什么会有这种差异稍微扯一点理论泛化误差界告诉我们模型在测试集上的期望误差可以被分解成训练误差加上一个跟模型复杂度、数据量有关的惩罚项。训练集精度高只能说明第一项低第二项是独立存在的——你的模型如果容量太大、训练太久、数据太少这个惩罚项就会变大两条精度曲线就会劈叉。所以训练集精度高并不是什么值得开心的成就它只是说明模型有足够的表达能力去拟合数据不说明任何泛化能力。1.2 两条精度曲线的四种典型组合别对号入座太早很多人在拿到训练结果之后会陷入一种数字焦虑看到某个组合就潜意识地开始想对应的解法。我把最常见的四种组合先列出来但先别急着套方案后面几节会讲很多时候你对组合的判断本身就是错的。训练集精度测试集精度最可能的信号常见处理方向高高正常模型收敛良好保持考虑进一步调优/压缩高低过拟合或数据分布不一致正则化、数据增强、检查泄漏低低欠拟合或不收敛加大容量、调学习率、检查数据低高指标失真或数据划分失衡检查测试集难度、样本数量和标注第四条训练低、测试高看起来反直觉但真实存在。后面专门展开讲。1.3 别只盯着精度看精度是一个很钝的指标说一个很多人踩过的坑训练日志里精度从第 5 个 epoch 开始就不动了一直在 92% 附近震荡损失却还在缓慢下降。这时候你会不会觉得模型已经到头了调不动了不一定。精度accuracy是一个离散指标——它对一个 batch 里的所有样本一刀切每个样本要么对要么错它只关心预测的 argmax 是不是和标签一致完全不关心模型到底有多确信。而损失函数是连续指标它会在模型从错得很离谱到错得很接近再到刚好跨过边界的整个过程中持续给出反馈。所以损失还在降、精度却不动很可能只是说明模型在让正确的样本变得更正确但尚未推动任何错误样本跨过决策边界。这意味着判断过拟合、欠拟合永远要看损失曲线精度曲线的组合而不是只盯精度。早停、选模型、找最优 epoch全部应该以验证集的损失为主参考精度为辅。这是我反复在实际项目里验证过的做法下面每一节的分析也都建立在这个基础上。2. 训练精度低、测试精度也低——先分清没学会和学不动2.1 两个成因方向模型容量不够与优化过程失败训练集精度低最直接的含义是模型连标准答案都没记住。这个状态一般来自两个完全不同的方向。第一个方向是模型容量不足。网络太浅、通道太少或者你给分类任务的 backbone 是一个表达能力不够的结构它根本没法在训练集上拟合出决策边界。这种情况你加大模型容量或者换更强的 backbone比如从 mobilenet 换到 efficientnet 或 vit通常会有效。第二个方向是优化过程失败。模型容量是够的但训练过程没有正常收敛——学习率太大loss 像心电图一样上下震荡爬不上去学习率太小模型走得比蜗牛还慢某个模块比如 BatchNorm 在 batch size 极小时表现异常激活函数或者数据归一化没做好导致梯度消失或爆炸优化器选得不对训练一直在原地踏步。区分这两条方向有个很笨但很有效的办法看看 loss 曲线的形状。如果 loss 从一开始就在一个高位震荡偶尔突刺多数是学习率偏大或优化器设置有问题。如果 loss 一路平滑下降但非常慢几个 epoch 才挪一点可能是学习率太小也可能是数据没有做归一化。如果 loss 下降一段之后突然变成 NaN那是数值不稳定通常和梯度爆炸有关。2.2 一个具体案例焊接瑕疵分类项目里训练精度被卡死的排查链路有个真实的工业项目做焊接件瑕疵分类总共六类缺陷。用的 halcon 深度学习工具先跑了一轮训练精度始终在 55% 上下测试精度也在 52% 左右。当场的第一反应是dataset 太小了模型学不出来但仔细看了数据之后发现根本不是。完整排查链路是这样的第一步先看训练曲线。输出显示 loss 在最初的几十个 iteration 里从 1.8 降到 1.5然后死活不降了精度一直在 50% 附近。50% 对于六分类问题来说距离随机猜测的 16.7% 明显高但距离可用很远——这说明模型学到了一些信息但被什么东西卡住了。第二步把 batch 里的输入图像直接打印出来看。发现图像来自产线相机是 12bit 的原始图读进来之后直接喂给了网络没有做归一化像素值普遍集中在 2000-4000 的一个窄区间内。网络对这个尺度非常不敏感梯度更新效率极低。第三步改成最基础的 z-score 归一化把像素分布拉到 0 均值、单位方差附近。再训练第一个 epoch 精度就跳到了 87%。继续训练到收敛训练精度 96%测试精度 88%。这个案例最大的教训是训练集精度低时第一步永远先检查数据管线而不是急着改网络结构。一张妖怪输入图会让整个训练过程无效化而深度学习框架默认不会给你任何报错你只能看到精度低但根本不知道源头在哪。2.3 数据标注错误导致的假性欠拟合还有一种情况训练集精度低但你抽查模型预测结果时发现——模型预测的很多错误其实是对的。这时大概率是标签错了。我在分类项目里遇到过一批错标数据一个类别的图像混入了大量另一个类别的样本标签和图像内容对不上。模型试图同时拟合两批矛盾的数据精度必然被拉低。最讨厌的是当标注错误率达到 5%-10% 的时候训练精度会卡在一个上不去的平台期看起来像欠拟合实际上模型已经在对抗噪声了。怎么排查简单有效的办法是把训练集里模型的 top-1 错误样本按预测置信度排序然后人工抽查前几十张图。如果你发现模型错得很合理比如一张被标为 A 类但预测 B 类的图人类肉眼看着就是 B 类那就是标注错误抓紧修数据比改任何网络都有效。另外类别不平衡也会制造这种假象某个类只有几十个样本另一个类有上万样本模型为了整体精度会直接忽略小类——这时候整体精度看起来还凑合但每个类别的精度一拆开就露馅了。3. 训练精度远高于测试精度——过拟合还是数据泄漏3.1 过拟合不是差距大这么简单的判据训练集精度 98%测试集 88%这算过拟合吗看情况。如果数据集十万张、类别均衡、标注干净这个差距非常正常甚至已经是很好的结果。反过来如果训练集只有两千张图片同样的差距那模型几乎可以肯定处于过拟合状态。所以判断过拟合要看差距更要看数据量和任务难度。经验上有一个大致的参考在中等规模数据几千到几万张的分类任务里训练集和测试集精度差距在 5 个百分点以内算健康超过 15 个百分点基本可以确定过拟合。目标检测任务比如 yolov8 训练自定义数据里这个差距通常会拉得更大因为检测任务中 anchor 匹配和 NMS 后处理本身就会造成训练/推理行为不一致不一定是过拟合。过拟合的本质是模型记住了训练集里不该记住的个体特征而不是学到了一类样本的共性。比如识别狗的任务里训练集中的狗都穿着红色项圈模型可能学的是有红色项圈狗这个特征在测试集里一旦不成立就会翻车。3.2 排在过拟合之前的头号嫌疑数据泄漏我在无数项目里发现所谓过拟合里有相当一部分是数据泄漏。就是训练集中混入了测试集的影子——语义上重叠、甚至完全相同的样本同时出现在两边。这会让测试集精度虚高但如果测试集里的泄漏样本恰好很少也可能表现为训练集精度高、测试集精度低因为测试集里真正没见过的样本模型确实没见过。泄漏有几个最常见的来源划分前做了全局归一化。把整份数据的均值、方差都算好再划分训练/测试集测试集的统计信息已经透传给了模型。正确做法是先划分、后只在训练集上计算归一化参数。增强之后没有隔离。你基于一张训练图做了平移、翻转、加噪的多张增强图如果其中某些被误分到了验证集或测试集等于开卷考试。时序数据按帧切分。视频帧之间高度相似如果你按帧随机划分训练集和测试集可能包含同一段连续动作的相邻帧模型相当于见过考试内容。工业场景中同一产线、同一批次的图像也高度相似必须按时间段或按产品编号分组划分而不是按单张图随机划分。重复样本没有去重。从网上爬的数据或者产线采集的数据里经常有完全相同的图像去重之后再划分。用测试集的信息做了数据处理。比如在测试集上做统计、在测试集上选阈值、在测试集上挑模型。我在实战里处理过一个 yolov8 检测项目训练 mAP 0.86、验证 mAP 0.87看起来非常健康但一上现场测试就掉到 0.5 以下。排查到最后发现验证集里大量帧和训练集来自同一段视频差一帧的画面对检测器来说几乎一样验证集根本不能代表真实分布。后来按视频时间段重新划分验证 mAP 掉到 0.62这才看见了真问题。3.3 对付真·过拟合的实用工具箱确认不是泄漏之后如果差距依然很大再上正则化手段按性价比排序加数据增强。这是最有效、副作用最小的手段。随机翻转、旋转、颜色扰动、随机擦除等于免费扩充训练集让模型没法背图像。早停early stopping。监控验证集 loss连续 N 个 epoch 不降就停不要傻傻训练到最后一个 epoch。关键在于监控的是验证集损失而不是训练集损失很多人写反了训到后面验证损失一路走高还在继续那就是在给过拟合送时间。Weight decay 调大一点。从默认的 1e-4 调大到 1e-3 或更高对大模型效果明显。如果用了 AdamWweight decay 是解耦的可以独立调。Dropout / Dropout path。CNN 的全连接层附近、ViT 里的 DropPath 都值得试试。线上用过 vit 做分类的都知道ViT 在小数据上过拟合极快DropPath 是基本配置。标签平滑label smoothing。把 one-hot 标签从 1 和 0 变成 0.9 和 0.1 这样的软标签可以防止模型对训练样本的置信度过高通常能换来一点测试精度。模型简化。前面提到过如果数据本身就几千张那再大的 backbone 都救不了你先换小模型比加任何正则化都划算。要提一句不要一上来就所有正则化手段全都上一遍。每加一个手段就单独训练一次观察它对验证集损失的影响加完效果不升反降就回退。我见过不少人一上来开十几个正则化选项结果训练不仅变慢精度反而不如裸训——正则化本身就是一种在偏差和方差之间做权衡的选择调过了头模型就欠拟合了。4. 那些不讲道理的精度组合——分布偏移评测口径特殊场景4.1 训练精度低、测试精度高是怎么发生的这条反直觉的组合真实出现过。那是一个医学影像分类任务训练集上反复只能跑到 82%测试集上倒是能到 88%。当时第一反应是不可能肯定是测试集太简单了查完之后发现确实如此——但简单的原因踩中了一个更隐蔽的坑。拆分案例训练集里正负样本的比例是 3:1模型把大多数偏难样本都分错了测试集里负样本特别多而模型恰好偏向预测负类于是精度反而高了。这种情况本质是类别分布不一致加上模型决策阈值偏向某一方。解决办法很简单画混淆矩阵看每一类的 precision 和 recall不要只看整体精度如果业务对某类特别关注用 f1-score 或按类别加权的指标来评估。另外还有一种情况测试集划分得太小比如只有 100 张那么 88% 和 82% 之间的差距很可能只是噪声。小测试集的评估结果波动极大一个 epoch 的随机性都可能带来 5 个点的精度变化此时的异常并不代表任何真实的模型状态。4.2 训练精度高、测试精度高的同时现场效果却一塌糊涂这个场景在工业视觉和安防领域特别常见热词里那条机器视觉动了什么会导致像素精度变化就是典型。实验室里训练集和测试集精度都很好一部署到现场就崩。这跟过拟合不同是模型建立的数据分布和真实场景数据分布不一致distribution shift。举个具体例子一个垃圾分类项目训练数据来自北方的分拣线测试集也是同一条线的历史数据精度 95%。换到另一条在南方、光照不同、垃圾桶型号不同的线精度直接掉到 70%。你对照精度曲线会发现训练集和测试集精度的关系在实验室里看起来没有任何问题但它们的底层分布只覆盖了北方线的工况。这种时候排查思路要换不再盯着模型和训练参数而是去量化数据分布的差异。做了三件事收集现场图像和训练集图像放在一起统计亮度、对比度、色彩分布、目标尺寸的差异。用训练好的模型对现场图像做预测按环境/时间段/光源条件分桶展示每桶的精度找出崩塌最厉害的那一类。把现场图像增量加入训练集优先加那些失败样本里置信度最低、混淆矩阵里错得最集中的类型做增量训练。这个思路在 halcon 做工业检测项目里也通用halcon 深度学习工具训练出来的模型在产线测试时精度下降先找相机位置移动、光源亮度变化、产品来料差异这些物理因素再考虑补数据。4.3 指标本身可能一直在骗你分类精度、mAP 还是混淆矩阵最后一种关系异常是评测口径的问题。训练精度和测试精度都高或者一个高一个低可能只是因为精度这个指标不适合当前任务。拿目标检测来说精度不是 accuracy而是 IoU 在某个阈值下的 mAP。模型在训练集上 mAP 0.72、测试集上 mAP 0.43这往往不是过拟合而是检测任务里训练和推理行为天然存在差异——训练时模型用正负样本匹配、多任务损失联合优化推理时需要自己框出区域再做 NMS后处理参数稍微不对mAP 就会大跌。此时你应该去查 NMS 阈值、置信度阈值、anchor 尺寸设定而不是去看模型的正则化。多标签分类里 accuracy 更不靠谱因为每个样本可能有多个标签你把至少一个标签正确算对还是要求所有标签全部正确才算对得到的精度天差地别。建议养成一个习惯每次跑完实验不仅记录精度顺便保存混淆矩阵和典型错误样例。只看精度这个数字你永远说不清训练集和测试集之间的差异到底来自哪里。5. 实操层面如何科学地划分数据集并正确处理精度差异5.1 划分策略进化从随机划分到按组、按时间段划分训练集/验证集/测试集怎么划分直接决定了你看到的精度关系靠不靠谱。标准的随机划分在多数场景下可行但有个前提样本之间必须是独立的。如果样本之间存在时间相关性、空间相关性或者设备相关性随机划分会让验证集偷看到训练信息精度关系全线失真。我在工业检测、遥感图像、视频检测这几个场景里都踩过坑。遥感目标检测用 mmrotate 训练 DOTA 风格数据时图像之间存在大量重叠区域直接随机划分会导致同一栋建筑的不同切片同时出现在训练集和测试集里模型等于见过答案但你没意识到。正确做法是按瓦片来源的图像大图分组同一个大图的所有切片都进同一个集合。给你一个简化的划分代码示例按文件分组再分层抽样import numpy as np from sklearn.model_selection import GroupShuffleSplit # filenames: 所有样本文件名 # labels: 每个样本的类别 # groups: 每个样本所属的分组编号比如同一视频/同一大图/同一批次产品 gss GroupShuffleSplit(n_splits1, test_size0.2, random_state42) train_idx, val_idx next(gss.split(filenames, labels, groups)) train_files filenames[train_idx] val_files filenames[val_idx]分组划分比单纯随机划分多出一步是在精度关系失真与否之间做的一次天壤之别的选择。先划好分组再随机这个顺序不能反。还有一个细节工业场景里经常需要按时间先后切分。用前两周的数据训练预测第三周的数据。这种切分方式下的测试精度往往比随机划分低 10 个点以上。这 10 个点是真实世界会打你的脸的那部分看起来丑但它是诚实的。5.2 验证集是训练的守门员测试集只留下一次终评机会这是训练/测试精度关系里最常被忽视的操作层面原则测试集只能被评估一次。如果你反复用同一个测试集去调超参数、选模型、做早停到最后一刻你会觉得测试精度还不低模型挺靠谱但实际上你已经在测试集上做了隐式过拟合——你选的模型是碰巧在这个测试集上表现好的那个而不是真正泛化最好的那个。正确流程是训练集用于学习参数。验证集用于早停、选超参、对比多个候选模型可以反复使用但心里要清楚每次使用都在向它泄漏信息。测试集只在全部流程结束后跑一次记录最终分数然后尽量不回头。很多深度学习框架的训练脚本只支持 train/val 两个集合于是很多人在验证集上反复调参后就把验证集结果当作最终结果去报告。这在项目汇报里可以糊弄过去但一旦你把它当作测试集精度去指导模型好不好的判断就失真了。我自己的习惯是即使小项目也一定拆出一个 holdout 测试集哪怕数量只有两三百张。这个集合的价值很大它给你一个不被自己反复调参过程污染的干净视角防止自我欺骗。5.3 判断模型是否健康的一套经验标准给你一套我在多个项目里沉淀下来的判断流程可以直接套用第一步对比训练精度与验证精度。差距小于 5 个百分点且两者都高健康差距大于 15 个百分点立即检查泄漏若确认无泄漏再判断是否需要提前停止。第二步对比验证精度与测试精度。两者接近说明你没有在验证集上过拟合整个评估流程是干净的测试远低于验证几乎可以肯定是分布偏移或测试集里存在训练数据没有覆盖的工况。第三步不只盯整体精度拆开看子集。按类别、按分辨率段、按环境条件拆分精度找到掉队最严重的那一块。很多关系异常的谜团拆开子集以后一眼就有答案。第四步跑一次最简回归测试。把训练精度直接和随机猜测精度的倍数关系对比一下例如 100 个类别的分类任务随机精度是 1%如果训练精度只有 5%那你连最简单有效特征都还没学会就别谈什么过拟合和泛化了。6. 一条容易忽略的经验训练集精度真的准备好做对比基准了吗最后说一个很多人没有意识到的问题训练集精度的绝对值并不是固定不变的。同一个模型换一个划分方式、换一个数据增强策略、换一次随机种子训练集精度都可能不同。所以任何用训练集精度与测试集精度做对比得出的结论都必须建立在数据划分一致的前提下。不要拿这次实验的训练集精度去对比上一个项目的测试集精度那没有意义。从另一个角度看训练集精度和测试集精度的关系本质上是信息被泛化和被记忆的比例。如果你的目标是在测试集拿到 90% 以上的精度那么过高的训练集精度往往不是必要件——我见过很多最终交付的模型训练精度只有 92%测试精度 90%它比一个训练精度 99%、测试精度 91% 的模型更健康、更抗造。因为训练精度被压到 92% 说明增强和正则化都很充分模型的决策边界不那么脆。所以下次再看见训练精度 0.98测试精度 0.63的时候先别急着上 L2、dropout把下面三个问题按顺序回答一遍训练和测试的数据划分是按组分的还是随机分的有没有泄漏训练过程的输入管线有没有问题图像宽度、归一化、标签有没有错指标有没有选对分类精度是不是被类别不均衡或者多标签规则带偏了大多数情况下走完这三步你根本不需要调模型结构——精度异常从来都不是模型的问题而是它周围那堆数据流和评测逻辑的问题。这就是我在这类问题上最值钱的一条经验。
返回列表