简介:YOLOv5 6.1版本全中文注释代码包,专为深度学习初学者、研究生及目标检测项目开发者打造,有效解决原生代码注释不足、难以理解内部实现的问题。压缩包内共2000个文件,涉及Python源码、C/C++扩展、文本说明、YAML模型配置、预训练权重及脚本工具等多种类型,总大小约296.99MB,目录划分清晰,便于按模块查阅。资源特别配套CSDN博客专栏教程,对数据增强、骨干网络、损失计算、推理后处理等核心部分进行逐段中文注解,同时给出详细的代码逻辑分析和运行演示,可帮助读者摆脱“只跑通、看不懂”的困境。无论是课程设计、毕业课题,还是创新创业大赛中的物体识别需求,都能借助完整注释与教程快速落地。目前已有2785人学习下载,值得目标检测入门与进阶者收藏。
1. 为什么我建议你直接啃YOLOV5 6.1的中文注释源码:一天从跑通到改得动
第一次打开 YOLOV5 工程的人,十个有八个会卡在同一个地方:代码能跑,但看不懂。尤其对研究生和准备参加创新创业大赛的团队来说,时间是最贵的,你不可能花两周去一行行查函数定义。这份 YOLOV5 6.1 版本全中文注释压缩包,把 detect.py、train.py、models、utils 等核心文件的注释都换成了人话,配合配套教程能让你一天内理清整个调用链。它解决的不是“怎么把模型跑起来”,而是“跑起来之后你能不能改得动”。适合目标检测入门者、需要快速出成果的比赛队伍,以及所有被源码劝退的开发者。我在给课题组分代码时,最常听到的抱怨就是“网上教程都是英文注释,看一行查一次词典”,所以这个包一出来,组里几个人直接原地复制。
2. 先看懂 YOLOV5 6.1 的工程结构:注释里最容易迷路的三个目录
拿到压缩包后,第一件事不是急着训练,而是建立“地图”。YOLOV5 6.1 的官方仓库本身不算大,但文件很杂。models、utils、data、runs 各自承担不同任务,注释包虽然把关键文件都翻译成了中文,但结构还是要自己理明白。
2.1 从入口到模型:detect.py、train.py、models/yolo.py 的调用链
先说三个最核心的文件。detect.py 和 train.py 是两个并列入口,对应推理和训练;models/yolo.py 定义了网络主结构。把它们的关系理清,整个项目就能横着走了。
# 解压中文注释包,注意用支持UTF-8的解压工具,避免Windows下中文乱码 unzip yolov5-6.1-zh.zip -d yolov5-zh cd yolov5-zh pip install -r requirements.txt # 先跑自带的检测demo,验证环境 python detect.py --weights yolov5s.pt --source data/images/bus.jpg这段命令解决“环境通不通”的问题。unzip 解压后,requirements.txt 里包含了 torch、opencv-python 等核心依赖。detect.py 是推理入口,内部会调用 models.yolo 的 DetectionModel 类,最终把检测框画到输出图片上。注意,第一次运行 detect.py 时,如果本地没有 yolov5s.pt,程序会尝试从 GitHub 下载。这一步在有些网络环境下会卡很久,我建议你提前把权重文件放到工程根目录,具体放在第 4 章的坑里细说。
接下来看训练入口。train.py 的代码量比 detect.py 大得多,但核心训练循环很短。注释包里在 train.py 里有一段醒目的中文说明,告诉你“这是每次迭代都会执行的四步”。类似这样:
# 前向传播:把一批图片喂给模型,得到预测特征 pred = model(images) # 计算损失:pred与targets比较,得到loss和各项子损失 loss, loss_items = compute_loss(pred, targets.to(device)) # 反向传播:把梯度回传,这一步决定网络如何更新 scaler.scale(loss).backward() # 更新权重:优化器根据梯度调整参数 optimizer.step()这段代码是训练循环的灵魂。pred 是模型输出,在 6.1 版本里是一个包含三层特征图的 tuple;targets 是标注数据,格式为[image_index, class, x_center, y_center, width, height]。compute_loss 由 utils/loss.py 提供,内部完成了 anchor 匹配和 GIoU 损失计算。中文注释在 loss 这一块写得非常密,因为很多想改检测头或想自定义 loss 的人都要从这里入手。注意 6.1 版本的 loss 代码相比 5.0 做过重构,不要拿着旧版本的教程硬套。
再回到 detect.py。它的核心其实也短:加载模型 -> 读取图片 -> 预处理 -> 推理 -> NMS -> 画框。其中预处理步骤里有个 letterbox 函数,会把图片等比缩放到 640 并补灰边,这一步很多人看不懂。注释包里在 utils/augmentations.py 里用中文标明了“缩放到 640x640,保持比例不变,用 114 灰度填充边缘”,并解释了为什么要这样:因为网络全卷积层对尺寸不敏感,直接 resize 会破坏物体比例。
调用链捋一遍:detect.py 和 train.py 都从 models/yolo.py 中实例化 DetectionModel,各自在 utils 目录中取所需工具。所以,除非你想改网络结构,否则每天打交道最多的其实是 utils 里的工具文件。另外,runs 目录也值得提前搞懂。每次运行 detect.py 或 train.py 后,结果都会输出在 runs/ 下面。6.1 版本默认输出到 runs/detect/exp、runs/detect/exp2 等。中文注释包里在 utils/general.py 的 increment_path 函数处做了注释,解释了为什么名字会递增而不是覆盖。很多新手找不到输出图片,就是因为不知道它在 runs/detect/exp 里。
2.2 utils/ 目录里那些“工具函数”其实决定了训练能不能跑
很多人忽略 utils 目录,以为只是辅助。实际上,训练能不能跑、loss 能不能降,很大程度取决于这一段。utils/datasets.py 负责加载图片与标签,里面的 LoadImagesAndLabels 类在每次取数据时都会执行 mosaic 增强、随机仿射变换等操作。如果你的数据集标签格式不对,很多报错恰恰发生在 datasets.py 内部,而不是你的训练代码里。
# 在中文注释包里,用grep快速定位关键函数位置 grep -n "def train" train.py grep -n "class DetectionModel" models/yolo.py # 查看mosaic增强相关注释 grep -n "mosaic" utils/datasets.pygrep -n 会把行号一并输出。配合配套教程的章节名,你可以快速跳转到注释对应位置。中文注释包在每段重要代码前都写了类似“这里做 mosaic 增强,把四张图随机拼成一张,提高小目标检测能力”的说明。对比原版英文注释,这种解释对新手理解“为什么写这段代码”帮助极大。
utils/general.py 里面的 check_img_size、check_file、check_dataset 是三个“检查狂魔”。很多让人摸不着头脑的报错,比如“Dataset not found”或“Image not found”,都是它们主动抛出来的。注释包里对这些函数标注了“这个检查是为了避免路径错误导致训练到一半崩溃”。遇到错误时,先看抛错的位置是不是这三个函数,再按提示改路径,比自己瞎猜高效得多。
还有一个容易踩坑的目录是 utils/torch_utils.py。它管理设备选择、模型并行、梯度累加等。6.1 版本在这里增加了一些自动混合精度训练的辅助函数。中文注释包特别标出了“当 batch_size 较小且检测目标较小时,不建议开启 AMP 训练”,原因是半精度在小目标场景下有时会丢精度。这个细节很少有人强调,但对比赛调模型很有用。
2.3 配套教程怎么用:先跑通 demo 再逐行读
配套教程是博客形式,我建议按“先环境,后单张图片,再训练”的顺序来。拿到代码包后不要直接开读源码,先运行 detect.py 看到结果,再反向去读代码。有了输出结果,你对变量的含义会有具体的感知。
# 在Python里直接查看模型结构,确认网络配置是否加载正确 import torch from models.yolo import Model model = Model(cfg='models/yolov5s.yaml', ch=3, nc=80) print(model)这段代码的逻辑:Model 类接收配置文件 cfg、输入通道数 ch 和类别数 nc。cfg 决定网络的深度和宽度,ch 是输入图像的通道(RGB 就是 3),nc 是数据集类别数。打印出来的网络结构很长,你会看到从 Conv 到 C3 再到 Detect 的完整流程。中文注释在 Detect 模块附近有大量说明,告诉你这个模块如何把三张特征图变成预测框。如果你换了自己的数据集,nc 必须改成你的实际类别数,否则最后一层维度不匹配,训练直接报错。
配套教程里还有网络结构图。6.1 版本相比 5.0 的最大改动之一是用 C3 模块替换了 BottleneckCSP,参数量更小。你会在 models/yolo.py 的 C3 类旁边看到中文注释,说明它由几个 Conv 和 Bottleneck 串联而成。建议把结构图打印出来,对着注释一行行看,比单独看代码更容易建立整体印象。
3. 把 YOLOV5 6.1 改成自己的数据集:从标注到训练的超参数设置
工程结构搞清楚之后,接下来就是把自带的 COCO 模型换成你的数据集。无论是研究生课题还是比赛,这一步都会反复用到。在这章你会看到三个核心操作:整理数据、改配置、设置超参数。
3.1 数据标注与目录组织:YOLO 格式到底长什么样
目标检测数据集的格式很多,VOC、COCO、YOLO 各有各的存储方式。YOLOV5 用的是 YOLO 格式:一个图片对应一个 txt 文件,txt 里每行表示一个目标,五个数字分别是 class_id x_center y_center width height,注意这四个坐标都是归一化到 0-1 的。第一次用的人最容易在“中心点坐标”和“左上角坐标”上翻车。
# 对应图片 bus.jpg 的标签文件 bus.txt 0 0.45 0.35 0.20 0.42 1 0.62 0.68 0.31 0.12 # class_id从0开始,x_center/y_center/width/height均为归一化值这段文本的逻辑:如果你用 LabelImg 这类工具标注,导出为 YOLO 格式时会自动生成这样的 txt。如果是从 COCO 的 json 转换,需要自己写脚本把归一化坐标算出来。常见误区:有人把坐标写成了像素值,或者把宽高写成了右下角的坐标,训练时会报 invalid box 或 loss 变成 nan。
目录组织也有讲究。YOLOV5 官方推荐的数据集目录结构是:
datasets/ your_dataset/ images/ train/ val/ labels/ train/ val/这个结构的核心是:images 和 labels 是两个并列的父目录,下面再按 train/val 划分。图片和标签文件必须同名(后缀不同)。训练时,YOLOV5 会根据图片路径自动找到同名标签,所以标签文件名千万不能改错。我习惯在数据准备阶段写一个检查脚本,确认每一张训练图片都有对应 txt,反过来也一样。
如果从 VOC 格式转换,可以按下面这个 Python 脚本操作。这也是我在比赛里经常改的模板:
# 把VOC的xml标注转成YOLO的txt标注 import xml.etree.ElementTree as ET def voc_to_yolo(xml_file, class_names, out_txt): tree = ET.parse(xml_file) root = tree.getroot() size = root.find('size') w = int(size.find('width').text) h = int(size.find('height').text) lines = [] for obj in root.iter('object'): name = obj.find('name').text box = obj.find('bndbox') xmin = float(box.find('xmin').text) ymin = float(box.find('ymin').text) xmax = float(box.find('xmax').text) ymax = float(box.find('ymax').text) x_center = (xmin + xmax) / 2 / w y_center = (ymin + ymax) / 2 / h bw = (xmax - xmin) / w bh = (ymax - ymin) / h class_id = class_names.index(name) lines.append(f"{class_id} {x_center:.6f} {y_center:.6f} {bw:.6f} {bh:.6f}") with open(out_txt, 'w') as f: f.write('\n'.join(lines))脚本逻辑:用 ElementTree 解析 VOC 的 xml,读取图片宽高,把每个目标的左上角/右下角坐标换算成归一化的中心点和宽高。class_names 是类别列表,比如['person','car']。注意:如果 xml 里没有 size 或者某个目标缺失 bndbox,脚本会报错,所以转换前最好先清洗一遍数据。
提示:转换脚本只负责格式,不负责检查坐标越界。转换后一定要用后面的 awk 命令筛选一遍,把越界标签扼杀在训练之前。
3.2 修改数据集配置:YAML 里的每个字段都别乱动
YOLOV5 通过一个 yaml 文件来告诉训练脚本你的数据在哪、有几类。这个文件虽然简单,但字段一旦写错,程序会以各种奇怪的方式报错。来看一个典型配置:
# data/custom.yaml path: ../datasets/your_dataset/ # 数据集根目录 train: images/train # 训练图片相对路径 val: images/val # 验证图片相对路径 nc: 2 # 类别数量 names: ['person', 'car'] # 类别名称,与标注里的class_id一一对应字段说明:path 指定数据集根目录,train 和 val 是相对 path 的路径。这里最容易踩的坑是 path 用绝对路径,换一台机器就崩。我一般使用相对路径,并把数据集放在和 yolov5 工程同级的 datasets 目录下。另一个坑是 nc 和 names 长度对不上,比如 nc 写了 2 但 names 给了 3 个,训练时会报 assert 错误。中文注释包在 data/ 目录下附带的 custom.yaml 模板里,对这些字段每一行都有中文解释。
修改完 yaml 后,建议先跑一个 1 个 epoch 的命令做自检:
python train.py --data data/custom.yaml --weights yolov5s.pt --epochs 1 --img 640这里的--epochs 1是为了快速验证数据和配置。如果前面有路径错误、标签错误,通常还没跑完第一个训练步就会报错。验证通过后,再把 epochs 改回实际值。我见过很多人在第一次训练时就设 100 个 epoch,结果第 2 分钟报错又得重来,浪费大量时间。
为了在训练前发现标签错误,我还会用两条 shell 命令做体检:
# 检查数据集里有没有0字节的空标签 find . -name "*.txt" -size 0 | wc -l # 统计所有标签里的class_id,确认没有超过nc-1的值 awk '{print $1}' labels/train/*.txt | sort -u第一条命令统计空标签数量,空标签会导致部分图片被忽略;第二条命令打印所有标签里出现的 class_id 值。如果最大值大于等于 nc,说明标注的类别编号超出了配置范围,训练时会出现越界错误。这两条命令不花时间,却能拦住大部分低级问题。
3.3 训练超参数与模型选择:s/m/l/x 怎么选
YOLOV5 6.1 一共有 n/s/m/l/x 五个尺寸的模型配置,区别是网络的深度和宽度。n 最轻量,x 精度最高但最吃显存。怎么选不是拍脑袋,而是看设备和你对速度的要求。如果是笔记本的 RTX 3060,我一般选 s 或 m;如果是 16G 显存的卡训练自己的数据,可以从 m 开始试;如果比赛对速度有硬性要求,就选小模型。
训练命令示例:
python train.py --data data/custom.yaml --weights yolov5s.pt --epochs 100 --batch-size 8 --img 640 --device 0参数说明:--weights有两种用法。给yolov5s.pt是预训练权重,属于迁移学习,收敛快;给yolov5s.yaml是随机初始化,从头训。新手建议用预训练权重,因为样本量通常不够从零训练。--batch-size要按显存调节,不足时先降到 4 或 2,而不是换更大的模型。--img是训练时输入的图片尺寸,默认 640;如果你的目标很小,可以提高到 960,但训练时间和显存占用都会上涨。
超参数文件 data/hyp.scratch-low.yaml 里包含学习率、动量、损失系数等参数。6.1 版本默认的 low 配置对多数任务够用,我不建议一上来就改。真要调,重点关注 lr0 和 anchor_t。lr0 太大 loss 容易震荡,太小收敛极慢。anchor_t 是 anchor 匹配阈值,小目标数据集可以考虑稍微调大。中文注释包在 hyp 目录里逐行写了中文解释,这在其他版本里是很少见的。
最后,训练命令里加上--project和--name,让每次实验输出到独立目录。这样后面对比模型非常方便:
python train.py --data data/custom.yaml --weights yolov5s.pt --epochs 100 --batch-size 8 --img 640 --project runs/train --name my_experiment_001这样 runs/train/my_experiment_001/ 下会保存权重、日志和曲线。如果没有这个习惯,默认会生成 runs/train/exp,第二次就是 exp2,时间一长谁也不知道哪个是哪个。这个习惯帮我省了无数找模型的精力。
4. 避坑:YOLOV5 6.1 跑通的五条血泪经验
这一章把我在科研和比赛中踩过的坑集中列出来。每一条都是“现象-原因-解决”结构,建议遇到问题时直接查对应的条目。
4.1 权重文件下载失败导致程序“卡死”
现象是执行 python detect.py --weights yolov5s.pt 时终端长时间没反应,最后报连接超时。原因是本地没有权重文件时,YOLOV5 会自动从 GitHub 下载,在部分网络环境下这个过程极慢,看起来就像程序卡住了。解决方法是提前手动下载权重文件,放在工程根目录,并用下面的命令验证权重完整性:
python -c "import torch; m = torch.load('yolov5s.pt', map_location='cpu'); print(m.keys())"能打印出模型的关键字典就说明文件没问题。我还建议把 detect.py 里自动下载那段代码注释掉,免得在演示或比赛现场突然弹出一个下载提示。注意,6.1 版本权重与 5.0 不通用,不要拿旧权重混用。
4.2 中文路径报错
现象是图片放在 D:\数据集\训练图片\1.jpg,运行 detect.py 时图片加载不出来,训练时报错找不到文件。原因是 OpenCV 在 Windows 下对中文路径支持不好,路径里的中文经过编码后无法正确找到文件。这不是代码逻辑问题,属于环境问题,很玄学。解决方法是把所有路径改成英文。数据集、工程、权重文件都不要出现中文。如果你的标签名称需要中文,可以放在模型后处理的映射字典里,但文件路径必须 ASCII 编码。我有一个课题组成员的样本图片放在“桌面\毕业设计\实验数据”,为此折腾了一个下午,改成 D:\data\project_data 后一切正常。
4.3 显存不够但换小模型治标不治本
现象是训练时报 CUDA out of memory,于是从 yolov5x 换成 yolov5s,结果还是 OOM。原因是显存占用不只是模型大小决定的。batch size 和图片尺寸同样关键,甚至影响更大。模型变小了,但 batch size 还是默认的 16,图片还是 640,照样爆显存。解决方法是优先把 batch-size 降到 2 或 4,同时把 img 降到 512 或 416。这样通常能在不损失太多精度的情况下跑起原本想用的模型。我的经验是:batch size 减半,显存占用几乎减半,训练时间变长但可控。如果仍然不足,再考虑使用梯度累加技巧,在 train.py 里把单 batch 的梯度累积起来,模拟更大的 batch。
注意:观察显存用 nvidia-smi 的 Memory-Usage 列,而不是只看系统全局内存。
4.4 图片尺寸与 anchors 不匹配:训练震荡的隐形原因
现象是训练时 loss 在某个 epoch 突然从 0.05 跳到 0.8,然后又降回去,反复多次。很多人以为学习率太大,结果调小后依然如此。原因是 YOLOV5 6.1 的默认 anchors 是基于 COCO 数据集的 640 尺寸设置的。如果你的数据集中目标很小,或者图片分辨率很特殊,默认 anchors 与真实目标尺寸差距过大,导致前期的 loss 巨幅波动。解决方法是让 YOLOV5 在训练前自动计算 anchors,它会根据你的标签聚类出合适的 anchors。在训练日志里你会看到anchors: 3.5,6.2,...这样的输出。如果看到 “anchors not suitable” 的警告,可以在 train.py 里调整 autoanchor 的匹配阈值,或者直接去掉--noautoanchor参数。中文注释包在 utils/autoanchor.py 里把这些逻辑都注释清楚了,这也是我推荐仔细看的文件之一。
4.5 标签格式“看起来对”但训练 loss 为 nan
现象是数据集来自网上下载,标签 txt 内容类似“0 0.5 0.5 100 200”,训练一开始 loss 就是 nan。原因是坐标必须是归一化到 0-1 之间的浮点数,宽高不能大于 1。上面的例子显然把宽高写成了像素值,导致计算损失时出现除零或无穷大。解决方法是检查标签文件,把宽高分别除以图片宽高。另外检查是否出现负数或极端值,可以用 awk 命令筛出所有大于 1 的坐标:
awk '{for(i=2;i<=NF;i++) if($i>1 || $i<0) print $0}' labels/train/*.txt | head这条命令会把含有越界坐标的标签行打印出来,方便定位。这个问题在中文论坛里被问过无数次,属于最常见的翻车原因。
5. 从模型到落地:用 Python 调用 YOLOV5 做批量推理的具体技巧
训练完模型不算完,比赛 Demo 和实际项目都需要把模型集成到自己的流程里。直接从命令行调 detect.py 看起来简单,但每张图片都启动一次 Python 进程,速度慢且没法处理动态数据。所以,我把常用的封装方法整理成三个小节,都是可以直接抄走的代码。
5.1 把 detect.py 的 main 逻辑抽成函数,省掉重复传参
detect.py 本身是一个命令行工具,但项目里要反复调用检测的时候,每次传一堆参数很烦。常见做法是把 detect.py 的 run 函数改造成一个可调用的函数。我这里写了一个精简版:
import torch from models.experimental import attempt_load from utils.general import check_img_size, non_max_suppression from utils.torch_utils import select_device def load_model(weights='yolov5s.pt', device='0', imgsz=640): device = select_device(device) model = attempt_load(weights, map_location=device) model.stride = int(model.stride.max()) imgsz = check_img_size(imgsz, s=model.stride) return model, device, imgsz def infer_image(model, img, device, imgsz, conf_thres=0.5, iou_thres=0.45): # img: BGR numpy array img = torch.from_numpy(img).to(device) img = img.float() / 255.0 # 0-255归一化到0-1 if len(img.shape) == 3: img = img[None] # 增加batch维 with torch.no_grad(): pred = model(img, augment=False)[0] det = non_max_suppression(pred, conf_thres, iou_thres)[0] return det.cpu().numpy()代码逻辑:load_model 负责加载权重并确定设备与输入尺寸。infer_image 接收一张 BGR 图片,先转成 Tensor 并归一化,然后前向传播,最后做 NMS。返回结果是 N*6 的 numpy 数组,每行是[x1, y1, x2, y2, conf, class]。注意:这里输入的 img 是原始尺寸,但模型要求输入尺寸是 stride 的倍数,所以我一般在外部会先对图片做 letterbox 处理。如果没有处理,小尺寸图片可能报错。
我还会在 infer_image 里补一个 letterbox 调用,让函数内部处理缩放和坐标映射。具体实现可以复用 utils/augmentations.py 里的原版函数,这里不展开。参数说明:conf_thres 默认 0.5,实际项目根据你对误检的容忍度调整,比赛里我一般设为 0.4;iou_thres 控制 NMS 的阈值,0.45 是经验值。
5.2 用 torch.hub 加载本地权重,不在命令行里绕圈
YOLOV5 官方支持 torch.hub 加载,但很多人不知道可以完全离线加载。当你已经把工程和权重文件下载好时,用 source='local' 方式加载最省事。
import torch model = torch.hub.load( local_path='./yolov5-6.1-zh', source='local', model='custom', path='weights/best.pt', force_reload=True ) results = model('./test.jpg') print(results.pandas().xyxy[0])代码逻辑:local_path 指向中文注释包的根目录,source='local' 告诉 torch.hub 不需要联网。model='custom' 是加载自定义权重的标准写法,path 指定权重文件位置。results 对象里封装了所有检测结果,pandas().xyxy[0] 会打印一张表格,包含检测框坐标、置信度和类别 id。force_reload=True 保证每次重新加载模型,避免 Jupyter 里用了旧的缓存。
torch.hub 方式的好处是它会自动处理图片预处理和 letterbox,输出坐标直接对应原图,适合快速验证。缺点是你必须保留整个工程目录,不能只拿一个权重文件去运行。如果比赛要部署到演示机上,我建议还是用 5.1 的方式写一个独立的推理类,把工程路径封装在类内部,这样对外只暴露一个函数,演示时不会报找不到模块的错误。
5.3 处理大批量图片时的显存管理与结果保存
批量推理最容易碰到两个问题:图片一次性读入导致 OOM,以及结果保存时文件名混乱。我一般用循环+逐张推理,并加上进度条和异常处理。
import os, glob, cv2 from tqdm import tqdm from model_api import load_model, infer_image model, device, imgsz = load_model(weights='best.pt', device='0', imgsz=640) image_folder = './test_images' output_folder = './test_output' os.makedirs(output_folder, exist_ok=True) paths = sorted(glob.glob(os.path.join(image_folder, '*.jpg'))) for p in tqdm(paths, desc='infer'): img = cv2.imread(p) if img is None: print(f"跳过无法读取的文件: {p}") continue det = infer_image(model, img, device, imgsz) # 画框 for x1, y1, x2, y2, conf, cls in det: cv2.rectangle(img, (int(x1), int(y1)), (int(x2), int(y2)), (0,255,0), 2) cv2.putText(img, f'{int(cls)}:{conf:.2f}', (int(x1), int(y1)-5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,0,255), 2) cv2.imwrite(os.path.join(output_folder, os.path.basename(p)), img)这段代码的逻辑:用 glob 收集所有 jpg 路径,tqdm 显示循环进度。每张图片单独读、单独推理、单独保存,内存占用始终是一个 batch 的量级。infer_image 里已经用了 torch.no_grad(),梯度不会累积,显存稳定。不过,如果图片尺寸小于 imgsz,模型内部的 letterbox 会先补边,返回的 det 坐标是 letterbox 后图像上的坐标,直接画在原始图片上会偏移。需要处理的话,可以把 letterbox 操作拿到函数外面,再用原始坐标逆变换回去。
对于大规模数据集,还能考虑把 batch_size 调大,在 infer_image 中支持 batch 输入。先对每张图做 letterbox,padding 成一个固定的 batch,一次推理多个样本。但要注意,不同图片的 letterbox 补边参数不同,输出坐标也要分别映射。我一般只在压力测试时才这么做,比赛演示用逐张循环完全足够。
6. 验证与迭代:用 TensorBoard 和混淆矩阵判断模型有没有“真学会”
训练结束后,很多人只看训练日志里最终 loss 很小就高兴地收工,这是不严谨的。loss 只能反映训练过程的稳定程度,真正能说明模型好坏的是验证集上的 mAP 和混淆矩阵。
在训练输出目录下,YOLOV5 会保存 TensorBoard 事件文件。启动它:
tensorboard --logdir runs/train/my_experiment_001浏览器打开后,重点看三个曲线:train_loss、val_loss、metrics/mAP_0.5。如果 val_loss 先降后升,而 train_loss 持续下降,说明模型开始过拟合,可以提前停掉而不是硬跑完 100 个 epoch。如果 mAP 曲线在某个节点后变平,再训下去也只是浪费时间。
然后看混淆矩阵。训练目录会生成 confusion_matrix.png,横纵坐标是类别名。矩阵对角线越亮越好,如果某一对类别之间的色块特别亮,说明模型经常把它们搞混。比如在安全帽数据集里,模型总是把“戴安全帽”的人识别成“未戴”,那你就要检查这两类样本数量是否均衡,或者是否出现了标注错误。中文注释包里在 utils/metrics.py 里对混淆矩阵的计算做了中文说明,你可以在推理阶段自己调用它,用在比赛报告里作为模型分析。
再用 val.py 独立评估一遍,这是最稳妥的验证方式:
python val.py --data data/custom.yaml --weights runs/train/my_experiment_001/weights/best.pt --img 640这条命令会输出每个类别的 precision、recall 和 mAP。如果验证集的 mAP 比训练过程显示的 mAP 低很多,说明模型在验证集上泛化能力差,数据划分可能有问题,或者增强参数过强。我通常在训练完成后立刻跑一次 val.py,把结果截图存档,作为调参的依据。
我第一次用 YOLOV5 时,只看 loss 降到 0.0 几就兴奋得不行,结果一测实际图片全是漏检。后来才明白,loss 只是过程指标,mAP 才是最终参考。从那以后,我每次训练结束都强制自己先跑一遍 val.py,再看混淆矩阵,最后才决定要不要调超参数。这个习惯帮我少走了很多弯路。希望帮到你。
本文还有配套的精品资源,点击获取