尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

YOLOv8检测、分割与姿态估计:原理、训练与部署

YOLOv8检测、分割与姿态估计:原理、训练与部署 前言YOLOv8 这套东西我用了一年多从最早拿它跑路口车流量统计到后面做小目标检测、实例分割、人体关节点估计前后踩的坑不算少。很多人第一次接触 YOLOv8脑子里只有一个模糊印象一个又快又准的检测框架装好环境、下个权重、跑一行yolo predict就完事。但真要做项目——尤其是本科毕业设计、工业质检、安防园区这类需要改结构、换数据集、上板子的场景——只会在命令行里敲命令是远远不够的。问题的根子在于YOLOv8 把三个任务塞进了同一套代码框架里目标检测detect、实例分割segment、关节点估计pose。它们共享同一个骨干网络、同一个特征融合结构只在检测头和解码方式上分叉。如果你不理解这个共用骨架 分叉头的设计逻辑就会陷入一种很憋屈的状态模型能跑但一改就崩一看输出张量就懵一调参数就掉点。比如分割任务的输出为什么是4nc32个通道那 32 是什么关节点为什么每个点输出三个值而不是两个这些数值背后都是有明确工程含义的。这篇文章我想做的事情很直接把 YOLOv8 在目标检测、实例分割、关节点估计三个任务上的原理掰开揉碎讲清楚从骨干结构、颈部融合、解耦头设计到正负样本分配、损失函数构成、输出张量的解码流程再到环境配置、训练参数、导出部署这些实操环节。适合两类人看一类是刚入门 YOLOv8、想知道它到底怎么工作的的同学另一类是做了一段时间、想改进结构或者上嵌入式设备比如 RK3588 这类带 NPU 的板子、需要搞清楚每个张量含义的工程师。我尽量不写成教科书按自己实际调试的顺序来讲中间会穿插不少我试过实测下来这地方我踩过坑之类的经验也会给出能直接抄的参数和命令。有些细节比如某些超参的默认值、某些层的具体实现不同版本会有微调我会在讲到的时候说明以常见实现为准你对着自己的版本核一下就行。1. 三任务共用一套骨架YOLOv8 的整体架构设计思路1.1 从 YOLOv5 到 YOLOv8结构上到底改了哪几处要理解 YOLOv8最快的路径是拿 YOLOv5 做对照。两者血缘关系很近但 YOLOv8 在几个关键位置动了刀这些改动恰恰决定了它后面能统一支撑三个任务。第一处是骨干里的模块替换。YOLOv5 用的是 C3 模块YOLOv8 换成了C2f。名字里的 f 是 faster 的意思核心思路借了 ELAN 的设计输入特征先做一次 1×1 卷积压缩通道然后 split 成两路一路直接往后传另一路反复经过多个 bottleneck 再拼接回去。这样做的好处是梯度路径更多、跨层信息保留得更充分同时参数量还压得住。实测下来同样深度下 C2f 比 C3 的梯度流更顺训练早期 loss 下降明显更稳不太容易在前几个 epoch 出现那种抖一下又弹回去的情况。第二处是把检测头从耦合改成解耦。YOLOv5 的检测头里分类和回归是共用一部分中间特征的YOLOv8 改成了两条完全独立的卷积分支分类走分类的回归走回归的。这个改动在检测任务上提升明显因为分类关心的是这是什么回归关心的是边界在哪两者的特征偏好本来就不一样硬绑在一起会互相牵制。第三处是彻底转向Anchor-Free。YOLOv5 还是基于 anchor 的你需要根据数据集统计出一组先验框尺寸重新聚类YOLOv8 直接去掉了 anchor每个特征点直接预测它到目标四条边的距离。对我这种经常换数据集的人来说这一步省了太多事——以前换个鸟类数据集、换个水果数据集第一件事就是跑 k-means 重新聚 anchor聚得不好 mAP 直接掉几个点。现在不用管了。第四处是回归方式换成了DFLDistribution Focal Loss。传统做法是直接回归一个连续的距离值YOLOv8 改成预测一个离散分布再求期望。这个我放到后面检测损失那节详细说先记住结论它让边界回归在小目标和遮挡目标上更稳。整体骨架可以概括成三段Backbone骨干提特征→ Neck颈部做多尺度融合→ Head头部出结果。Backbone 在 640 输入下依次输出 1/8、1/16、1/32 三个尺度的特征图也就是 80×80、40×40、20×20。Neck 用的是 PAN-FPN 结构自顶向下传一次语义、自底向上传一次定位两轮下来让每个尺度都同时具备大目标的语义信息和小目标的细节信息。Head 就挂在这三个尺度上各出一组预测。提示如果你看网络结构图看得头晕一个简单的判断方法——数一下输出特征图的分辨率是 80、40、20 还是别的。只要记住 640 输入对应 80/40/20其他尺寸按比例推就行比如 1280 输入就是 160/80/40。1.2 三个任务的分叉点头部输出的通道数差异三个任务共用同一套 Backbone 和 Neck真正的差异全部集中在 Head 输出的通道数上。这一点非常关键也是我见过最多人卡住的地方——明明加载的是同一个.pt权重为什么有的输出 84 通道有的输出 116 通道有的输出 56 通道答案就在下面这张表里。以 640 输入、COCO 80 类、三个尺度共 8400 个特征点为例任务输出张量形状通道构成额外输出目标检测[B, 4nc, 8400]4 个框偏移 nc 个类别分数无实例分割[B, 4nc32, 8400]4 nc 32 个 mask 系数proto 原型 [B, 32, 160, 160]关节点估计[B, 413k, 8400]4 1 个人类别 3k 个关键点分量无8400 这个数字怎么来的80×80 40×40 20×20 6400 1600 400 8400。Anchor-Free 之后每个特征点就是一个预测位置所以总数就是三个尺度特征图的像素数之和。目标检测的 4 是(l, t, r, b)即该点到目标左、上、右、下四条边的距离单位是特征图像素解码时要乘回 stride。nc 就是类别数COCO 是 80你自己的数据集是几类就是几。实例分割多出来的 32 是mask 系数它本身不是掩码而是用来和 proto 原型做线性组合的权重。这也是为什么分割模型必须额外输出一个 proto——真正的高分辨率掩码信息存在 proto 里32 个系数只是怎么组合的配方。关节点估计里41 中的 1 是是不是人的置信度3k 是 k 个关键点每个点的 3 个值分别是x、y和可见性置信度visibility。COCO 人体是 17 个点所以是 51 个通道。很多人第一次看到3k会疑惑为什么不是 2k因为关键点存在被遮挡但位置可推断和完全不在画面里的区别多出来的一维就是用来表达这种状态的。注意关节点估计的 YOLOv8 官方实现只做单类person也就是说nc1。如果你想做多类姿态比如同时估人手和动物骨架得自己改头部的类别分支和数据集组织方式这不是改个 yaml 就能解决的。2. 目标检测分支从特征图到边界框的完整链路2.1 解耦头与 Anchor-Free 的取舍逻辑先说解耦头为什么值得单独拿出来讲。在耦合头里分类和回归共享同一个卷积塔最后再分叉出两个 1×1 卷积输出。问题在于分类任务希望特征对语义类别敏感回归任务希望特征对空间边界敏感两者的最优特征子空间并不重合。共享卷积塔相当于让两个目标妥协谁都拿不到最优解。YOLOv8 的做法是让两条分支各走各的每个尺度上分别有独立的卷积序列中间不共享参数。代价是参数量和计算量略微上升收益是精度稳定提升。我在自己的水果检测数据集上对比过同样的数据、同样的训练轮数解耦头比耦合头 mAP50 高大约 1.5 到 2 个点小目标单个果子在图中占比很小上的提升更明显。Anchor-Free 的取舍则更偏工程实用。基于 anchor 的方法有个天然优势先验框提供了尺度上的归纳偏置在网络容量有限时能加速收敛。但它要求你为每个数据集重新调 anchor泛化到新场景时容易失配。Anchor-Free 直接预测距离省掉了这部分调参成本代价是训练初期收敛稍慢需要更充分的 warmup。这里有个实操细节Anchor-Free 预测的四个距离是不加约束的实数理论上可以是负数或超大值。所以训练时正样本分配的质量就直接决定了回归质量——这也是为什么 TaskAlignedAssigner 这套分配策略在 YOLOv8 里地位这么重。分配错了网络就会去拟合一个根本不对应的目标loss 再怎么降mAP 也上不去。2.2 TaskAlignedAssigner正样本凭什么分给它正负样本分配是目标检测里最容易被忽视、但影响最大的一环。简单说一幅图里 8400 个预测点绝大多数是背景负样本只有少数几个应该负责预测真实目标正样本。分配策略决定了哪些点算正样本、每个正样本对应哪个 GT。YOLOv8 用的是TaskAlignedAssigner核心思想是一个预测点够不够格当正样本要看它分类得分和定位精度的综合表现而不是像早期方法那样只看 IoU 或者只看中心点落在哪个格子。具体流程分四步。第一步先算每个预测点和每个 GT 的对齐度量align_metric cls_score^alpha × iou^beta这里cls_score是该点对 GT 类别的预测分数iou是预测框和 GT 框的 IoU。alpha默认 0.5beta默认 6.0。beta 远大于 alpha说明这套策略更看重定位质量——IoU 高的点优先级更高。这个数值选择是有讲究的如果 alpha 太大会让分类分高的点占便宜但那些点可能定位很差回归学出来就是歪的。第二步对每个 GT找出中心点落在它内部的候选预测点这叫中心先验筛选能砍掉大量无关候选。第三步在这些候选里按 align_metric 排序取topk10个作为正样本。第四步算这些正样本的软标签用加权后的 IoU 作为回归目标的置信度权重。我实测下来topk 这个值调到 13 左右在某些密集小目标场景上会有一点提升但稳定性下降容易在目标密集区域出现重复检测。要动这个参数建议先跑通 baseline再单独做消融别一上来就改。提示TaskAlignedAssigner的 alpha、beta、topk 在 ultralytics 里可以通过自定义 loss 或者改源码调整。但改之前想清楚——你是在解决漏检多还是误检多漏检多可以把 topk 提一点误检多应该优先查数据集标注质量那才是根因。2.3 损失函数三件套BCE CIoU DFL 怎么配合YOLOv8 的总损失是三项加权求和total_loss box_gain × box_loss cls_gain × cls_loss dfl_gain × dfl_loss默认权重是box7.5、cls0.5、dfl1.5。这三个数不是拍脑袋来的box 权重最高说明定位是主要矛盾cls 权重低是因为分类相对好学。分类损失用的是 BCE二值交叉熵每个类别独立判断。注意这里不是 softmax而是对每个类做 sigmoid 后算 BCE。这样做是为了兼容一个目标属于多个类别的情形在某些重叠类别场景比如人和骑手下更有优势。边界损失用的是 CIoU。相比普通 IoUCIoU 额外考虑了中心点距离和长宽比一致性。公式大致是CIoU IoU - ρ²(b, b_gt)/c² - αv其中ρ²是两个框中心的欧氏距离平方c是最小外接矩形的对角线长度v衡量长宽比的一致性。我理解它的方式很直白光看重叠面积不够还得看中心对不对得上、形状像不像。对于细长目标比如电线杆、车道线这种长宽比悬殊的物体CIoU 比普通 IoU 收敛快很多。DFL 损失是 YOLOv8 比较有特色的地方。传统回归直接输出一个数DFL 把到某条边的距离离散成reg_max16个区间网络输出 16 个 logitssoftmax 之后求期望距离 Σ (i × softmax(logit_i))为什么要这么绕因为直接回归一个连续值对边界模糊、遮挡的目标容易学出一个平均值结果框不大不小、定位不准。改成分布之后网络可以表达我倾向于认为距离在 12 到 14 之间这种不确定性训练更稳定对遮挡目标尤其友好。代价是回归分支的通道数变成4 × reg_max 64比直接 4 通道重不少这也是为什么 YOLOv8 的 head 部分计算量不小。三项损失的平衡我很在意。有次做泥石流、滑坡这类自然灾种检测目标形状极不规则、边界模糊我把 box 权重从 7.5 提到 9.0同时把 dfl 从 1.5 降到 1.2mAP50-95 提了不到一个点但训练稳定性明显变好波动小了。这种微调没有标准答案要结合数据特点试。2.4 输出解码从 8400 个预测到最终结果训练完或者加载权重做推理时输出的原始张量是[B, 4nc, 8400]必须经过解码才能变成人能看懂的框。解码大概是这么几步。第一步拆分。把通道维切成[B, 4, 8400]的框偏移和[B, nc, 8400]的类别分数。第二步反 sigmoid如果需要。类别分数本身经过 sigmoid直接用就行但有些导出格式比如 ONNX会把 sigmoid 融合进去要确认一下。第三步找候选。对每个特征点取类别分数最大值如果超过conf_thres默认 0.25就进入候选。第四步解码框坐标。将预测点特征图上的整数坐标加上预测的偏移量得到框的中心(x, y)再用偏移量的(l, t, r, b)算出左上右下。这一步要注意缩放三个尺度的 stride 分别是 8、16、32解码后要乘回去。第五步NMS。候选框之间做非极大值抑制iou_thres默认 0.7去掉重复框。第六步把框从 640×640 的输入尺度映射回原图尺度同时做边界裁剪防止超出图像范围。注意导出 ONNX 时YOLOv8 默认会把一些后处理解码 NMS也打包进去形成端到端模型。好处是部署端省事坏处是灵活性差改个阈值都得重新导出。我一般导出两版一版带后处理给嵌入式用一版纯输出给需要自己写后处理的场景。3. 实例分割分支32 个系数 proto 原型的组合游戏3.1 Proto 模块与 mask 系数是怎么配合的实例分割任务的输出多了一个 32 通道的 proto 张量形状是[B, 32, 160, 160]。160 640/4也就是说 proto 的分辨率是输入的 1/4。为什么不直接输出原图分辨率的掩码因为那样计算量和显存占用都无法接受——如果每个实例都要输出一张 640×640 的掩码几十个实例乘起来显存直接爆掉。所以 YOLOv8 采用了原型 系数的分解思路这是受 Mask R-CNN 之后的很多工作启发。proto 是素材库包含 32 个通道的通用掩码特征这些特征对不同目标共享每个检测实例只需要额外预测 32 个系数用这 32 个系数对 proto 的 32 个通道做加权求和就得到属于它的掩码。具体计算流程是这样取某个实例的 32 个 mask 系数形状[32]。与 proto 做矩阵乘法[32] × [32, 160×160] → [160×160]。对结果做 sigmoid得到 0 到 1 之间的掩码概率图。用该实例的检测框裁剪掩码框外的区域全部置 0。上采样回原图分辨率得到最终的二值掩码。我第一次看这个设计的时候觉得挺妙把共性和个性分开了。proto 学的是这一片区域像不像目标轮廓这种通用知识系数学的是这个具体实例对应哪些通道。好处是共享计算、参数利用率高坏处是掩码精度上限受 proto 分辨率限制。因为 proto 只有 160×160对于原图里很小的目标掩码边缘会比较糙。提示如果你做的是精细边缘分割比如工业零件缺陷边界160×160 的 proto 可能不够。我看到过的改进思路包括把 proto 分辨率提到 1/2、加边缘感知分支、或者把 proto 换成可变形卷积但这些都涉及改 head 结构改动量不小而且要重新训练。3.2 分割数据集的组织方式与标注要点分割的数据集格式和检测差别很大。检测的标签是class cx cy w h都是归一化到 0 到 1 的值即class x_center y_center width height分割则需要多边形点集class x1 y1 x2 y2 x3 y3 ... xn yn每一行是一个实例后面跟着一串归一化的多边形顶点坐标按顺序连起来构成轮廓。注意这里必须是闭合多边形而且顶点的顺序要沿着边界顺序走不能乱序。实际标注中我总结了几条经验都是踩过坑才明白的。第一顶点数量要够但不能太密。太少轮廓会失真比如圆形标注成三角形太多会让文件体积暴涨、训练变慢。一般 30 到 100 个顶点能覆盖大多数形状。如果是特别复杂的分叉轮廓可以用多个多边形描述同一个实例但要注意不同工具的支持情况。第二标注必须自交要避免。如果多边形自己缠绕掩码生成的时候会出问题可能出现部分区域被填充、部分区域空白的情况。标注时尽量顺着边界描别来回穿插。第三孔洞是老大难。YOLO 格式原生不支持带孔的多边形比如甜甜圈一般的处理方式是拆分或者简化把中间的孔当作另一个类别标注。这个局限要有心理准备。第四类别平衡对小目标分割尤其重要。我做过一个场景画面里主体目标很大、远处小目标很小结果模型几乎完全忽视小目标。解决方式是 Oversample 小目标样本或者提高输入分辨率。数据格式转换这块一般是从标注工具labelme、CVAT、Roboflow 等导出的 json/xml 转成 YOLO 的 txt。转换脚本的核心就是把多边形坐标归一化、按类别分组。转换完一定要可视化验证——我见过太多跑通了但标注全错位的情况原因是归一化用错了尺寸用了缩放后的尺寸而不是原图尺寸。3.3 分割任务的损失与评估指标分割任务的损失是在检测三项之外再加一项掩码损失。掩码损失用的是 BCE把预测掩码和 GT 掩码逐像素比算二值交叉熵。注意这里只对正样本也就是被分配为某个实例的预测计算掩码损失背景不参与。权重方面默认是box7.5, cls0.5, dfl1.5掩码损失的权重通常是 1 左右具体看版本。我实测过把掩码权重提到 2.0边缘细节会好一些但训练更容易过拟合小数据集。评估指标上检测用的是 mAP50 / mAP50-95分割额外有mask mAP50。这两个要分开看。我遇到过检测 mAP 挺高、mask mAP 很低的情况原因是框的位置大致对但轮廓完全不对——通常是标注质量差导致的。这时候应该回去检查标注多边形而不是调模型。还有个容易忽略的点mask mAP 的计算会用到检测框。也就是说如果检测框不准即使掩码本身没问题mask mAP 也会被拖累。所以分割任务的调优要先把检测分支调好再去看掩码。4. 关节点估计分支单类人体姿态的轻量实现4.1 关键点表示方法与可见性建模关节点估计在 YOLOv8 里的实现比很多人想象的简单——它本质上是在检测的基础上让每个正样本额外回归一组关键点坐标。所以它的输出是[B, 413k, 8400]其中41是人体框和是人的置信度3k是关键点。为什么是3k而不是2k这是姿态估计里的一个经典设计。每个关键点输出(x, y, v)三个值x、y是归一化到 0 到 1 的坐标相对于检测框需要再映射回原图v是可见性置信度。可见性这一维区分了三种情况关键点在画面内且清晰可见v 接近 1关键点在画面内但被遮挡位置靠推断v 取中间值关键点完全不在画面里v 接近 0坐标无意义这个设计比只输出(x, y)强太多了。如果只有两维遇到人被门挡住一半这种情况网络会强行给一个坐标导致骨架结构扭曲有了可见性维度网络可以表达我不知道这个点在哪损失函数也能选择性地忽略这些点。坐标的归一化方式需要注意。YOLOv8 的关键点坐标通常是相对于该实例的检测框归一化的即值域 0 到 1表示在框内的相对位置。解码时要先乘以框的宽高再加上框的左上角坐标才是原图上的绝对坐标。我见过有人在可视化的时候直接用 0 到 1 的值画点结果所有骨架都缩在左上角一小块区域——就是漏了这一步映射。4.2 关键点损失可见性 BCE 加坐标回归关键点的损失由两部分组成分别对应可见性判断和坐标回归。可见性是分类问题用 BCE。对每个关键点GT 的可见性标签是 0 或 1或者 0、0.5、1 三档预测值经过 sigmoid 后算 BCE。坐标回归是回归问题YOLOv8 的实现思路和检测框回归类似也是基于 CIoU 或者类似的几何度量但作用对象变成了关键点组成的骨架结构。具体来说它会把预测的关键点和 GT 关键点分别看成一组点计算它们之间的相似度相似度越高损失越小。这里有个细节值得说不可见的关键点不参与坐标损失。这符合直觉——你不可能要求网络去回归一个画面里根本不存在的位置。所以数据集里那些v0的点只贡献可见性损失不贡献坐标损失。我自己做过鸟类关键点的项目标注喙、双翅、双脚、尾羽这些点体会是关键点的定义一致性比数量更重要。如果同一个姿态在不同样本里关键点的语义不一致比如有的标在关节、有的标在骨骼中点网络学出来的骨架会飘。标注前一定要写清楚每个点的定义并且让所有标注员对齐标准。这个坑我栽过一次重标了两千多张图。注意YOLOv8 官方 pose 实现是单人、单类。如果你要做的场景里有多个人它其实是用检测框分别框出每个人然后每个人单独估骨架。但如果两人重叠严重检测框本身就会相互干扰这时候可能需要在检测阶段就做得更细或者考虑专门的多人姿态方案。4.3 三任务的横向对比复杂度与适用场景把三个任务放一起看能更清楚它们的成本和边界。下面这张表是我自己整理的性能数据以 YOLOv8n 在 640 输入下的常见量级为参考具体数值随版本和硬件会有浮动维度目标检测实例分割关节点估计输出通道COCO4808448032116415156额外输出无proto [32,160,160]无计算量相对基准约 1.3 倍约 1.1 倍显存占用相对基准约 1.5 倍约 1.2 倍标注成本中高多边形高逐点典型场景车流统计、质检缺陷轮廓、遥感动作识别、体育分析从工程角度选型我的建议是能用检测解决的就别上分割。比如统计路口车流量你只需要知道有几辆车、在哪检测就够了。分割的边际价值只在需要精确轮廓时体现比如计算缺陷面积、区分粘连目标。关节点估计的适用面更窄基本都是人体动作、运动分析这类场景。还有个现实问题标注成本。检测框标一张图可能几秒钟多边形要几十秒关键点更慢。如果你的项目预算有限一定要在选型阶段想清楚别一开始选了分割标了两千张才发现其实检测够用。5. 环境配置与训练实操从零跑通自己的数据集5.1 环境搭建与版本组合怎么选环境配置这块我踩过的坑比训练本身还多。核心原则只有一条不要盲目装最新版。深度学习生态的版本兼容性很差一个版本不匹配就是一堆莫名其妙的报错。我的推荐做法是用 Conda 建独立环境然后按这个顺序来conda create -n yolo python3.10 -y conda activate yoloPython 选 3.10 是比较稳的兼容性好各种依赖都有轮子。然后是 PyTorch。这一步必须去官网的安装页面选对应的 CUDA 版本不要直接pip install torch那样装到的是 CPU 版本跑起来慢到怀疑人生。选择逻辑是先看你的显卡驱动支持哪个 CUDA 版本然后选对应的 PyTorch。比如显卡是 GTX 1660 Ti 这类较老的卡CUDA 11.8 的组合兼容性最好如果是较新的卡可以考虑 CUDA 12.x 的组合。# 以 CUDA 11.8 为例具体命令以官网生成器为准 pip install torch torchvision --index-url 对应源装完之后一定要验证import torch print(torch.__version__) print(torch.cuda.is_available()) # 必须是 True print(torch.cuda.get_device_name(0))cuda.is_available()返回 False 是最常见的问题。原因通常有三个装的是 CPU 版 PyTorch、CUDA 版本和驱动不匹配、或者环境变量没配好。排查顺序是先重新装对版本的 PyTorch再检查驱动版本nvidia-smi能看到最后看环境变量。之后装 ultralyticspip install ultralytics如果是在 PyCharm 里做记得把解释器切到刚才建的 conda 环境别用系统默认的 Python否则又是我明明装了却 import 不到。提示如果你用的是较老的显卡int8 量化推理可能不被支持或者性能提升不明显。这种卡上老老实实用 FP16 或者 FP32 推理就行别折腾量化。5.2 数据集组织与训练参数配置YOLO 的数据集目录结构是固定的dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── data.yamlimages和labels下的文件名要一一对应只是扩展名不同图片是 .jpg/.png标签是 .txt。这一点很容易出错——我曾经因为一张图没有对应的 label 文件训练时报了个很隐晦的错误排查了半天。data.yaml内容大致是path: /your/dataset/path train: images/train val: images/val nc: 3 names: [cat, dog, bird]nc和names必须和标注里的 class id 严格对应。class id 从 0 开始。训练命令的基本形态yolo taskdetect modetrain modelyolov8n.pt datadata.yaml epochs100 imgsz640 batch16如果是分割把taskdetect换成tasksegment姿态换成taskpose。模型可以选yolov8n/s/m/l/x五个尺寸n 最小最快x 最大最准。我的一般原则是先跑 n 验证流程通不通再换大模型冲精度。很多人一上来就上 l 或 x结果显存爆了、训练一天没结果浪费时间。关键参数解释几个我觉得最重要的参数默认值作用与调法epochs100训练轮数。小数据集 50 到 100 够了大数据集可以到 300imgsz640输入分辨率。小目标多可以提到 960 或 1280但显存翻倍batch16批次大小。显存不够就减配合accumulate补偿lr00.01初始学习率。小数据集可以降到 0.001 防止过拟合lrf0.01最终学习率系数和 lr0 相乘得到末期学习率freezeNone冻结前 N 层。迁移学习时冻结 backbone 能防过拟合close_mosaic10最后 N 轮关闭 mosaic 增强让模型适应真实分布freeze这个参数我用得比较多。如果你自己的数据集不大几千张以内直接全量微调很容易过拟合表现就是训练 loss 一直降、验证 loss 先降后升。这时候冻结前 10 层大概就是整个 backbone只训练 neck 和 head效果通常更稳。freeze的写法yolo taskdetect modetrain modelyolov8s.pt datadata.yaml freeze10 epochs100注意freeze是按层索引冻结的具体冻多少层要看你选的是哪个尺寸的模型。n 和 s 的层数少冻 10 层可能已经冻掉大半个网络了。5.3 训练过程监控与损失曲线解读训练启动后会生成runs/detect/train/目录里面有results.csv、权重文件、以及各种曲线图。怎么看这些曲线是判断训练是否健康的关键。loss 曲线box_loss、cls_loss、dfl_loss三条。正常情况下三条都应该单调下降然后趋于平缓。如果出现以下情况要警惕cls_loss一直不降可能是类别标注有错或者类别极不平衡box_loss降得很慢可能是学习率太大或者正样本分配有问题训练 loss 降但验证 loss 升典型的过拟合加正则或者减模型容量mAP 曲线metrics/mAP50和metrics/mAP50-95。前者宽松后者严格。一般 mAP50 会到 0.9 以上mAP50-95 能有 0.6 到 0.7 就算不错。如果 mAP50 高但 mAP50-95 很低说明框的大致位置对但精度不够可能是 DFL 没学好或者数据标注框不够紧。学习率曲线可以看到 warmup 和余弦退火的过程。warmup 阶段 lr 从很小的值线性升到 lr0然后稳定最后余弦退火到lr0 × lrf。如果这条曲线异常比如一直平的检查一下配置。画曲线的话results.csv可以直接用 pandas 读出来画import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(runs/detect/train/results.csv) df.columns df.columns.str.strip() plt.plot(df[epoch], df[train/box_loss], labelbox_loss) plt.plot(df[epoch], df[val/box_loss], labelval_box_loss) plt.legend() plt.show()注意results.csv的列名可能带前导空格读进来后要处理一下否则KeyError。这个小坑我中过。提示如果训练中途中断了想接着训用resumeTrue配合last.pt。但注意如果你的代码或数据改了resume 出来的结果可能不一致最好还是重新训。我一般只在显存不足导致中断时才 resume。6. 常见问题排查与部署踩坑实录6.1 训练阶段的典型故障与速查训练阶段的问题八成集中在数据和环境上真正是模型本身的问题反而少。我把遇到过的整理成一张表方便你对照排查。现象可能原因排查与解决训练一开始 loss 就是 nan学习率过大、数据里有非法值降 lr0 到 0.001检查标注是否越界或为空mAP 一直是 0类别 id 不匹配、标签路径错检查 data.yaml 的 nc/names可视化一批标注显存溢出batch 太大、imgsz 太大降 batch 或用 batch 配合 accumulate只检测到部分类别类别不平衡补充少数类样本或用 mosaic、copy-paste 增强验证集表现远差于训练集过拟合加 freeze、加数据增强、减模型尺寸、加早停小目标基本检不到输入分辨率不够imgsz 提到 960/1280或调小 anchor 相关设置这里我想单独说小目标检测因为这是提问频率最高的方向之一。小目标难检的根本原因是在 640 输入下一个 10×10 像素的目标经过 32 倍下采样后在最小特征图上只剩不到一个像素信息基本丢失了。解决办法有几个方向第一提高输入分辨率。这是最直接有效的640 提到 1280小目标在特征图上的尺寸翻倍。代价是显存和计算量增加大约 4 倍。第二用更深的高分辨率分支。比如在 backbone 早期层1/4 分辨率加一个额外的检测头专门负责小目标。这就是所谓的 P2 头代价是计算量增加不少。第三改进颈部融合。ASFFAdaptive Spatial Feature Fusion这类思路是让不同尺度的特征自适应加权融合对小目标有一定帮助。我看到过把 ASFF 加进 YOLOv8 neck 的方案在遥感数据集上有效但要注意权重初始化和通道对齐的细节加不好反而掉点。第四切图推理。大图切成小块分别检测最后合并。这个方法简单粗暴但有效缺点是推理时间线性增长而且跨图目标会被切断。6.2 导出与部署ONNX、TensorRT 与边缘板子训练完只是第一步真正落地要做导出和部署。ONNX 导出是最基础的一步yolo export modelbest.pt formatonnx opset12 imgsz640opset版本建议 12 或 13太低有些算子不支持太高有些部署框架还没跟上。导出后用onnxruntime跑一遍验证对比 PyTorch 的输出确保数值一致。TensorRT部署是性能瓶颈时的选择。核心流程是 ONNX 转 engine然后 FP16 或 INT8 推理。FP16 一般能带来 1.5 到 2 倍加速INT8 更多但要校准数据集而且精度可能掉。trtexec --onnxbest.onnx --saveEnginebest.engine --fp16这里有个大坑输入输出的后处理要不要包含在模型里。YOLOv8 默认导出会带上解码和 NMS好处是部署端简单如果你想在 C 侧自己写后处理导出时要加nmsFalse拿到原始张量。两种方式我都用过一般来说嵌入式场景带后处理更省事服务器场景自己写更灵活。边缘板子部署比如带 NPU 的 RK3588 这类走的是另一条路径通常是 ONNX 转成板子专用的模型格式再调用 NPU 推理。这条路的难点在算子支持——YOLOv8 里的 DFL、某些激活函数、还有后处理中的 topk可能不被 NPU 直接支持需要替换或者放到 CPU 上跑。实操体会是先跑通官方提供的示例模型再换自己的权重。很多人一上来就拿自己训练的模型去转结果各种算子报错根本分不清是模型问题还是流程问题。量化方面如果板子支持 int8 并且你追求速度需要准备一批有代表性的校准图几百张就够覆盖各种光照和场景。校准集选取不好量化后的精度会掉得很难看。6.3 改进方向的取舍与经验总结最后聊聊改进。现在网上关于 YOLOv8 改进的内容非常多——换注意力、换卷积、加 ASFF、加各种模块看起来眼花缭乱。我的经验是改进之前先确认 baseline 有没有做到位。很多人的改进其实是补了基础工作的漏洞。比如你的 mAP 低可能是因为数据集标注质量差、类别不平衡没处理、学习率没调好、输入分辨率不匹配目标尺寸。这些问题不解决加什么模块都是白费。我见过有人加了三个注意力模块mAP 涨了 0.3结果一检查发现验证集里有一半图片标注框偏移修完标注直接涨了 8 个点。真正值得做的改进我按性价比排个序。第一档是数据层面的改进增加数据、修正标注、做针对性的增强小目标用 copy-paste、遮挡用 mosaic。投入产出比最高。第二档是训练策略层面调学习率策略、调损失权重、用 EMA指数移动平均、用更好的初始化。这些不改结构风险低。第三档是结构层面换骨干、加模块、改颈部融合。这里要注意改结构一定要做严格的消融实验同数据、同参数、同随机种子只改一个变量。不然你根本不知道涨点是来自改进还是来自随机波动。第四档是后处理层面改 NMS 策略、多尺度测试、TTA测试时增强。推理成本会增加但精度稳定提升。关于轻量化改进如果你的目标是macs 仅 5MB 级别这种极小模型思路无非是深度可分离卷积、通道剪枝、知识蒸馏。这些技术本身成熟但要在 YOLOv8 上落地得改源码、重训工作量不小。如果只是想在小设备上跑我建议直接用 YOLOv8n 或者找一个 n 级别以下的官方小模型比自己去剪枝划算。还有个方向是开放词汇检测和多模态微调这类工作需要额外的文本或图像编码器工程复杂度高一般项目用不上除非你的场景确实需要零样本识别新类别。我在实际使用中最大的一个体会是YOLOv8 的价值不只在精度更在于它的工程完整性。从数据组织、训练、验证、导出到部署整条链路都有现成的工具支持而且三个任务的接口高度统一。这意味着你可以把精力放在数据质量和场景适配上而不是重复造轮子。真要说有什么遗憾大概是文档对内部实现细节的披露比较有限很多机制比如正样本分配的具体实现、关键点损失的确切形式得去翻源码才能确认。但换个角度想这也是个机会——能把源码读明白的人改起结构来会顺手很多。最后分享一个小技巧调试阶段我会开一个小脚本把模型的中间层输出 hook 出来逐层看特征图的形状、数值范围和分布。这样改结构的时候哪一层通道对不上、哪一步归一化出问题一眼就能看出来。这个习惯帮我省了大量时间比对着报错瞎猜强得多。
返回列表