ARTICLE DETAIL

资讯详情

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

YOLOv5行人检测实战:数据集校验、模型微调与部署避坑指南

YOLOv5行人检测实战:数据集校验、模型微调与部署避坑指南 简介目标检测是计算机视觉中最基础也最常被问到的技术方向之一而YOLO系列凭借其效率与精度的平衡成为工程落地的首选框架。在实际项目中用户往往拿到现成的训练权重和标注数据却因数据集格式错误、环境版本不匹配、推理参数设置不当等问题反复翻车。本文从目标检测的基本原理出发详细拆解YOLOv5行人检测的完整实践链路包括YOLO标注格式的归一化规则、数据完整性校验脚本、基于预训练权重的迁移学习方法、推理阈值调优策略以及边缘端部署时的算子兼容性注意点。这些技术不仅适用于行人检测也可泛化到其他单类别目标检测任务。在智慧安防、交通监控与园区巡检等场景中掌握数据治理与模型微调的关键能力远比直接套用权重更重要。文章将带领读者从解压数据集开始逐步走通模型推理、重训与部署的每一个环节避开那些最容易让人卡壳的深坑。1. 拿到一个行人检测压缩包先别急着解压跑起来做视觉的同行应该都有过这种经历项目催得紧领导丢过来一个写着“YOLOv5行人检测训练权重 3000行人数据集”的压缩包让你一周内把它跑通并部署。这个标题里的yolov5-6.0-person_detect.zip指向的东西很明确一份基于 YOLOv5 6.0 版本训练好的行人检测权重外加 3000 张已经标注好的行人图片。它能解决的是“我要一个能直接用的行人检测模型不想从零打标、不想花两周调参”的问题——适合做安防、智慧交通、园区巡检、边缘盒子项目的开发者也适合刚接触目标检测、想用现成数据快速跑通全流程的学生。但我要先说一个反直觉的结论这份压缩包的真正价值不在那个.pt权重文件里而在那 3000 张标注数据的组织方式上。权重是训练的结果是“鱼”数据集的目录结构、标注格式、类别分布才是“渔”。如果你只是想调个接口做检测权重够用但如果你想把它适配到自己的场景比如检测广场上的行人、厂区里的工人就必须吃透这个数据集的标注约定。接下来我按落地顺序把这个包从解压到部署的每一步拆开讲包括参数怎么设、哪些地方最容易翻车。2. 解包与数据校验先把“底料”检查一遍再动手2.1 压缩包的常见结构权重、数据、脚本的分工虽然我没看过这个压缩包的原始内容但按 YOLOv5 6.0 项目的一般组织方式这种“训练权重 数据集”的包通常会按下面这种结构归档。我建议你解压后先对照这个结构不要急着跑任何命令。目录/文件作用需要注意的点weights/或runs/train/exp/weights/存放训练好的.pt权重文件可能有best.pt和last.ptbest.pt按验证集指标挑选last.pt是最后一轮推理优先用best.ptdatasets/或person_data/3000 张行人图片及对应标签重点检查images/和labels/是否一一对应data.yaml或person.yaml数据集配置文件声明类别数、类别名、训练/验证路径里面nc: 1表示单类别names: [person]是行人train.py/detect.py/val.pyYOLOv5 6.0 的核心脚本如果压缩包里没带需要从官方仓库补齐requirements.txt依赖清单6.0 版本对 torch 版本敏感后面会讲解压后先看data.yaml这是理解整个包怎么被训练出来的钥匙。常见做法是里面写着path: 数据集根目录、train: images/train、val: images/val、nc: 1、names: [person]。如果nc不是 1比如写了 80COCO 类别数说明这个权重可能是全类别迁移过来的行人只是其中一类后面做微调时要注意别把类别索引搞乱了。2.2 标注格式与目录约定YOLO txt 文件里的四个数字行人检测在这里用的是 YOLO 系列的标准标注格式也就是每个图片对应一个同名.txt文件放在labels/下。txt 里每一行代表一个目标格式是class_id x_center y_center width height注意x_center、y_center、width、height这四个值统统是归一化的范围 01用目标实际像素坐标除以图片宽高得到。比如一张 1920×1080 的图里一个行人 bbox 左上角是 (960, 540)宽 200高 400那么这一行应该写成0 0.5521 0.5111 0.1042 0.3704其中0.5521 (960 200/2) / 19200.5111 (540 400/2) / 10800.1042 200 / 19200.3704 400 / 1080。我见过不止一次有人把归一化坐标写成了像素坐标训练时模型 Loss 直接变成 NaN。所以拿到压缩包后的第一件事就是写脚本抽查 labels 里的数值是否都在 01 区间。另外要确认images/和labels/的子目录结构是对齐的常见做法是person_data/ ├── images/ │ ├── train/ │ │ ├── 0001.jpg │ │ └── ... │ └── val/ │ ├── 0901.jpg │ └── ... └── labels/ ├── train/ │ ├── 0001.txt │ └── ... └── val/ ├── 0901.txt └── ...图片和标注文件的文件名必须完全一致扩展名不同没关系如果0001.jpg没有对应的0001.txt训练时 YOLOv5 会静默跳过那张图导致有效数据量缩水这属于比较坑的“静默失败”。2.3 用一段脚本校验数据集完整性三分钟排除低级错误这一步真的有用能省下后面训练时排查黑匣子的大把时间。把下面这个脚本保存为check_dataset.py放在压缩包解压目录的父目录下按你的实际路径修改后运行import os img_dir person_data/images/train label_dir person_data/labels/train imgs {os.path.splitext(f)[0] for f in os.listdir(img_dir) if f.endswith((.jpg, .jpeg, .png))} labels {os.path.splitext(f)[0] for f in os.listdir(label_dir) if f.endswith(.txt)} # 1. 找出没有标注的图片 missing_label imgs - labels # 2. 找出没有图片的标注 missing_img labels - imgs print(f图片总数: {len(imgs)}标注总数: {len(labels)}) print(f缺少标注的图片: {len(missing_label)}示例: {list(missing_label)[:3]}) print(f缺少图片的标注: {len(missing_img)}示例: {list(missing_img)[:3]}) # 3. 抽查标注内容是否合法 empty_count 0 out_of_range_count 0 for name in labels: txt_path os.path.join(label_dir, name .txt) with open(txt_path, r) as f: for line in f: parts line.strip().split() if len(parts) ! 5: print(f{name}.txt 格式异常: {line.strip()}) continue try: cls, x, y, w, h int(parts[0]), *map(float, parts[1:]) except ValueError: print(f{name}.txt 数值异常: {line.strip()}) continue if x 0 or y 0 or w 0 or h 0 or x 1 or y 1 or w 1 or h 1: out_of_range_count 1 if cls ! 0: print(f{name}.txt 类别号不是0: {line.strip()}) if w * h 1e-6: # 归一化面积太小可能标注错误 empty_count 1 print(f越界标注数: {out_of_range_count}面积异常数: {empty_count})这段脚本干了三件事第一检查图片与标注的文件名是否一一对应避免训练时数据被静默跳过第二检查每行是否恰好 5 个字段、数值是否在 01 区间第三检查类别号是否为 0单类别数据集里类别号只能是 0。如果脚本报出大量越界标注说明这个数据集可能被脚本批量处理过且处理错了不要拿它直接训练先修数据。参数说明person_data/images/train路径要按你实际解压位置改如果你的数据集用了.png或.bmp把endswith的元组补上就行。判断w * h 1e-6是因为归一化后如果目标宽度高度都小于 0.001相当于在原图里不到 2 个像素这种标注基本是噪声。跑完这段你对这份数据集的信心会完全不一样。3. 用训练权重跑通推理从单张图片到视频流3.1 环境安装YOLOv5 6.0 对 torch 版本的硬性要求YOLOv5 6.0 是 2022 年初发布的版本它和之后的 7.0、ultralytics 统一版有挺大差别。最大的坑在依赖6.0 的requirements.txt里对torch1.8.0的要求其实很宽松但实际跑下来torch 2.x 和 6.0 的某些算子存在兼容问题症状是推理时直接报RuntimeError: CUDA error: no kernel image is available for execution on the device。我一般建议直接用 torch 1.12.1 torchvision 0.13.1 CUDA 11.6 这个组合验证过最稳。创建虚拟环境并安装conda create -n yolov5 python3.8 conda activate yolov5 pip install torch1.12.1 torchvision0.13.1 --index-url https://download.pytorch.org/whl/cu116 pip install -r requirements.txt如果你的显卡是 RTX 30 系以上这个版本组合能正常发挥性能如果是 RTX 40 系torch 1.12.1 的 CUDA 11.6 可能不支持新架构需要换成 torch 1.13.1 CUDA 11.7。requirements.txt从哪来如果压缩包里没带从 YOLOv5 官方 6.0 分支拉一份同样的文件即可核心依赖是numpy1.22.4、opencv-python4.1.2、matplotlib3.2.2、pillow1.0。其中 numpy 版本别装成 2.xYOLOv5 6.0 的代码里用了np.int这种老接口numpy 2.x 会直接报AttributeError: module numpy has no attribute int。3.2 用 detect.py 跑单张图片最小推理命令环境装好后把压缩包里的权重文件假设叫best.pt放到项目根目录然后跑python detect.py --weights best.pt --conf-thres 0.4 --iou-thres 0.45 --source test.jpg --project runs/infer --name person_result解释一下这条命令背后的逻辑detect.py会加载best.pt读取里面保存的模型结构、类别名、训练时的图片尺寸一般是 640 或 480然后把test.jpg做 letterbox 缩放保持宽高比的填充缩放不是拉伸前向推理得到一组候选框。--conf-thres 0.4表示置信度低于 0.4 的框全部丢掉--iou-thres 0.45表示 NMS 时两个框 IoU 超过 0.45 就合并为一个。这两个参数是行人检测场景里最需要反复调的东西下一节细说。参数说明--weights换成你自己解压出来的.pt文件名--source可以是单张图、目录、视频文件或摄像头编号--project和--name控制结果输出路径输出在runs/infer/person_result/下里面除了标注好的图片还有一个记录了运行参数和耗时的results.txt。第一次跑推荐用 CPU在命令后加--device cpu确认流程没问题再切 GPU省得环境问题混淆了判断。3.3 推理参数调优conf-thres 和 iou-thres 对行人尺度的坑行人检测和通用目标检测在参数上有明显的不同。行人目标通常“窄而高”在远距离下像素占比很小比如 100 米外的人可能只有 20×50 像素。这种情况下conf-thres设太高会漏检设太低会出现大量误检——把树干、电线杆、广告牌上的人影都框出来。我的经验值是这样的近景行人像素高度 100px用 conf-thres 0.5 没问题远景小目标建议降到 0.250.3同时把 iou-thres 提高一点到 0.5。因为小目标之间容易重叠NMS 阈值高了会误删真框。下表是我在不同场景下试过的组合可以作为起步参考场景conf-thresiou-thres说明密集人群、目标互相遮挡0.30.5宁可多框不要漏检开阔道路、行人稀疏0.50.45减少误检框比较干净监控摄像头固定机位、远中近混合0.350.5需要结合后续跟踪算法做平滑边缘设备树莓派等推理0.30.45算力受限减少后处理时间注意--iou-thres对应的是 NMS 的 IoU 阈值不是检测框和真值匹配用的那个。这个值越大NMS 保留的框越多但重叠的冗余框也会变多值越小两个重叠目标可能被合并成一个。在行人这种“竖长条”目标上我踩过的坑是iou-thres 设成 0.3 时两个并肩行走的人被合成了一个框跟踪算法直接跟丢。所以密集人群场景千万别把 iou-thres 调太底。3.4 摄像头或视频流检测把 detect.py 改成你的实时管线detect.py本身支持直接传视频文件或摄像头编号比如--source 0但它没有跟踪功能人脸框会剧烈抖动。如果你想在视频流里用这个行人检测权重常见做法是写一个简单的推理循环把检测结果交给跟踪器import cv2 import torch from models.experimental import attempt_load from utils.general import non_max_suppression from utils.augmentations import letterbox device torch.device(cuda if torch.cuda.is_available() else cpu) model attempt_load(best.pt, map_locationdevice) model.eval() cap cv2.VideoCapture(test.mp4) while True: ret, frame cap.read() if not ret: break # letterbox 保持宽高比输入尺寸 640 img, ratio, pad letterbox(frame, 640, stride32, autoTrue) img img[:, :, ::-1].transpose(2, 0, 1) # BGR转RGBHWC转CHW img torch.from_numpy(img.copy()).float() / 255.0 img img.unsqueeze(0).to(device) with torch.no_grad(): pred model(img)[0] det non_max_suppression(pred, conf_thres0.35, iou_thres0.45)[0] if det is not None: for *xyxy, conf, cls in det: # xyxy 是原图坐标需要减去 letterbox 的 padding 再除以缩放比例 x1, y1, x2, y2 map(int, [xyxy[0] - pad[0], xyxy[1] - pad[1], (xyxy[2] - pad[0]) / ratio[0], (xyxy[3] - pad[1]) / ratio[1]]) cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imshow(person detect, frame) if cv2.waitKey(1) 0xFF ord(q): break这里最关键的是坐标还原letterbox把原图缩放并填充到 640×640输出的检测框坐标也是 640 坐标系下的必须减掉 paddingpad 是左/上填充量再除以ratio缩放比才能画回原图上。很多人直接画框结果框的位置全部偏右下就是这个 padding 没减。逻辑不复杂但属于那种“错得莫名其妙”的点建议先拿单张图验证坐标还原是否正确再上视频流。4. 用 3000 张行人数据微调让模型适配你的专属场景4.1 为什么 3000 张够用从零训练 vs 迁移学习的差别很多人拿到“3000 张行人数据集”第一反应是就这么点图能训练出什么这里有个关键认知我们不是用这 3000 张图从零训练而是在 COCO 预训练权重的基础上微调。COCO 数据集里有 118K 张训练图、80 个类别其中 person 类别的样本数以万计。YOLOv5 的官方权重已经学会了“如何提取特征、如何回归边框”的通用能力我们手里这 3000 张行人图只是让它把注意力更集中到行人这个类别、适应当前场景的光照和视角。从零训练--weights 需要的数据量级是几万到几十万张图否则必然过拟合而迁移学习--weights yolov5s.pt之类在 3000 张图上可以训练出能用的模型前提是你的数据分布和你的部署场景不要差太远。比如你的摄像头装在商场入口俯视视角行人密度高但压缩包里的 3000 张图如果都是平视街景那你需要额外补充俯视样本再做微调否则模型在你场景里会漏检严重。4.2 把标注转成 YOLO 格式转换脚本与四个边界坑如果压缩包里的数据集不是 YOLO 格式比如是 VOC 的 XML 或 COCO 的 JSON你需要先转格式。这里给一个 VOC XML 转 YOLO txt 的脚本这种需求最常见import xml.etree.ElementTree as ET import os def convert_voc_to_yolo(xml_path, out_dir, class_names): tree ET.parse(xml_path) root tree.getroot() img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) txt_name os.path.splitext(os.path.basename(xml_path))[0] .txt with open(os.path.join(out_dir, txt_name), w) as f: for obj in root.iter(object): cls obj.find(name).text if cls not in class_names: continue cls_id class_names.index(cls) box obj.find(bndbox) x1 float(box.find(xmin).text) y1 float(box.find(ymin).text) x2 float(box.find(xmax).text) y2 float(box.find(ymax).text) # 边界防护超出图片边界的坐标要截断不能直接归一化 x1, y1 max(x1, 0), max(y1, 0) x2, y2 min(x2, img_w), min(y2, img_h) if x2 x1 or y2 y1: continue x_center ((x1 x2) / 2) / img_w y_center ((y1 y2) / 2) / img_h w (x2 - x1) / img_w h (y2 - y1) / img_h f.write(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}\n) # 用法示例 class_names [person] xml_dir voc_annotations/ yolo_out yolo_labels/ os.makedirs(yolo_out, exist_okTrue) for xml_file in os.listdir(xml_dir): if xml_file.endswith(.xml): convert_voc_to_yolo(os.path.join(xml_dir, xml_file), yolo_out, class_names)转换脚本里的四个边界坑全是血泪经验一是坐标截断有些标注工具的 bndbox 会超出图像边界比如 xmax 写成 1921 而图片只有 1920 宽不截断归一化后会大于 1训练时直接报错二是过滤无效框截断后如果宽高为 0 或负数要跳过三是默认 640 尺寸下小目标的权衡——如果你的原图是 4K 高清行人目标相对较小转换没问题如果原图本身只有 320×240那归一化后的 bbox 非常小模型学起来很吃力这种情况优先换数据集而不是硬调参数四是class_names.index(cls)必须与data.yaml里的names顺序完全一致否则类别错位一个常见翻车是 data.yaml 里写names: [person, background]而脚本里 index 是 0训练出来模型把人都识别成背景。4.3 用 train.py 微调从已有权重继续训练而不是从头开始数据准备好后训练命令如下python train.py --data person.yaml --weights yolov5s.pt --batch-size 16 --epochs 100 --img 640 --hyp hyp.scratch-low.yaml --patience 10 --device 0这个命令的每个参数都有讲究我拆开讲--data person.yaml你的数据集配置文件里面train:和val:必须是绝对路径或相对path字段的路径很多人漏写path字段导致训练时找不到图片。--weights yolov5s.pt这是整个命令的灵魂。yolov5s.pt是 YOLOv5 官方在 COCO 上预训练的权重模型会把学到的通用特征迁移到你的行人数据上。如果你想在压缩包自带的best.pt基础上继续微调把这里换成best.pt即可。但注意如果两者的类别数量不一致官方是 80 类你的只有 1 类YOLOv5 会帮你做权重裁剪只保留类别相关的部分这个操作是自动的会打印一行日志。--epochs 1003000 张图训练 100 轮足够看到收敛趋势。如果 30 轮后 val mAP 还在上升加 50 轮不要在 100 轮硬停。--hyp hyp.scratch-low.yaml这个文件控制数据增强强度。hyp.scratch-low.yaml是轻增强适合数据质量好、分布典型的场景如果你的数据有大量小目标用hyp.scratch-high.yaml增强更强但训练时间也变长。--patience 10早停轮数超过 10 轮验证集指标没有提升就自动停止省时间。训练过程中建议每 10 轮看一眼runs/train/exp/下的results.png里面有train_loss、val_loss、mAP0.5、mAP0.5:0.95四条曲线。如果训练损失一直在降、验证损失却在上升就是过拟合了说明 3000 张数据不够模型记需要加大增强强度或减小模型规模换成yolov5n.pt或yolov5s.pt。4.4 超参数与数据增强hyp 文件里能改变命运的几个值hyp文件是 YOLOv5 训练的黑匣子所在也是“玄学”浓度最高的地方。hyp.scratch-low.yaml里我一般会先动这四个参数# hyp.scratch-low.yaml 部分内容 lr0: 0.01 # 初始学习率微调时可以降到 0.005 mosaic: 1.0 # mosaic 增强概率1.0 表示每张图都做 mixup: 0.0 # mixup 增强概率行人场景建议开 0.1 fliplr: 0.5 # 水平翻转概率行人检测建议保持 0.5使用预训练权重微调时lr0建议从 0.01 降到 0.005原因是预训练模型已经收敛到了一个比较好的局部最优学习率太大会把学到的通用特征冲掉。mosaic是 YOLOv5 最核心的数据增强把 4 张图拼接成一张能大幅提升小目标检测能力——但它对行人这种“长条状”目标有个副作用拼接时不同图片里的行人可能在边界处被截断造成标注框超出拼接图边界。YOLOv5 会做边界采样处理但mosaic: 1.0时这个截断很常见模型学到的是“半个人”的特征。我的调参习惯是mosaic保持 1.0但把mixup从 0.0 提到 0.1。mixup 是两张图按比例混合对行人这种刚性目标鲁棒性影响较小能有效抑制过拟合。fliplr保持 0.5 没问题因为行人左右对称水平翻转不会引入语义错误。如果你部署场景是固定的监控机位行人方向比较单一比如都从左往右走可以把fliplr调到 0.2减少增强引入的方向偏差。5. 避坑指南行人检测训练与推理的 5 个典型翻车现场5.1 训练 Loss 是 NaN学习率太大或数据里有脏标注现象训练到第 3、4 轮终端打印的box_loss、obj_loss突然变成nan训练直接卡死。原因最常见的是学习率配合大 batch 时梯度爆炸其次是labels里出现了非法值——比如某个.txt里类别号写成 5而你的nc: 1或者坐标里有负数。我在解压数据后第一次训练就遇到过check_dataset.py查出有 7 个标注文件的坐标写成像素值没归一化。解决第一重新跑一遍第 2 章的校验脚本把非法标注修掉或删除对应文件第二如果数据没问题把lr0从 0.01 改成 0.005同时在命令里加--cos-lr让学习率平滑下降第三如果还报错就检查 GPU 显存是不是不够导致 OOM用--batch-size 8试探。5.2 GPU 利用率上不去数据传输瓶颈而不是算力瓶颈现象训练时nvidia-smi显示 GPU 利用率在 30%50% 之间波动显存占用正常但 epoch 耗时很长。原因YOLOv5 6.0 默认的 dataloader 在 CPU 上做解码和增强如果你的 CPU 核数少、机械硬盘读取慢GPU 就在空等数据。3000 张图的数据集还不明显如果后续扩展到几万张这个瓶颈会非常突出。解决在train.py命令里加--workers 8默认是 8但 Windows 上无效Linux 有效并确认数据放在 SSD 上。如果还不行就把增强参数调低比如--hyp hyp.scratch-low.yaml换成自建的低增强 hyp——至少能让mosaic的处理耗时降下来。5.3 推理时把所有行人漏掉权重文件与训练配置不配套现象拿压缩包里的best.pt跑test.jpg结果一张图一个框都没有但置信度阈值已经调到 0.1。原因best.pt保存的是训练时的模型结构如果压缩包里的权重是在 YOLOv5 6.0 上训练的却拿 5.0 版本的detect.py去加载模型结构不匹配会导致输出张量完全错乱——YOLOv5 5.0 和 6.0 在 Head 输出的编码方式上有差异。另一个常见原因是best.pt对应的类别名不是 person比如它是 COCO 全类别的权重而你用detect.py默认的 COCO 类别名列表逻辑上应该能框出来但如果这个权重是定制训练的、只有person一个类别你需要检查best.pt里的model.names属性。解决在 Python 里加载权重并打印类别import torch model torch.load(best.pt, map_locationcpu) print(model[model].names) # 查看类别名列表如果打印结果是{0: person}但detect.py跑出来没框那就是detect.py的类别列表和权重不一致。6.0 版本里detect.py会从权重里自动读取names一般不会错如果你是从旧版脚本改的务必检查这一条。5.4 小目标行人检测效果差数据分布与模型尺度不匹配现象远距离的行人比如 50 米外检测不到近距离行人能检测到但框的位置上下偏移明显。原因YOLOv5 的检测 Head 有三个尺度P3、P4、P5分别负责小、中、大目标。如果压缩包里的 3000 张图行人占比都很大比如每张图 12 个人占画面 20% 以上模型在 P5 大尺度上的权重会偏高P3 小目标分支学习不足。你的部署场景如果行人很小自然翻车。解决用你场景里的真实数据补训练。从监控视频里抽帧挑行人像素高度在 3080px 的图用标注工具LabelImg 或 X-AnyLabeling标几十张到一百张混入原数据集重训。这个补数据的过程是不可避免的没有任何公开数据集能完全覆盖你的摄像头视角除非你的安装位置和采集场景一模一样。5.5 同一行人产生大量重复框NMS 阈值设错或后处理分支没走对现象一张图里同一个行人被框了 34 次框之间重叠度很高但就是没被合并。原因non_max_suppression的iou_thres设置过高比如 0.7 以上或者你修改了detect.py但没调用 NMS 直接输出了原始预测。另一个可能你的模型输入尺寸设得很大比如--img 1280锚框数量爆炸NMS 的输入候选框太多导致算法行为异常。解决iou_thres调回 0.450.5 区间确认在model(img)[0]之后一定调用了non_max_suppression函数。如果框还是重合把--img降到 640这个尺寸对行人检测足够还快一倍。6. 验证与进阶从 mAP 指标到边缘端部署的最后一公里6.1 用 val.py 做可信的评估mAP 不是全部看看 PR 曲线训练完成后不要只看训练日志里最后几行 mAP。用命令单独跑一遍验证集python val.py --data person.yaml --weights best.pt --batch-size 8 --conf-thres 0.001 --iou-thres 0.6 --task val --project runs/val --name person_eval注意--conf-thres 0.001是为了在评估时“宁滥勿缺”让 PR 曲线能看到完整趋势。输出会有一个PR_curve.png如果曲线右下角塌陷严重召回率高时精度掉得厉害说明模型在低置信度区间输出大量误检部署时conf-thres就得设高一些比如 0.5。同时看F1_curve.png那个图的峰值位置就是你部署时最优的置信度阈值比拍脑袋定的 0.4 可信得多。6.2 轻量化部署导出 ONNX 到 RKNN 或树莓派如果你的部署目标是边缘盒子比如 RK3568或树莓派6.0 版本官方export.py可以直接导出 ONNXpython export.py --weights best.pt --img 640 --batch-size 1 --include onnx --simplify导出的best.onnx可以用onnxruntime做 CPU 推理也可以再转成 RKNN瑞芯微的工具链或 TensorRT。量化时注意RK3568 的 RKNN 工具对 YOLOv5 6.0 的某些层比如 Focus 层支持不完整6.0 版本可以用--include onnx导出时指定--opset 12来减少算子兼容性问题。我自己的做法是先在 RKNN 的模拟器上跑通再烧录到开发板上因为真实板子的 NPU 驱动版本各不相同模拟器通过只代表算子支持没问题实际帧率另说。树莓派 5 上部署onnxruntime 的 ARM 版本在 CPU 上跑 YOLOv5s640 输入大概能到 58 FPS适合低帧率巡检或配合跟踪算法做降频检测。6.3 把检测和 ROS 对接无人小车场景的注意点如果你做的是 ROS 无人小车把检测节点封装成 ROS Node 时我吃过一个亏摄像头话题的图像是 1920×1080但 YOLOv5 的letterbox每次都会重新计算缩放参数导致连续帧间检测框的抖动比预期更严重。解决方案是固定输入尺寸并缓存ratio和pad不要每帧都算。另外ROS 回调里直接跑推理会阻塞其他话题订阅正确做法是开一个独立线程处理推理只把结果发布到detect_result话题里。这些和模型本身关系不大但直接影响系统稳定性。写到最后想说YOLOv5 行人检测这套链路不算新但每一年都有人在这几个老坑里翻车。我自己的习惯是——任何数据集和权重到手先花三十分钟做完整性和分布校验再跑通最小推理闭环最后才谈训练和调参。这个顺序值得坚持。希望今天的分享帮到你省下这几天的探索时间。本文还有配套的精品资源点击获取
返回列表