尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

YOLO训练结果分析:读懂mAP、损失曲线与混淆矩阵

发布时间:2026/9/29 4:53:22

资讯中心
01
ARTICLE

YOLO训练结果分析:读懂mAP、损失曲线与混淆矩阵

YOLO训练结果分析:读懂mAP、损失曲线与混淆矩阵
训练完一个 YOLO 模型终端日志刷完最后一轮runs/detect/train/目录里躺着一堆文件results.csv、results.png、confusion_matrix.png、PR_curve.png还有十几张预测可视化图。绝大多数人这个时候只做一件事——翻到results.csv最后一行看一眼metrics/mAP50(B)0.9 就发朋友圈0.3 就换个模型重跑。但真正把这批文件读进去的人会发现训练结果分析这件事的含金量恰恰在于那些看起来不好看的曲线里。同一个 0.78 的 mAP50可能是一个已经到天花板的模型也可能是一个再改两行配置就能冲到 0.88 的模型区别就在于你会不会读它给出的证据。这篇内容面向的是已经跑通 YOLO 训练流程、能把模型训出结果但拿到结果之后不太确定下一步该动什么的人——不管是 YOLOv5 时代过来的老手还是直接用 YOLOv8、YOLO11 起步的新人读曲线、读混淆矩阵、读预测图的这套方法都是通用的。1. results.csv 不是成绩单而是七条线索的合集1.1 一次训练真正留下的可分析产物很多人把训练输出当成一个黑盒的结果文件实际上它至少包含四类互相印证的证据缺了任何一类你的判断都会偏。第一类是数值趋势也就是results.csv里的逐 epoch 记录它告诉你模型是怎么一步步走到今天的第二类是汇总曲线也就是results.png它把 CSV 里的列画成折线方便你一眼看形状第三类是统计分布包括labels.jpg、labels_correlogram.jpg它反映的是数据集本身的构成第四类是推理快照包括val_batch0_pred.jpg、train_batch0.jpg这些图它是唯一能让你用眼睛直接验证模型行为的东西。我见过太多人跳过第四类。数值指标是聚合后的结果聚合必然抹掉个体信息。一个 mAP50 是 0.85 的模型可能在某个类别上全军覆没只是被其他几个大类的高分平均掉了也可能在小目标上完全瞎只是数据集里小目标占比低所以不影响总分。这类问题只有翻预测图才能发现。产物关注点什么时候必须看results.csv逐轮损失与指标趋势每次训练第一手results.png曲线形状是否健康趋势异常时对照confusion_matrix.png类间错分结构总分尚可但可用性差时PR_curve.png/F1_curve.png阈值取舍依据准备部署、定 conf 时val_batch*_pred.jpg真实错检样本上线前终审labels.jpg数据分布是否畸形指标长期上不去时1.2 看结果的顺序先形状后数值最后看具体样本我自己的固定顺序是这样的先把results.png打开看一遍整体形状这一步花不到三十秒但能筛掉八成的问题然后回到 CSV 用脚本把关键列拉出来算几个我关心的比值最后才去翻混淆矩阵和预测图验证数值上的猜测是否成立。为什么先看形状因为绝对值是没有参照系的。你看到一个 mAP50-95 是 0.41这个数本身说明不了任何问题——它可能是收敛了但天花板就低也可能是训到一半被打断了。但如果你看到曲线在第二个 epoch 冲到 0.6然后掉到 0.2 再也没起来这个形状本身就是明确信号要么是预训练权重没加载成功要么是学习率设置有问题导致早期发散。这一步可以用一小段脚本快速完成省掉手动翻表格的麻烦import pandas as pd import numpy as np df pd.read_csv(runs/detect/train/results.csv) df.columns [c.strip() for c in df.columns] # 关键列位置在不同版本里名字略有差异先做一次兜底匹配 def col(keyword): hit [c for c in df.columns if keyword in c] return hit[0] if hit else None map50 col(mAP_0.5) map5095 col(mAP_0.5:0.95) prec col(precision) rec col(recall) epochs len(df) print(总轮数:, epochs) print(最佳 mAP50:, df[map50].max(), 出现在第, df[map50].idxmax() 1, 轮) print(最佳 mAP50-95:, df[map5095].max()) # 指标落差判断框的紧致程度 gap (df[map50] - df[map5095]).mean() print(mAP50 与 mAP50-95 平均落差:, round(gap, 4)) # 末段是否还在涨判断有没有欠训练 tail df[map50].tail(max(5, epochs // 10)) print(末段均值:, round(tail.mean(), 4), 整体均值:, round(df[map50].mean(), 4))这段脚本里我最在意的其实不是最大值而是最佳值出现在第几轮。如果最佳 mAP50 出现在倒数第三轮说明模型还在涨你极有可能是欠训练加轮次大概率还能提升如果最佳值出现在中段、之后一路微跌那就是典型的过拟合拐点继续加轮次只会更糟。这个信息比那个 0.9 或 0.3 有用得多。提示不同版本的列名不完全一致YOLOv5 记录的是train/obj_lossYOLOv8 之后换成了train/dfl_loss写脚本时用关键字匹配而不是硬编码列名能避免换版本就报错。2. box、cls、dfl 三条损失曲线各自在说什么2.1 回归损失长期不降问题多半不在模型而在框box_loss有的版本叫train/box_loss负责的是边界框回归也就是框得准不准。正常训练里它应该在前两三个 epoch 快速下降之后进入缓慢下降的平滑曲线末段趋于平缓。如果你的 box_loss 从第一轮到最后一轮几乎是一条水平线甚至还在 1.0 以上先别怀疑网络结构按下面这个顺序查。第一看标签格式。YOLO 要求的是归一化的class cx cy w h全部在 0 到 1 之间。用 LabelImg 之类的工具导出时如果选了 Pascal VOC 格式再手工转很容易出现坐标没归一化、或者 xmin/ymin 顺序写反的情况。这类问题的表现就是 box_loss 降不下去但也不爆炸卡在一个尴尬的中间值。第二看标注框的尺度。如果你数据集里绝大多数目标都是几十像素的小目标而输入尺寸还是默认的 640那么经过五次下采样之后特征图上目标只剩几个像素回归难度天然极高。这时候 box_loss 高是合理的解决方案是把imgsz提到 1024 甚至 1280而不是去改损失函数。第三看是否存在大量贴边目标。目标被图像边界截断时真实框会超出图像范围而 YOLO 在计算损失时会把超出的部分裁掉导致模型永远学不会预测一个完整但不该完整的框损失卡住。这类样本建议在数据清洗阶段直接丢弃或用 mosaic 增强策略部分掩盖。2.2 分类损失震荡比不降更常见原因往往在采样策略cls_loss的形态和 box_loss 完全不同它的绝对值通常更小而且震荡幅度更大这是正常的因为分类损失直接受 batch 内类别构成影响——一个 batch 全是难例损失就高下一个 batch 全是简单样本损失就低。所以看 cls_loss 要看移动平均后的趋势而不是逐点的波动。真正需要警惕的是两种情况。一种是 cls_loss 从头到尾都在高位来回锯齿完全没有下行趋势这通常意味着类别标签本身有问题要么是同一类目标在不同图片里被标成了不同类别要么是两个高度相似的类别被混在一起。后者在工业检测里特别常见比如把划痕和裂纹混标模型永远学不明白因为标注本身就自相矛盾。另一种是 cls_loss 降到很低但 mAP 不涨。这说明模型在训练集上把分类学明白了但泛化不出去问题出在数据分布而不是模型能力。这时候需要做的是增加数据多样性、检查训练集和验证集是否来自同一批采集条件而不是继续调学习率。2.3 不同代际的损失构成差异别用老经验套新模型这是很多从 YOLOv5 迁移到 YOLOv8 的人容易踩的坑。两代模型的损失项根本不是一个东西用 v5 的经验去判断 v8 的曲线结论会完全反过来。下面是常见的对应关系代际记录的主要损失项特点YOLOv5 / v7box_loss、obj_loss、cls_loss有独立的 objectness 分支锚框机制YOLOv8 / YOLO11 及后续box_loss、cls_loss、dfl_loss无 objectnessDFL 负责分布化回归关键差异在obj_loss和dfl_loss上。v5 的obj_loss负责判断这个位置有没有目标数值通常明显高于 cls_loss而 v8 之后取消了这个分支置信度直接由分类分支的最大值承担多出来的dfl_loss是用分布的形式去建模边界距离让框的回归更精细。所以当你看到 v8 的dfl_loss前几轮很高、后面缓慢下降这是正常节奏但如果它一直不降对应的现象就是 mAP50-95 上不去——框能大致框住但边缘永远不够贴合。3. mAP50 与 mAP50-95 的落差在暴露什么3.1 两个指标的口径差别决定了它们的分工mAP50是 IoU 阈值取 0.5 时的平均精度意思是框和真值重叠一半就算对mAP50-95是 IoU 从 0.5 到 0.95 每隔 0.05 取一次十个阈值下的平均值。这两个数放在一起看的价值远高于单独看任何一个。如果 mAP50 是 0.90 而 mAP50-95 只有 0.45落差 0.45这说明模型找目标的能力没问题但对框的边缘拟合很差。这类模型在只需要知道大概位置的场景里够用比如计数、区域告警但一旦下游需要精确裁剪比如 OCR 前置检测、尺寸测量就会暴露短板。反过来如果两个数都很低且差距不大比如 0.42 和 0.33说明模型连目标在哪都没学好这时候讨论框的精度没有意义应该回头查数据量和训练轮次。3.2 落差偏大时我固定按这个顺序排查第一步看labels.jpg的框尺寸分布。如果框普遍很小那这个落差大概率是分辨率造成的优先调imgsz。第二步看是否用了mosaic和mixup。这两个增强在训练前期非常有用能让模型见到大量不同尺度和位置的组合但它们的副作用是把目标尺度分布搅乱了尾部几个 epoch 如果继续开着回归精度会被持续拉低。标准的做法是在最后 10 到 15 个 epoch 关掉它们。# 训练配置里的关键几行 imgsz: 1024 # 小目标场景不要用 640 硬扛 close_mosaic: 15 # 最后 15 轮关闭 mosaic让回归收敛 box: 7.5 # 回归损失权重小目标场景可以适当提高 dfl: 1.5 # DFL 权重影响框的精细度第三步看 DFL 相关设置。YOLOv8 之后的 DFL 分支默认的回归范围是 16意味着它主要建模 16 像素以内的距离偏移。如果你的目标框尺度普遍在几百像素以上这个默认值会让回归受限需要在配置里调整reg_max并重设检测头的通道数。第四步看是否真的收敛了。把 mAP50-95 单独画一条曲线如果它在最后几轮还在缓慢爬升那就是欠训练别急着改结构。3.3 小目标指标塌陷的典型表现与切入点小目标的问题在结果分析阶段有个非常清晰的特征整体 mAP 看起来还行但只要你按目标面积分箱统计会发现在面积小于 32×32 的箱子里 AP 断崖式下跌。而results.png上看不出任何异常因为它是所有尺度混在一起的聚合值。要定位这个问题需要在验证阶段单独统计。YOLO 官方验证脚本本身不直接给出分尺度结果通常的做法是拿val阶段的预测结果可以用save_jsonTrue导出自己按框面积分箱算 AP。这个分析一旦做出来结论往往很直接小目标 AP 0.15中大目标 AP 0.85那你要做的事情就很明确了——加 P3 检测层、提高输入分辨率、或者在数据里对小目标做专门的过采样。这里有个经验数值可以参考当目标在原始图像上只有 20 像素左右时输入 640 意味着在最小的特征图上stride 8 对应 80×80 的特征图目标占约 2.5 个格子输入 1280 时占 5 个格子。后者对回归任务来说难度是断崖式下降的。所以在显存允许的前提下小目标场景提高分辨率带来的收益通常大于换更大 backbone 带来的收益。4. PR 曲线、F1 曲线与混淆矩阵把数字翻译成具体错误4.1 从 PR 曲线的形状判断模型处在什么状态PR_curve.png里每条彩线对应一个类别曲线下的面积就是该类别的 AP。判断方法很朴素曲线越靠近右上角越好。但更值得看的是形状差异。如果某个类别的曲线是接近直角的形状也就是 recall 很低的时候 precision 还能保持在 0.9 以上然后突然垂直掉落说明这个类别的召回能力受限——模型只在最确信的那几个样本上敢报其他全漏了。常见原因是该类别样本太少或者标注质量参差。如果曲线是一条对角线似的缓坡precision 随着 recall 提升平滑下降说明模型对该类别的判断本身就模糊很可能与另一个类别存在视觉相似性。还有一种情况是曲线整体位置很低但形状正常那基本就是数据量不够没什么好办法只能补数据。4.2 混淆矩阵里能读出的两类错误confusion_matrix.png是行归一化前的原始计数confusion_matrix_normalized.png是行归一化后的比例。这两张图要配合着看前者告诉你绝对数量后者告诉你错误结构。矩阵里有两个区域必须重点看。右下角之外的对角线格子是类间错分比如真实类别是 A 被预测成 B。这类错误的数值如果在 10% 以上就不是模型能力问题而是标注一致性问题。我遇到过一个案例两个类别在视觉上区别只是颜色深浅标注团队内部对边界判断都不统一最后的结果就是这两个类永远互相错分。解决办法是重新定义类别边界把这两个类合并或者干脆拆成两个独立模型分别训练。最后一行和最后一列对应的是漏检和误检。YOLOv8 的混淆矩阵里有一个额外的background行列表示没有目标被判成有目标和有目标被判成没有目标。如果 background 那一列真实有目标但被判成背景数值很大说明漏检严重通常对应小目标或者遮挡目标。对比矩阵的时候有个技巧不要只看出错最多的那一格要看归一化之后错误比例最高的那一格。举个例子A 类错分成 B 类共 200 次听起来很多但如果 A 类本身有 20000 个样本那错误率只有 1%可以忽略而 C 类总共只有 300 个样本错了 50 次错误率 16%这才是真正的问题所在。样本量小的类别永远是需要单独盯的。4.3 用 val_batch 预测图做人工终审数值和矩阵都看完之后我一定会花时间翻val_batch*_pred.jpg。这些图是随机抽的每张图上有真值框和预测框的叠加对比是唯一能看到模型具体错在哪张图上、错成什么样的途径。翻图的时候我会特别留意三类现象。一是密集目标下的漏检数值上可能只影响几个点但如果你的业务场景就是密集人群或者密集零件这个问题会直接决定模型能不能用。二是置信度虚高也就是框明显错了但标着 0.85 的分数这说明模型对自己的错误很自信这类问题在模型集成和阈值调节时最难处理。三是框的抖动同一类目标在不同图里框的大小习惯不一致有的贴得很紧有的很松这是回归不稳的表现通常和 DFL 设置或者训练轮次不足有关。提示验证可视化图默认只保存若干张可以在训练命令里加上plotsTrue并调大保存数量训练结束后再慢慢翻比训完就删掉重新跑一次划算得多。5. 四类典型异常曲线的完整排查链路5.1 前期虚高然后断崖下跌这个现象我见过太多次形态非常统一第 1 到第 3 个 epochmAP50 能冲到 0.5 甚至更高然后突然掉到 0.05 并且再也爬不起来。新手看到这个会以为模型崩了其实早期那几轮的高分是假象来源通常是两类。第一类是预训练权重加载失败。如果--weights路径写错或者权重和模型结构不匹配YOLO 会用随机初始化继续跑但日志里往往只报一行 warning很容易被忽略。早期的高分其实来自模型输出的极端偏置——比如所有框都预测图像中心而恰好数据集中大部分目标位于中心区域于是瞎猜也能拿到不低的 IoU。随着训练推进模型开始真正学分布这些巧合失效分数自然暴跌。第二类是学习率过大导致的发散。如果lr0设成了 0.1 这种级别前几轮梯度更新幅度太大会把权重推到极端值表现为损失先是剧烈下降然后突然爆炸成 NaN指标断崖。排查方法是看 box_loss 有没有出现 NaN 或者突然跳到几百。排查链路是这样的先确认权重真的加载了日志里通常会打印加载了多少层、跳过了多少层再确认lr0是否在合理范围从零训练和微调的推荐值差别很大最后看是否用了 AMP 混合精度某些显卡和某些版本组合下 AMP 会溢出关掉amp再跑一次就能验证。5.2 全程锯齿震荡不收敛曲线上下大幅波动、平均值几乎不动这种形态的根因通常在 batch 和数据量上。batch size 太小的时候每个 batch 的梯度估计噪声极大模型像是在被随机拉扯。经验上如果 batch 小于 8训练稳定性会明显变差。但在显存受限的情况下正确做法不是硬把 batch 调大而是用梯度累积把batch4配合nbs64这类设置让优化器在多个小 batch 上累积梯度后再更新一次等效于大 batch 的效果代价只是训练时间变长。另一个容易被忽略的原因是数据量太少。如果整个训练集只有几百张图那无论怎么调曲线都会抖因为每个 epoch 的样本组合差异太大。这种情况应该做的是强增强拉长训练并且坦然接受抖动用移动平均看趋势而不是看单点。5.3 训练损失下降、验证损失上升的过拟合这是最经典也最好识别的一种。train/box_loss一路平滑下降val/box_loss在某个 epoch 之后开始掉头向上同时验证集的 mAP 停止增长甚至微跌。三条曲线同时出现这个特征就是明确的过拟合信号。处理方式按性价比排序第一加数据或者加强增强这是收益最高也最费力的第二加正则weight_decay从默认值往上调或者开dropout分类头部分第三减模型容量从 l 换到 s或者从 s 换到 n第四早停把patience设小一点让训练在拐点附近自动停。有个细节值得说过拟合的拐点位置和数据集大小强相关。数据量小的时候可能在 50 轮就出现数据量大的时候 300 轮都不出现。所以别拿别人的轮次数当参照看自己的验证曲线。5.4 假性收敛指标好看但框飘这一种最隐蔽因为它在数值上几乎看不到异常。mAP50 稳定在 0.9损失曲线平滑收敛看起来完美。但你把预测图翻出来一看框普遍比真值大一圈或者系统性地往某个方向偏移。根源通常有两个。一是验证集和训练集同源模型记住的是这一批数据的统计规律换一批就崩。二是框的回归目标尺度失衡DFL 分支对回归范围的建模和实际目标尺度不匹配导致框整体往外扩。排查方法很直接拿模型去跑一批完全没参与训练、采集条件也不同的图片看指标掉多少。如果掉幅超过 20%那假性收敛的判断就成立了。这类问题的解法不是调参而是重新审视数据划分方式尽量让训练集和验证集在采集时间、设备、场景上都有区分度而不是简单随机切分。6. 验证集好看、现场拉胯时该回头查什么6.1 对标注质量做一次反向抽查当指标和实际表现严重不符时我第一件事不是怀疑模型而是抽 50 张验证集图片把标注框和图像对照着看一遍。这个动作非常枯燥但有效。常见的标注问题包括框只框住了目标的一部分、多个紧邻目标被标成一个框、相似类别的标注标准不统一、遮挡严重的样本没标但也没剔除。有个快速判断标注一致性的办法计算同一类别所有标注框的宽高比分布。如果某个类别的宽高比标准差异常大说明标注团队对这个类别的框该多大没有共识。人眼很难发现问题统计量能。6.2 训练验证分布有没有隐性错位随机切分数据集在实验阶段没问题但在真实项目里往往有坑。如果你的数据是按批次采集的随机切分会让训练集和验证集共享同一批采集条件指标虚高。正确做法是按采集批次、按日期、按设备分组切分让验证集代表未来的数据。判断方法也简单把验证集按来源分组分别算每组的 AP。如果组间差异超过 30%说明分布严重不均这时候看总 mAP 意义不大。6.3 推理配置和训练配置对不上的几个高频项这类问题纯粹是工程细节但踩的人极多。输入尺寸不一致是最常见的训练用 640部署时图省事直接送 1920 的原图进去模型没见过这个尺度分布效果直接打折。置信度阈值不一致也很常见训练时验证用的conf0.001为了算完整的 PR 曲线这个值是故意设低的部署时照抄了这个值结果输出一堆垃圾框。NMS 的 IoU 阈值同理验证阶段用的是 0.7部署时用默认值密集目标场景下会大量误抑制。最稳妥的做法是把验证阶段确定下来的那套参数——imgsz、conf、iou、max_det——原封不动搬到部署配置里定完再微调而不是凭感觉设。7. 把分析结论落回下一轮训练7.1 从现象到参数的映射表分析做完如果不落到具体动作上等于没做。我把常见的现象和对应操作整理成了一张表基本上按这张表走能解决八成的结果问题。观察到的现象优先尝试的动作预期效果最佳指标出现在倒数几轮增加训练轮数指标继续上涨验证损失中段掉头向上早停、加正则、加数据抑制过拟合mAP50 高、mAP50-95 低提分辨率、末段关 mosaic、调 DFL框更贴合小目标 AP 断崖提 imgsz、加 P3 层、小目标过采样小目标召回提升cls_loss 长期高位震荡复查标签一致性、合并混淆类分类收敛box_loss 不降检查标签格式、剔除贴边样本回归启动某类 AP 明显偏低补该类样本、检查标注标准长尾改善全线锯齿梯度累积、增大等效 batch曲线平滑7.2 单变量原则和一份够用的实验台账最后说一个习惯问题。我见过很多人调模型的方式是这轮改学习率、改增强、换 backbone一起上然后指标涨了就不知道是谁的功劳跌了更不知道是谁的锅。正确做法是一次只改一个变量哪怕这样一轮实验要多跑几个小时。配套的是一份实验台账字段不用多够用就行实验编号 | 数据版本 | 模型 | imgsz | batch | lr0 | 增强策略 | 最佳mAP50 | 最佳轮次 | 结论 E021 | v3 | s | 640 | 16 | 0.01| 默认 | 0.812 | 87 | 基线 E022 | v3 | s | 1024 | 8 | 0.01| 默认 | 0.856 | 92 | 分辨率是正向因素 E023 | v3 | s | 1024 | 8 | 0.01| 关mosaic | 0.871 | 95 | 末段关增强收益明显有了这张表你在第 30 次实验的时候还能清楚地知道哪条路走过、效果如何而不是凭记忆猜。台账里最佳轮次这一列看起来不起眼但它能告诉你模型还需要多少训练量长期看比 mAP 本身更有指导价值。我自己在长期跑实验之后最大的体会是结果分析这件事的瓶颈从来不在工具上而在耐心上。大多数人不是不会看曲线而是不愿意花二十分钟去翻预测图、对标注、算分尺度指标。可恰恰是这二十分钟决定了你下一轮训练是瞎调还是有据可依。另外一个实用的习惯是每次训练结束后不要立刻删掉 run 目录哪怕显存紧张也把results.csv和几张关键图存下来。等到三个月后你要复现某个结果这些文件就是唯一的线索从零再跑一遍的成本要高得多。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。