1. 为什么在 Ubuntu 上折腾 YOLOv3 训练自己的数据集如果你手里有一批标注好的图片想让模型学会识别你自己关心的目标——比如车间里的零件缺陷、仓库里的特定货品、农田里的病虫害叶片——YOLOv3 到今天依然是一个值得上手的起点。它不像一些新框架那样对环境和显存要求苛刻网络结构清晰配置文件全写在明面上改起来心里有底。而 Ubuntu 作为训练环境最大的好处是命令行工具链完整、依赖安装路径干净、多卡和后台训练都顺手很多开源脚本默认就是照着 Linux 环境写的。这篇笔记面向的是已经跑通过官方 Demo、但一到换成自己的数据就卡住的人。我会把从数据准备、配置文件改写、训练启动到结果验证的完整链路拆开讲重点放在那些官方文档一笔带过、但实际会让人卡半天的细节上。比如类别数改了但过滤器数量没同步、训练日志里 loss 突然变 NaN、验证时一张图都框不出来——这些问题我都会给出排查思路而不是只丢一句检查配置。需要提前说明的是YOLOv3 有多个实现分支不同分支的目录结构和脚本命名差异不小。我下面讲的是最常见的那套基于 Darknet 的流程如果你用的是其他封装版本思路一致但具体文件名要对照你手里的代码。另外训练自己的数据集这件事数据质量的重要性远大于模型调参这一点我会在数据准备那节重点展开。2. 训练前的环境与数据准备2.1 Ubuntu 下的依赖安装与编译踩坑Darknet 是 C 语言写的编译本身不复杂但依赖缺一个就会在 make 阶段报错。我习惯先把基础编译工具和图像库装齐sudo apt update sudo apt install build-essential git libopencv-dev python3-opencv如果你要用 GPU 训练还需要装好对应版本的 CUDA 和 cuDNN。这里有个经验CUDA 版本和显卡驱动版本要匹配装之前先用nvidia-smi看一下驱动支持的 CUDA 上限别直接照搬别人的版本号。装完之后改 Makefile把GPU1、CUDNN1、OPENCV1打开然后make -j8编译。编译报错最常见的原因是 OpenCV 头文件路径找不到这时候检查pkg-config --cflags opencv4能不能正常输出不行就手动在 Makefile 里指定 include 路径。提示编译完成后先跑一次官方权重做推理测试确认环境没问题再动自己的数据。这一步能帮你把环境问题和数据问题彻底分开后面排查会轻松很多。2.2 数据标注格式与目录组织YOLO 用的是归一化后的中心点坐标加宽高格式是类别 中心x 中心y 宽 高每个值都在 0 到 1 之间。很多人用标注工具导出的是 VOC 的 XML 或者 COCO 的 JSON需要转一道。转换脚本网上很多但我要提醒的是转换后一定要抽查几张把归一化坐标反算回像素坐标画框到原图上肉眼确认。我遇到过标注工具把图片旋转信息写进 EXIF、转换脚本没读导致框整体偏移的情况光看数字是发现不了的。目录结构建议这样组织清晰且不容易乱dataset/ images/ train/ (训练图片) val/ (验证图片) labels/ train/ (对应标注txt) val/ train.txt (训练图片路径列表) val.txt (验证图片路径列表) classes.names (类别名称一行一个)train.txt和val.txt里每行是一张图片的绝对路径或相对路径。路径写错是新手最常见的翻车点训练一启动就报 Cannot load image八成是路径问题。建议用find $(pwd)/images/train -name *.jpg train.txt这种方式生成避免手写出错。2.3 数据量与类别平衡的现实判断这里说点实在的。YOLOv3 要训出能用的效果每个类别至少准备几百张标注图类别越多、目标越小需要的量越大。如果某个类别只有几十张模型大概率学不会或者学了也过拟合。我见过有人拿每个类二三十张图就想训一个五类检测器结果 loss 降不下去还以为是配置问题其实是数据根本不够。另外负样本不含目标的背景图也要适当放一些能明显降低误检。比例上大概每十张正样本配一两张纯背景图就够。类别不平衡的话多的类别可以适当下采样或者用数据增强给少的类别补量但增强不能替代真实多样性翻转、裁剪、调亮度这些手段只能锦上添花。3. 配置文件改写那些一改就错的关键字段3.1 类别数与过滤器数量的联动关系这是 YOLOv3 训练自己数据集时最高频的错误来源。配置文件里有两处必须同步改classes和filters。classes改成你的类别数而每个 yolo 层前面的卷积层filters要改成(classes 5) * 3。这个 5 是四个坐标加一个置信度3 是每个网格预测三个锚框。假设你有 4 个类别那 filters 就是(45)*3 27。cfg 文件里通常有三个 yolo 层对应三个不同尺度的特征图这三处前面的 filters 都要改。只改 classes 不改 filters训练可能不报错但检测结果会完全乱套或者直接崩掉。我建议改完之后用grep -n filters\|classes把相关行都列出来核对一遍别凭记忆。3.2 锚框anchors要不要重新聚类官方 cfg 里的 anchors 是在 COCO 数据集上聚类出来的如果你的目标尺寸分布和 COCO 差很多——比如你要检测的是航拍图里的小车或者工业图像里的大面积缺陷——那重新聚类 anchors 是有收益的。做法是用 k-means 对你的标注框宽高做聚类聚出 9 组按面积从小到大填到三个 yolo 层里。不过我要泼个冷水如果你的数据量和 COCO 差着数量级重新聚类带来的提升可能被数据不足掩盖掉。我的建议是先用官方 anchors 跑一版基线看验证效果如果发现小目标漏检严重再考虑聚类。聚类脚本网上有现成的输入是所有标注框的宽高输出 9 组 anchor注意聚类时用的是绝对像素尺寸还是归一化尺寸要和 cfg 里的单位保持一致。3.3 训练超参的调整逻辑cfg 里和训练相关的几个关键参数batch、subdivisions、learning_rate、max_batches、steps。batch/subdivisions决定每次送入显存的图片数显存不够就调大 subdivisions。max_batches一般设为classes * 2000但不少于 4000。steps通常设成 max_batches 的 80% 和 90%在这两个点学习率会衰减。学习率初始值官方给的是 0.001小数据集可以适当调小到 0.0005 甚至更低避免一开始就震荡。这里有个判断技巧训练前几百轮如果 loss 一直剧烈波动不下降多半是学习率偏大如果 loss 几乎不动可能是学习率太小或者数据有问题。别一上来就大改先小步调。4. 启动训练与日志解读4.1 训练命令与断点续训启动训练的命令大致是这样./darknet detector train data/obj.data cfg/yolov3-custom.cfg darknet53.conv.74最后那个是预训练权重用官方在 ImageNet 上训好的卷积层权重做初始化比从零开始收敛快得多。obj.data文件里要写清楚类别数、训练列表、验证列表、类别名文件和权重保存目录。训练中断了想接着训把最后的权重路径换上去再加-clear参数部分版本需要就能续训。但要注意续训时 cfg 里的 max_batches 不要改否则学习率调度会乱。权重文件每隔一定轮数会自动保存命名带迭代次数挑最新的那个续就行。4.2 从 loss 曲线判断训练状态训练日志里每若干轮会打印一次 loss格式是avg loss加各项分损失。正常情况下总 loss 应该在前几百轮快速下降然后缓慢收敛。如果出现下面几种情况就要警惕loss 变成 nan多半是学习率太大、数据里有异常标注比如坐标超出 0-1 范围、或者某张图损坏。先检查标注文件有没有越界值再考虑降学习率。loss 长时间不降检查数据路径对不对、标注和图片是否一一对应、类别数配置是否一致。loss 降得很低但验证效果差过拟合了加数据或加增强或者减少训练轮数。我习惯把日志重定向到文件训练完用脚本把 loss 抽出来画曲线比盯着终端刷屏直观得多。4.3 显存不足时的取舍显存不够是训练自己数据集时的常客。除了调大 subdivisions还可以把输入尺寸从 608 降到 416 甚至 320代价是小目标检测能力下降。另一个办法是冻结前面部分层不训练只训后面的检测头但这需要改代码适合有一定基础的人。我的经验是如果显存只差一点点优先调 subdivisions差得多就降输入尺寸别硬撑。5. 验证、调优与常见问题排查5.1 用验证集算 mAP 而不是只看 lossloss 低不代表模型好用。真正能说明问题的是在验证集上算 mAP平均精度。Darknet 自带detector map命令能对验证集批量推理并计算指标。跑之前要确认验证列表和标注都正确否则算出来的数字没意义。算 mAP 时注意 IoU 阈值默认 0.5如果你的任务对定位精度要求高可以看 0.75 的结果。另外每个类别的 AP 都要看别只看平均值。我遇到过整体 mAP 还行、但某个类别 AP 接近 0 的情况一查发现那个类别的样本太少模型根本没学会。5.2 检测结果可视化与误检分析把模型在测试图上的检测结果画出来看是最直接的调优依据。重点看两类错误漏检该框的没框和误检不该框的框了。漏检多可能是 anchors 不合适、输入尺寸太小、或者该类样本不足误检多可能是负样本不够、置信度阈值太低。针对性地补数据或调阈值比盲目改网络结构有效得多。置信度阈值和 NMS 阈值也是可调的。默认置信度 0.5、NMS 0.45实际部署时可以根据业务需求调。要求高召回就降置信度阈值要求高准确就升。这些阈值在推理命令里就能传不用重新训练。5.3 几个我踩过的典型坑第一个坑训练时图片路径用了相对路径换目录跑就找不到图。建议统一用绝对路径生成列表。第二个坑类别名文件里有多余空行导致类别索引错位检测出来的类别名全对不上。第三个坑验证集和训练集有重复图片mAP 虚高实际部署一塌糊涂。划分数据集时一定要去重。第四个坑改了 cfg 但忘了重新编译或没保存训练用的还是旧配置白跑一晚上。注意每次改完配置先拿一张图跑一次推理确认模型输出正常再启动长时间训练。这个习惯能帮你省下大量无效训练时间。6. 从训练完成到实际可用的最后一步训练出权重只是中间产物要真正用起来还得考虑推理速度和部署环境。YOLOv3 在普通显卡上跑单张图大概几十毫秒如果要在边缘设备上跑可能需要转成其他推理格式或者换更轻量的模型。这一步的取舍取决于你的实际场景追求精度就用大输入尺寸追求速度就降尺寸或换模型。另外训练好的权重最好固定下来做版本管理记录清楚用的是哪版数据、哪版配置。我吃过亏隔了几个月想复现某个效果结果数据和配置都对不上了只能重训。把数据版本、cfg 文件、权重、mAP 结果打包存档是个值得养成的习惯。最后分享一个实用技巧如果训练过程中想中途看看效果不用等训完直接拿中间保存的权重跑推理就行。Darknet 保存的权重是完整可用的随时可以加载测试。这样你能在训练早期就发现方向性问题及时调整而不是等几天后才发现白训了。