ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

YOLOv5目标检测实战:环境搭建、自定义训练与部署避坑全指南

YOLOv5目标检测实战:环境搭建、自定义训练与部署避坑全指南 简介面向具备一定Python和计算机视觉基础的入门研究人员或工程技术人员这份以实操为导向的YOLOv5搭建与目标检测指导文档系统梳理了从环境准备到首次实例检测的完整流程。内容兼顾CPU与GPU两种硬件场景详细说明了操作系统选择、Python版本要求以及CUDA、cuDNN等依赖组件的配置要点并给出基于PyTorch的安装备忘帮助新手高效搭建可运行框架。在完成基础检测后文档进一步介绍利用自定义数据集训练模型的高级操作包括数据格式整理、模型配置文件修改以及训练脚本调用适合有定制化识别需求的研究者参考。资源以docx格式文档呈现共1个文件压缩包仅19KB内容精炼便携。目前已有238人学习浏览文档中不仅对每条命令和参数加以解释还针对常见环境问题提供排查思路可直接作为教学材料或工程部署手册反复查阅。1. 先别急着跑demo环境才是第一个玄学你在网上搜“YOLOv5搭建与目标检测详解”时大概率还没跑通一次完整的训练或者跑通了demo但一换自己的数据集就翻车。我的经验是这个项目真正的难度不在模型结构而在环境匹配和数据集质量。Ubuntu 20.04 Python 3.8 CUDA 11.2这套组合能跑通不代表Windows Python 3.10 CUDA 12.x也能直接跑。这篇笔记从建环境开始一步步讲到推理、训练自己的数据集、以及部署路上那些没人明说的坑。适合刚入门目标检测的算法工程师、毕设做检测方向的学生、以及要把模型塞进边缘设备的人。先把它跑起来再去谈调优这是所有后续工作的地基。2. 搭建YOLOv5环境GPU、CUDA与PyTorch的三角匹配2.1 为什么不建议直接用requirements.txt一把梭YOLOv5的requirements.txt里列了torch、torchvision、opencv等依赖但没告诉你torch要装哪个版本。直接pip install -r requirements.txt在多数情况下会给你装一个CPU版PyTorch或者装了一个与显卡驱动不匹配的CUDA版本训练时要么慢到怀疑人生要么直接报AssertionError: Torch not compiled with CUDA enabled。PyTorch与CUDA的版本对应关系可以用这个查法先运行nvidia-smi看驱动支持的最高CUDA版本再打开PyTorch官网查对应版本。表1是几组我实测过的稳定组合覆盖了从同学的老笔记本到服务器显卡的情况。显卡举例驱动支持的最高CUDAPyTorch安装命令备注GTX 1660 / RTX 3060CUDA 12.xpip install torch2.1.0 torchvision0.16.0 --index-url https://download.pytorch.org/whl/cu121大多数个人机器稳定Tesla V100 / RTX 2080 TiCUDA 11.xpip install torch1.13.1cu117 torchvision0.14.1cu117 --extra-index-url https://download.pytorch.org/whl/cu117老服务器常用于跑yolov5纯CPU机器仅学习无pip install torch torchvision能跑推理训练很慢我个人的习惯是先确认显卡驱动版本驱动偏老就选PyTorch 1.13 CUDA 11.7驱动较新就选PyTorch 2.1及以上。YOLOv5官方在持续适配新版本但不用追新能满足需求就行。2.2 从零创建conda环境把Python版本和依赖一起锁死为什么用conda而不是venv因为深度学习的环境里Python版本、CUDA运行时、cuDNN经常要切换venv只能换库不能方便地换Python版本conda则能一并管理。下面这套是经过反复踩坑后固定的流程。conda create -n yolov5 python3.8 -y conda activate yolov5 git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt pip install torch1.13.1cu117 torchvision0.14.1cu117 -f https://download.pytorch.org/whl/torch_stable.html逻辑说明第三行克隆的是官方源码仓库之后所有训练和推理都在这套源码里进行第五行先把requirements装一遍再单独指定PyTorch版本后者会覆盖前者的默认安装。为什么不只执行最后一行因为requirements里还有其他依赖是PyTorch之外的东西比如opencv-python、tqdm、seaborn一把梭能少折腾。参数说明Python版本我固定用3.8原因是很多老代码在3.10以上会出现语法告警或依赖不兼容而且conda环境里3.8很成熟CUDA版本通过PyTorch的安装参数隐式决定不需要单独安装CUDA Toolkit。2.3 验证搭建结果torch.cuda.is_available()的三种结局环境搭完不要急着跑训练先验证一下。一句话这一步走通后面的路就顺一半。import torch print(PyTorch:, torch.__version__) print(CUDA available:, torch.cuda.is_available()) if torch.cuda.is_available(): print(GPU name:, torch.cuda.get_device_name(0))逻辑说明这段代码会打印PyTorch版本、CUDA是否可用、以及显卡型号。如果CUDA available输出的是False恭喜你后面每跑一次训练都会多花一笔时间在报错上。False的三种常见原因PyTorch装成了CPU版重新按上一节的命令装GPU版显卡驱动太老导致PyTorch检测不到CUDA更新驱动装了多个Python环境pip装到了别的环境用which pip和which python检查是否都在conda环境里。弄完环境后我习惯跑一个10秒的GPU烧机测试python -c import torch; atorch.randn(1000,1000).cuda(); print((aa).sum())。能输出tensor值说明环境没问题。这一步过关就可以进入下一步真正跑一次推理。3. 跑通detect.py从源码到第一次看到检测框3.1 源码结构拆解哪些文件是你真正要碰的YOLOv5的仓库看起来文件很多但90%的日常操作只涉及三个目录。理清它们的职责后续改东西心里就有底。文件/目录作用日常操作频率detect.py推理脚本用训练好的权重检测图片、视频、摄像头高train.py训练脚本所有训练参数在这里传高models/hub/yolov5s.yaml网络结构定义改通道数或深度核心看它低data/数据集配置和类别文件如coco128.yaml高utils/后处理、数据增强、loss计算等工具函数中初学者最容易搞混的是models/hub/yolov5s.yaml和data/coco128.yaml前者是网络结构后者是数据集描述两者都不是单独运行的脚本。改错地方最常见的就是把数据集的类别数写进了网络结构配置文件导致通道不匹配报错。拿一张YOLOv5网络结构图对照着看代码时重点盯三个部分Backbone的CSP结构、Neck的PANet结构、Head的Detect输出层。3.2 第一次推理下载权重并运行detect.py跑推理之前先下载官方预训练权重。我习惯放在项目根目录下建一个weights/文件夹统一管理这样后文训练、导出模型、部署时路径都清晰。wget -P weights/ https://github.com/ultralytics/yolov5/releases/download/v6.1/yolov5s.pt python detect.py --weights weights/yolov5s.pt --source data/images --conf-thres 0.25 --iou-thres 0.45逻辑说明第一行下载YOLOv5s的预训练权重这是COCO数据集上训练的版本能识别80类常见物体。第二行用这个权重对data/images目录下的所有图片做推理输出结果保存在runs/detect/exp/下。参数说明--conf-thres是置信度阈值只有置信度大于0.25的检测框才会输出。调低这个值能看到更多框但也会带入更多误检--iou-thres是NMS阶段用来合并重叠框的阈值取0.45是官方默认值。刚上手不理解没关系第三节讲后处理时会解释这两个值各自卡在哪一步。--source除了传目录还支持单张图片路径、视频路径、0表示摄像头、rtsp://表示流媒体地址。3.3 读懂后处理NMS、置信度筛选与输出解析detect.py跑完会在终端打出一行日志例如image 1/2: 640x640, 2 persons, 1 dog。这行日志背后分了三步走把输入图缩放到640×640送入网络得到1×25200×85的特征图80类目标×每个目标的框坐标、置信度、类别概率再做后处理。对于新手后处理是最容易忽略但最容易出问题的一环。后处理在代码里由non_max_suppression函数实现它做两件事先用置信度阈值也就是--conf-thres把置信度接近于0的候选框筛掉一张640×640的图上会从25200个候选框筛到几个或几百个然后用NMS按--iou-thres消除重叠框同一个目标只保留得分最高的框。很多人直接把--conf-thres调成0.01后看到一堆框就说“模型检测不准”其实是被后处理参数误导了。我自己调参时的习惯是对单张测试图先跑一次--conf-thres 0.1 --iou-thres 0.5把所有可能的目标都拉出来观察哪些是误检、哪些是漏检再逐步提高置信度阈值。输出目录runs/detect/exp/里除了标注了框的原因还有一个labels/文件夹存放了每张图的检测结果txt格式是类别序号 x_center y_center width height这能帮你在不做可视化的情况下快速核对检测结果。4. 训练自己的数据集从标注到跑完100轮4.1 数据准备LabelImg与VOC转YOLO格式训练自己的数据集是这个项目真正拉开差距的地方。官方默认用的是COCO数据集但实际业务场景里你需要的往往是某种特定物体——比如鸟类检测、零件缺陷检测或者树莓派上的小目标检测。第一步是把数据整理成YOLO格式。YOLO格式的核心是每张图片对应一个同名txt文件内容每一行是class x_center y_center width height其中中心坐标和宽高都是相对图片尺寸的归一化值范围0到1。标注工具我推荐LabelImg它安装简单支持VOC格式和YOLO格式切换。下面是一段VOC格式转YOLO格式的Python脚本新项目通常都会用到。import os, xml.etree.ElementTree as ET def voc_to_yolo(xml_file, classes, output_root): tree ET.parse(xml_file) root tree.getroot() img_w int(root.find(size).find(width).text) img_h int(root.find(size).find(height).text) labels [] for obj in root.iter(object): cls obj.find(name).text if cls not in classes: continue 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) cx ((x1 x2) / 2) / img_w cy ((y1 y2) / 2) / img_h w (x2 - x1) / img_w h (y2 - y1) / img_h labels.append(f{classes.index(cls)} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) out_path os.path.join(output_root, os.path.basename(xml_file).replace(.xml, .txt)) with open(out_path, w) as f: f.write(\n.join(labels)) classes [bird, nest] # 按你的类别列表修改 for xml_file in glob.glob(annotations/*.xml): voc_to_yolo(xml_file, classes, labels/)逻辑说明脚本读取VOC格式的XML标注把左上角和右下角坐标换算成中心点坐标加宽高并除以图片尺寸做归一化。classes.index(cls)会把类别名映射成从0开始的整数序号这样YOLO模型才能识别。这里有一个很容易踩的坑YOLO的类别编号必须从0开始且与训练配置文件里的顺序严格一致。如果你在配置文件里把“鸟类”放在第二个位置序号是1而标注脚本里用的是classes.index(bird)也就是0训练时损失就会居高不下。我在新项目里一定会打印一条label样本出来核对print(open(labels/xxx.txt).read())确认类别序号和坐标范围都在0到1之间。4.2 修改数据配置文件从coco128.yaml到自定义数据集训练前要新建一个yaml文件描述数据集的路径和类别。我用的是官方提供的coco128.yaml作为底板复制一份再改这样不会破坏原文件。# mydata.yaml train: ./datasets/bird/train/images val: ./datasets/bird/val/images nc: 2 names: [bird, nest]逻辑说明train和val分别指训练集和验证集的图片目录YOLOv5会自动到同级找对应的labels目录存放的txt标注。nc是类别总数names是类别名称列表顺序必须和标注txt里的序号对应。路径建议用绝对路径或相对于项目根目录的路径不要用相对路径加../这种方式因为我见过太多人因为工作目录和项目目录不一致导致路径找不到。参数说明训练集和验证集的划分我一般按8:2或9:1做验证集太少了mAP指标的波动会很大。类别名称不用和标注工具里完全一样但建议保持统一命名因为后续做可视化分析时会省很多事。4.3 启动训练train.py的关键参数与训练监控训练命令看起来很长但每个参数都有明确用途。这里给出一个我常用的基准配置后续调优都在这条命令上改。python train.py \ --data mydata.yaml \ --weights yolov5s.pt \ --epochs 100 \ --batch-size 16 \ --img 640 \ --device 0逻辑说明--weights yolov5s.pt使用预训练权重做迁移学习这会显著加速收敛是从零训练时间的一半左右。--epochs 100是训练轮数对小数据集来说100轮足够--batch-size 16是每轮迭代的样本数--img 640是训练时输入图像的尺寸。参数说明batch-size是显存占用的大头8GB显存跑yolov5s建议从8或16起步跑不起来就减半。--device 0指定第一块GPU如果你有多卡也可以用--device 0,1启动分布式训练。--workers参数控制数据加载线程数Windows系统上如果设为大于0经常报错建议Windows用户直接加--workers 0。4.4 训练过程的信号解读loss曲线、mAP与过拟合迹象训练启动后用下面的命令打开TensorBoard监控曲线我基本上每半小时看一次训练趋势。tensorboard --logdir runs/train主要盯这几个信号train/box_loss、train/cls_loss、train/obj_loss应该整体呈下降趋势metrics/mAP_0.5应该逐步上升。前30轮mAP上升慢是正常的因为此时学习率还在热身阶段从第60轮开始如果val/cls_loss不再下降甚至反弹而train/cls_loss还在跌那就是过拟合信号。这时不要急着继续加轮次优先减小模型复杂度、增加数据增强或调低学习率。YOLOv5的超参数配置文件在data/hyps/hyp.scratch-low.yaml初学者不建议一上来就动它跑通基线再调优否则你无法判断是哪个改动导致的效果变化。5. 常见问题与避坑我翻过的这些车希望你绕过去5.1 现象训练到一半报错CUDA out of memory进程直接被杀原因batch-size或输入图片分辨率设置超过显存上限。很多人从教程里复制命令不调整在8GB卡上跑batch-size 16、img 640必炸。解决先试--batch-size 8再试--img 512。如果你的目标物体不是特别小把img降到512对检测精度的影响远比想象中小。--img 512训练结束后推理时也用--img 512保持训练和推理尺寸一致。5.2 现象loss在几十个迭代后变成NaN或者训练直接中断报错原因数据集中存在异常图片纯黑图、损坏的jpg、EXIF方向旋转的图导致预处理时像素值异常梯度爆炸。另一个原因是学习率超参数不当但默认配置下更可能是数据问题。解决写个简单脚本过滤掉面积过小的图片比如尺寸小于32×32和无法读取的图片。检查标注txt里是否有坐标大于1.0或宽高为0的行这类脏数据不用特殊处理直接删掉对应样本比清洗更省事。5.3 现象训练正常结束metrics/mAP_0.5一直为0但loss一直在降原因标注类别编号和yaml配置文件里的names顺序不一致。比如标注时用LabelImg保存的类别是2 0.5 0.5 0.2 0.2但names列表里第0个位置写的是别的类。解决训练前打印一条训练集label和一条验证集label确认编号能对上。跑一次python val.py --data mydata.yaml --weights weights/yolov5s.pt用预训练权重先验证数据本身如果输出显示检测到目标就说明数据和配置没问题问题出在训练产生的权重上。5.4 现象模型在训练集上误检多验证集上漏检多调参数效果不明显原因训练集分布和验证集分布不一致。我见过有人把同一场景的视频抽帧后前80%做训练、后20%做验证但视频里目标和背景高度相似模型等于在背样本。解决按场景切分数据而不是按时间顺切。比如鸟类检测数据集要保证训练集和验证集包含不同姿态、不同光照条件下的鸟而不是同一只鸟的不同帧。5.5 现象本地推理效果很好部署到服务器后检测框消失或类别错乱原因推理时传入的图片预处理方式和训练时不一致或者后处理代码里NMS实现有差异。解决检查代码是否做了和训练一样的letterbox缩放、归一化、RGB通道顺序转换。如果部署到C或C#环境优先移植YOLOv5自带的utils/augmentations.py里的letterbox函数逻辑不建议用OpenCV直接resize替代。6. 部署与进阶从pt到ONNX、TensorRT与边缘设备的路径6.1 导出ONNX并确认输出维度的两个坑训练完的pt权重不能直接扔给服务端用通常要先导出为ONNX。python export.py --weights runs/train/exp/weights/best.pt --include onnx --opset 12逻辑说明导出后的ONNX文件可以用onnxruntime或onnx2trt转换后部署。两个必须注意的坑一是--img参数要和训练时保持一致否则宽度维度对不上二是opset版本不要太新opset 12兼容性最好。导出后我用下面的Python脚本验证输出维度输入是1×3×640×640输出是1×25200×85。如果输出形状不对一定是导出参数设置错了。import onnxruntime as ort import numpy as np sess ort.InferenceSession(best.onnx, providers[CPUExecutionProvider]) fake_img np.random.randn(1, 3, 640, 640).astype(np.float32) output sess.run(None, {images: fake_img})[0] print(output shape:, output.shape) # 期望 (1, 25200, 85)逻辑说明这段代码用假数据跑一次ONNX推理output.shape的25200是640×640输入下三个尺度特征图上的候选框总数85是框坐标加置信度加类别数。如果这个值对不上部署端的后处理代码一定出错。6.2 部署端后处理为什么必须重写NMSONNX里默认不包含NMS算子这意味着你需要在业务代码里自己实现置信度筛选和NMS。很多初学者在这里直接抄一段网上的代码但输出tensor的排列顺序和yolov5官方不一致导致框的位置全乱。关键是输出形状是1×25200×85其中25200个候选框的顺序是三个尺度的特征图按照从大到小排列的后处理时要按顺序解码每个框的中心点坐标、宽高和类别得分。我的建议是在C或Java端移植时先对照Python端utils/general.py里的non_max_suppression函数理解透之后再动手不要直接套用其他目标检测模型的后处理代码。6.3 部署方案的取舍mAP、FPS与开发成本之间的平衡如果只做一次性能对比我给的建议是三个指标一块看同尺寸输入下ONNX Runtime在CPU上的推理速度约为PyTorch的1.5到2倍TensorRT在GPU上则是ONNX的2到3倍。但TensorRT的模型转换和动态尺寸处理比ONNX复杂得多如果业务没有毫秒级延迟需求ONNX就够用。上了TensorRT后需要重新验证精度损失FP16量化通常会让mAP下降0.5到1个百分点可以接受就上不能接受就保留FP32。当你想做开放词汇目标检测、或想突破YOLOv5在小目标上的瓶颈时我建议从数据集增强和锚框设置入手而不是急着换模型。YOLOv5的锚框是通过k-means自动学习的如果你的目标普遍极小考虑在训练时把--img提高到960并把锚框数量从默认的9个增加到12个。我的习惯是每次只改一个变量记录mAP变化改三个版本后再决定是否跳到YOLOv8或v12路线。给自己多留基准实验部署后出了问题也有后悔药可吃。希望这份从搭建到部署的路径能帮到你少熬夜多跑通几个版本再谈优化。本文还有配套的精品资源点击获取
返回列表