基于YOLOv5的人物动物检测实战:从复现到训练部署全流程
简介一份基于YOLOv5的人物与动物目标检测完整工程面向计算机视觉初学者与需要快速落地检测任务的开发者。压缩包共六十四份文件包含二十四份Python脚本、十四份编译缓存、十份文本配置、五份XML标注、三份Markdown文档以及模型权重、样例图片等整体约二十点二兆字节目录按训练、预测、数据准备等模块划分解压后即可按照说明文档直接运行。已有两千八百七十三人浏览学习。项目内置锚点计算与标注生成脚本可快速适配自定义数据集覆盖数据预处理、模型训练、推理评估等全流程。通过这套代码读者既能掌握目标检测网络的结构设计与指标评估方法也能了解工程化训练流程中的参数配置与优化思路还能借助配套代码与数据开展二次实验适合课程设计、竞赛训练或算法入门。1. 基于yolo5的目标检测人物动物识别一份能直接落地复现的检测方案标题里的 yolo5其实就是 YOLOv5 的常见写法。拿到一份声称“有代码、有数据、可以直接运行”的目标检测项目先别急着迷信结果我的判断顺序是先确认代码版本和依赖再把权重跑通一张图最后换成自己的数据集做训练和验证。YOLOv5 把人物动物识别做成了标准流水线输入图像直接给出类别、置信度和边界框在普通单卡上推理一帧只需要几十毫秒适合监控客流统计、野生动物观测这些对实时性有要求的场景。真正值得花时间的不是把 demo 跑起来而是数据标签和参数调整这两个环节。2. YOLOv5 的选型与原理为什么人物动物识别任务还在用它2.1 相比更新的检测模型YOLOv5 的优势在哪人物和动物检测这类任务类目相对固定场景也相对可控这时候“稳定能改”比“架构最新”更重要。YOLOv5 的 s/m/l/x 四个型号覆盖了从边缘设备到服务器的大部分算力档位官方仓库里自带的数据格式、训练脚本、导出脚本都是现成的社区里遇到的坑基本都有对应解法。方案人物动物适配度部署生态改造成本YOLOv5 系预训练权重已覆盖 COCO 人物和常见动物成熟ONNX/TensorRT 方案齐全低资料多新版本 YOLO 系精度更高但推理逻辑变化大中等社区资料相对少中需重新踩坑YOLOX 系无 anchor 设计小目标有优势一般部署需自己写解码中高SSD 系小目标弱人物动物表现一般老牌但活跃度低中我一般会直接选 YOLOv5s 起步等确认精度不够再考虑换大模型或者升级架构而不是一上来就追新。具体精度差异以你自己数据集上跑出的 mAP 为准不要拿别人在不同分辨率下测的数值直接对号入座。2.2 骨架、颈部、输出头三个结构点决定了人物动物检测的边界YOLOv5 的网络结构可以拆成三块。骨干网络用的是 CSPDarknet它的核心是让梯度在残差连接里走两条路既减少重复计算又能保留更多细粒度特征。人物有轮廓、动物有毛皮纹理这类中等强度的特征正好被它吃得住。颈部用 PANet 做双向特征融合自顶向下传递语义信息自底向上补回纹理细节大目标和小目标都能拿到足够的信息。输出头在三个尺度上做预测以 640 输入为例网格分别是 80x80、40x40、20x20对应小、中、大三个目标档位。需要重点理解的是 anchor。YOLOv5 每个尺度预设 3 组 anchor 框模型预测的不是最终框而是相对于 anchor 的偏移量。这也是为什么训练自己数据时要考虑是否重新聚类 anchor因为你的人物动物框比例很可能和 COCO 默认的不一致。小动物占比多的场景往往需要提高输入分辨率让它们至少占几十个像素否则再好的网络也无能为力。2.3 COCO 预训练权重能识别哪些人物和动物用官方 COCO 权重跑“人物动物识别”人物就一个类别 person动物大约分布在 cat、dog、horse、sheep、cow、elephant、bear、zebra、giraffe 等十个类别里鸟也算常见但容易被漏。这里有一个项目上容易理解错的地方如果你想输出一个统一的“动物”类而不是分散成猫、狗、马不能只改标签名字要在后处理里做类别合并映射把上述多个类别的检测结果归并到同一个 ID 上。COCO 训练集里人物样本量远大于动物所以直接拿通用权重做动物识别稀有类别的召回率会偏低。那种“有数据能直接运行”的体验只能覆盖到 demo 阶段做实际应用时建议把你场景里的动物图片单独收集两三千张做微调否则猫狗还行野外的鹿、狐狸这类数据基本是抓瞎状态。3. 把官方代码先跑起来最小环境、权重与一行推理命令3.1 环境准备与依赖安装先不要急着改代码第一步是把运行环境固定住。YOLOv5 官方仓库对 Python 的要求通常在 3.8 到 3.10 之间我习惯用虚拟环境隔离避免和系统 Python 打架。# 创建虚拟环境Python 建议 3.8~3.10 conda create -n yolo python3.9 -y conda activate yolo # 进入官方代码目录后安装依赖 cd yolov5 pip install -r requirements.txt # 验证 CUDA 是否可用输出 False 就说明 PyTorch 装成了 CPU 版 python -c import torch; print(torch.cuda.is_available())逻辑说明requirements.txt 里已经把 numpy、opencv、matplotlib、pyyaml 这些主要依赖锁好了不需要手动一个个装。参数说明如果你没有 NVIDIA 显卡把 PyTorch 装成 CPU 版也能跑推理只是训练不现实输出 False 之后要先确认自己装的 PyTorch 版本对应的 CUDA 版本再决定要不要卸载重装。依赖里最容易出问题的是 opencv装完后建议顺手执行python -c import cv2; print(cv2.__version__)验证一下能避免后面读视频踩坑。3.2 目录结构、权重放置与最小的推理命令直接运行前把目录结构摸清楚能省下不少排查时间。实际只需要关注这几个路径代码入口在仓库根目录权重文件放在根目录下数据放在 data 目录里推理结果默认写进runs/detect/。一张图片跑通全流程的命令是# 用官方预训练权重直接跑一张测试图 python detect.py \ --weights yolov5s.pt \ --source data/images/bus.jpg \ --conf-thres 0.25 \ --iou-thres 0.45 \ --device 0参数说明--weights指向权重文件yolov5s.pt 是官方 COCO 预训练权重第一次运行会自动下载--source是输入来源单张图、目录、视频、摄像头都支持--conf-thres是置信度阈值越高误检越少但漏检变多--iou-thres是 NMS 去重阈值越高越倾向保留重叠框越低越倾向只留最大框--device 0表示用第一张 GPU改用 CPU 时写--device cpu即可。跑完后打开runs/detect/exp/下的结果图能看到人物被画了框置信度一般都在 0.5 左右或更高。3.3 source 的多种输入图片、视频、摄像头和 RTSP 流换成自己数据时不需要改代码只要换--source的值。这一步很关键因为实际项目里你手里的输入可能是一堆抓拍图也可能是一个摄像头实时流。source 值输入类型示例单个图片路径图像文件data/images/bus.jpg目录路径批量图片data/images/视频文件路径mp4/avitest.mp4设备 ID摄像头0rtsp/http 地址视频流rtsp://192.168.1.64:554/stream在我做过的人物动物识别 demo 里批量图片识别最常用因为它是离线任务逐帧跑完把结果汇总就行。摄像头和 RTSP 流则要关注读取瓶颈opencv 对部分摄像头的编码格式支持不完整表现是程序启动后一直黑屏或报解码错误常见做法是检查系统是否装了 ffmpeg 依赖或者先把流拉下来存成 mp4 再离线推理。换 source 时唯一要注意的是路径别带中文和空格YOLOv5 对特殊字符路径的支持一直是老大难提前规避比事后排查省事得多。4. 避坑快速跑通阶段最容易翻车的四个点4.1 权重文件下载不完整推理直接报错现象运行 detect.py 后报出UnicodeDecodeError或者error during load检查发现 yolov5s.pt 只有几 KB 甚至几十字节。原因下载过程被中断或者下载工具把网页错误页存成了 .pt 文件这类文件长得像权重实际内容完全不是模型。解决先看文件大小再跑程序。# 看文件大小正常 yolov5s.pt 大约几十 MB只有几 KB 就是坏的 ls -lh yolov5s.pt # 重新下载加超时和断点续传参数 wget -c --timeout30 --tries3 -O yolov5s.pt 权重官方下载地址逻辑说明PyTorch 加载权重时先做反序列化文件内容不对就会抛出格式解析类的异常这类报错比较有迷惑性容易被误判成代码问题。参数说明-c表示断点续传--timeout 30是每个连接超时 30 秒--tries 3是失败重试 3 次下载完成后最好再执行一次ls -lh确认大小。4.2 中文路径和空格路径导致图片读不到现象程序不报错但结果图里没有任何检测框日志里显示读取到的图片数量为 0。原因检测脚本用 glob 遍历文件遇到中文或空格路径时匹配失败或者 OpenCV 在 Windows 上读不了含中文的路径。解决数据集和输出目录全部改用英文命名层数不要太深。如果是已有数据集改名成本高就在代码里改源文件读取方式把cv2.imread(path)换成cv2.imdecode(np.fromfile(path, dtypenp.uint8), cv2.IMREAD_COLOR)后者能处理中文路径。能用改名解决的就不改代码这是我反复验证后的习惯。4.3 显卡显存不足CUDA out of memory现象推理时突然报CUDA out of memory或者训练刚开始几十步就崩了。原因输入分辨率过高、batch size 太大或者机器上同时跑了多个占用显存的进程。解决推理阶段在命令里加--batch-size 1以及降低--imgsz 640训练阶段就按显存反推 batch size显存占用约等于输入尺寸的平方乘以 batch size 再乘以模型复杂度没有固定公式只能从 8 或 16 开始试。如果只是临时跑一版结果直接切 CPU 更省心python detect.py --weights yolov5s.pt --source test.mp4 --device cpu说明CPU 推理一张 640x640 的图大概需要一两秒用来验证流程没问题做实时应用不现实。4.4 检测框全部错位成方块状贴边现象人物和动物明明在画面中间检测框却紧贴图像边缘形状也不是正常的矩形比例。原因常见于你自己写推理脚本时直接cv2.resize把图像拉成正方形没有做 letterbox 等比缩放边界框坐标也跟着错了。官方 detect.py 内置了 letterbox 预处理但自定义脚本里漏掉这一步就会出这种诡异结果。复现官方逻辑的完整函数如下import cv2 import numpy as np def letterbox(img, new_shape640, color(114, 114, 114)): h, w img.shape[:2] # 取最小缩放比保证原始内容完整保留 r min(new_shape / h, new_shape / w) nh, nw int(h * r), int(w * r) img cv2.resize(img, (nw, nh), interpolationcv2.INTER_LINEAR) # 生成画布并将缩放后的图居中放置 canvas np.full((new_shape, new_shape, 3), color, dtypenp.uint8) top (new_shape - nh) // 2 left (new_shape - nw) // 2 canvas[top:top nh, left:left nw] img return canvas, r, top, left参数说明new_shape是模型期望的输入边长color是填充色YOLOv5 默认是 (114,114,114) 灰色返回的r是缩放比例top和left是填充偏移量后处理把模型输出的坐标映射回原图时要用(x - left) / r和(y - top) / r还原。这个函数我几乎在每个项目里都直接复用不要再自己重新实现一遍容易出细节错。4.5 能跑但一张图都没检测出来现象输出图片正常生成但画面里没有框日志显示0 detections。原因置信度阈值设太高、目标在画面里太小子 32x32 像素、或者图像通道顺序反了导致内容看起来发蓝绿色。解决先把--conf-thres降到 0.1 试一下肉眼能看见目标但仍无框就检查通道问题再用--save-txt输出原始检测文本确认是后处理丢结果还是网络本身没预测出来。这三个原因里阈值问题最常见通道问题最隐蔽BGR 和 RGB 颠倒后模型依然能跑只是输出质量极差。5. 数据准备与自训练从 YOLO 标签格式到 train.py 参数5.1 YOLO 标签格式一份 txt 对应一张图官方代码能跑通不等于你的业务能跑真正进入人物动物识别场景后数据格式是第一道坎。YOLO 标签用纯文本 txt 文件保存每行代表一个目标格式是class_id x_center y_center width height其中坐标全部归一化到 0 到 1。举个例子图片尺寸 640x640一个人体框左上角在 (80, 40)右下角在 (320, 640)对应的 txt 内容是0 0.3125 0.53125 0.3750 0.9375。计算过程是中心点 x 为 (80320)/2 再除以 640得到 0.3125宽为 (320-80) 除以 640得到 0.375。目录组织必须和 YOLOv5 预期一致images 和 labels 分开放置且 txt 文件名和图片名严格一一对应datasets/ ├── images/ │ ├── train/ │ └── val/ └── labels/ ├── train/ └── val/如果一张图没有目标txt 文件应该是空文件但文件本身要存在否则训练脚本会跳过并提示找不到标签。我见过的最常见错误是把边界框的四个数字写成了两个对角顶点的原始像素坐标没有归一化也没有算中心点和宽高结果训练时 loss 乱跳模型输出的框位置全部异常。5.2 从 VOC XML 转到 YOLO 格式一个可直接套用的转换脚本大部分人手里的标注是 VOC 格式也就是每个图片对应一个 XML里面用 xmin、ymin、xmax、ymax 记录框。转 YOLO 的常用做法是写一个一次性脚本XML 和图片都放到同名字的 train/val 目录下脚本读 XML 后生成 txt。下面的脚本可以直接改路径使用import os import xml.etree.ElementTree as ET VOC_DIR /path/to/voc # XML 文件所在根目录 OUT_DIR /path/to/datasets # 输出目录下面建 labels/train 等 CLASSES [person, cat, dog, horse, sheep, cow, elephant, bear, zebra, giraffe] # 类别序必须固定 def convert_one(xml_path, out_txt): tree ET.parse(xml_path) 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 if name not in CLASSES: continue cls_id CLASSES.index(name) 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 lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {bw:.6f} {bh:.6f}) with open(out_txt, w) as f: f.write(\n.join(lines)) # 转换 train 和 val 两个分割 for split in [train, val]: xml_dir os.path.join(VOC_DIR, split) out_dir os.path.join(OUT_DIR, labels, split) os.makedirs(out_dir, exist_okTrue) for xml_name in os.listdir(xml_dir): if not xml_name.lower().endswith(.xml): continue txt_name xml_name.replace(.xml, .txt) convert_one(os.path.join(xml_dir, xml_name), os.path.join(out_dir, txt_name))逻辑说明脚本先读 XML 里的图片宽高再遍历每个 object用 CLASSES 列表确定类别 ID最后把像素坐标转成归一化中心点格式。参数说明VOC_DIR下按 train/val 分了两个子目录图片文件要同步拷贝到OUT_DIR/images/下并保证文件名一致CLASSES里的顺序一旦定下来就不要改否则以前生成的 txt 里的类别 ID 会错位。脚本运行完可以随机抽几张图把生成的 txt 和原图叠加画框检查这个习惯能拦下八成标注问题。5.3 数据配置与训练启动命令训练前要写一个 YAML 数据配置文件它告诉训练脚本数据在哪、有多少类、类别名是什么。路径配置是这里最大的坑path必须指向 images 的上级目录而不是 images 本身。# mydata.yaml path: /absolute/path/to/datasets # 绝对路径不要用相对路径 train: images/train val: images/val nc: 10 names: [person, cat, dog, horse, sheep, cow, elephant, bear, zebra, giraffe]训练启动命令python train.py \ --data mydata.yaml \ --weights yolov5s.pt \ --batch-size 16 \ --epochs 100 \ --imgsz 640 \ --device 0参数说明--weights用官方 COCO 权重做初始化相当于热启动比随机初始化收敛快得多--batch-size 16在显存不够时先降到 8batch 太小会让 BN 统计不稳定--epochs 100是从头训练的新手建议值实际一般 50 代左右就能看出趋势--imgsz 640是默认输入分辨率小目标多再考虑 960。训练脚本会自动做增强Mosaic、随机翻转、HSV 扰动都内置了不需要自己写数据增强逻辑。启动后训练日志会持续打印每个 epoch 的 box_loss、cls_loss 和 mAP如果 loss 在第一个 epoch 就异常大大概率是标签归一化写错了。5.4 三个必调参数与训练日志的判读方法人物动物识别这类固定类别任务不需要每个参数都摸一遍真正值得重点关注的就三个输入分辨率、batch size 和学习率相关配置。参数影响范围经验建议imgsz小目标召回率显存占用按平方上升640 起步小动物多再试 960batch-sizeBN 统计稳定性和收敛速度小数据集用 8 或 16显存大再往上加hyp 里的学习率收敛速度和过拟合边界默认 hyp.scratch-low 即可别一上来就动训练完成后结果存在runs/train/exp/下里面weights/里有last.pt和best.ptbest 是验证集上 mAP 最高时的权重last 是最后一个 epoch 的权重。日常推理用 best继续训练才用 last。日志里的results.png会画 loss 和 mAP 曲线重点看 val 指标是否在某个 epoch 后开始掉头向下那说明过拟合了。我见过不少训练 loss 降得很快但 mAP 不涨的情况排查到最后基本都是标签顺序和 YAML 里 names 不一致训练不收敛先怀疑数据不要怀疑模型这是用人物的动物识别项目踩出来的习惯。6. 部署验证与进阶ONNX 导出和小目标召回的两个技巧6.1 把 best.pt 导出成 ONNX脱离 PyTorch 部署训练完的模型不能总靠 Python 脚本跑实际部署到服务器或嵌入式设备时我习惯先导出 ONNX 格式。官方仓库已经内置导出脚本python export.py \ --weights runs/train/exp/weights/best.pt \ --include onnx \ --dynamic导出后得到best.onnx用 onnxruntime 加载部署机器上不再需要安装 PyTorch。注意 ONNX 的输出是原始预测矩阵NMS 后处理要自己写import onnxruntime as ort import numpy as np session ort.InferenceSession(best.onnx, providers[CPUExecutionProvider]) # 输入需要预处理成 [1,3,640,640] 的 BGR 归一化张量 # 输出形状为 [1, anchor总数, 类别数5] # 在 640 分辨率下 anchor 总数 80*80 40*40 20*20 25200参数说明--dynamic允许输入尺寸可变方便测试不同分辨率导出后先用同一张图对比 PyTorch 推理和 ONNX 推理的结果确认框位置一致再接入业务代码。6.2 小目标召回升分辨率或切片推理人物检测通常压力不大动物识别里鸟、鼠这类目标才是难点。目标在画面里不足 32x32 像素时640 分辨率基本无能为力。最简单的改进是把 imgsz 提到 960代价是显存和推理耗时接近翻倍仍然不够就做切片推理把原图按网格切成 2x2 或 3x3 的子图分别检测再合并结果代价是后处理变复杂帧率下降一个量级。单看帧率会以为模型变差了其实是输入分布变了验证时要用统一的数据集跑 val.py 对比 mAP不能只看几张效果图。6.3 一个不占显存但有效的验证习惯我的习惯是每次调参后完整跑一遍 val.py输出一个可对比的 mAP 数值再决定保留还是回退。因为单张测试图很容易骗人某次我为了减少误检把 conf-thres 从 0.25 提到 0.5肉眼觉得画面干净多了后来用验证集一跑动物类别的召回率掉了两成等于把检测能力砍了一刀才换来视觉上的整洁。后来凡是动过阈值、分辨率、权重这三个变量的操作我都强制自己跑一次 val.py 留档数值不涨就直接回退到上一版。希望这个习惯和这套流程能帮到你让你在人物动物识别上少走几步弯路。本文还有配套的精品资源点击获取