YOLOv5实战:猪脸目标检测从数据标注到部署全流程

发布时间:2026/10/11 21:44:50
YOLOv5实战:猪脸目标检测从数据标注到部署全流程 简介这份基于YOLOv5的猪脸目标检测资源包含PyTorch实现的完整模型与可直接运行的代码适用于畜牧业的猪只个体识别、健康监测等智能化场景也适合想将目标检测应用于特定小目标方向的开发者学习参考。压缩包共236个文件约70.75MB内容以Python源码53个py、YOLOv5模型配置49个yaml与Jupyter Notebook5个ipynb为主辅以少量示例图像和说明文档整体目录结构清晰。项目内置两套预训练权重轻量的best_yolo_tiny.pt适合算力受限环境标准版best.pt更侧重检测精度可满足不同使用需求。主文件main.ipynb完整覆盖环境配置、模型加载、数据准备、推理检测和结果可视化等环节下载后按步骤运行即可查看猪脸检测效果便于在此基础上进一步调整参数或优化模型。目前已有1292人学习下载对初学者和养殖智能化方向的技术人员都有不错的参考价值。1. 猪脸目标检测的最后一公里yolo-v5模型与完整复现代码新手拿到一个基于 yolo-v5 的猪脸目标检测项目最容易犯的错是直接开训。某开发者的教训是把数据从手机里导出来文件名批量改名然后丢进 YOLOv5 仓库里跑 train.py结果模型收敛得慢、精度虚高、验证集上错检一堆。问题不在模型而在标签和数据集划分。这个资源实际解决的是从原始猪脸图到能跑通的检测模型中间那一大段脏活数据整理、标签格式转换、训练参数调优、推理与可视化、坑点规避。它适合两类人——准备做养殖场景个体识别但卡在目标检测环节的开发者以及想把 YOLOv5 泛化到非标准目标而非行人、车辆上验证思路的算法工程师。本文按这套流程实测过的路径逐层拆数据集结构、脚本逻辑、参数含义、踩坑记录都会覆盖到看完你能直接照着复现一份自己的猪脸检测模型。2. 数据与标签先让数据集变成 YOLOv5 能认的格式2.1 为什么在这个任务上选 YOLOv5 而不是 YOLOv8 或 Faster R-CNN目标检测领域现在可选的东西很多。常见做法是追求极致精度选 Faster R-CNN 这类两阶段模型追求部署速度选 YOLO 系列。但猪脸检测这个场景里YOLOv5 比 YOLOv8 更稳原因有三。第一YOLOv5 的生态最成熟网上能找到最多的预训练权重、数据增强配置和踩坑记录遇到问题搜索出来的答案基本都是针对这个版本的。第二YOLOv5 的模型结构CSPDarknet53 PANet对小型目标比较友好猪脸在监控画面里通常占比不大FPNPAN 的多尺度融合能力比单尺度检测头更适合这种场景。第三它的推理代码和部署工具链齐全从 detect.py 到 ONNX 导出再到嵌入式部署都有现成脚本不用自己从头写。与之对比YOLOv8 在精度上确实有一定提升但其标签格式边框归一化方式略有不同和回调接口改了不少如果团队里其他人还在用 v5 的标签格式和工具脚本迁移成本不低。Faster R-CNN 在专用数据集上精度也很漂亮但是推理速度慢养殖场往往需要实时监测多路摄像头动辄 10-20 FPS 的需求它顶不上去。所以在猪脸识别这个“单类目标、需要快速落地”的任务上YOLOv5 是综合代价最小的选择。2.2 数据目录结构与标签格式转换脚本拿到手的数据通常是这样的一堆 JPG 图片加上标注软件比如 LabelImg导出的 XML 文件。YOLOv5 不直接吃 XML它需要每个图片对应一个同名的 .txt 文件内容是“类别编号 归一化中心x 归一化中心y 归一化宽度w 归一化高度h”。类别编号要从 0 开始猪脸是这个项目里唯一的类所以全是 0。归一化是指把像素值除以图片宽高比如一个框左上角在 (100, 80)、右下角在 (300, 240)图片宽度 640、高度 480那么中心点就是 ((100300)/2/640, (80240)/2/480)宽高同理。写转换脚本时我一般会同时处理路径拼接和留空文件的问题。下面这段代码可以放进项目里直接改路径用import os import xml.etree.ElementTree as ET def convert_xml_to_txt(xml_path, out_dir, class_namesNone): # class_names: 按标注文件中的顺序传入类别列表本项目只用 [pig_face] if class_names is None: class_names [pig_face] class_to_id {name: idx for idx, name in enumerate(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) out_lines [] for obj in root.iter(object): name obj.find(name).text if name not in class_to_id: continue cls_id class_to_id[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) # 防止 xmax xmin 这类脏标注导致归一化出现负数 if xmax xmin or ymax ymin: continue x_center (xmin xmax) / 2.0 / img_w y_center (ymin ymax) / 2.0 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h # 越界裁剪防止框超出图像范围 x_center min(max(x_center, 0.0), 1.0) y_center min(max(y_center, 0.0), 1.0) w min(max(w, 0.0), 1.0 - x_center) h min(max(h, 0.0), 1.0 - y_center) out_lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) if out_lines: txt_name os.path.splitext(os.path.basename(xml_path))[0] .txt with open(os.path.join(out_dir, txt_name), w, encodingutf-8) as f: f.write(\n.join(out_lines)) else: # 标注质量太差或者图片里没有目标时最好直接丢弃不要生成空文件 print(f[warn] 跳过无有效目标文件: {xml_path})这段逻辑里有个细节值得说遇到 xmax 小于等于 xmin 的脏数据直接跳过而不是抛异常因为一个 XML 里可能只有一两个框是标注失误其他框都是好的跳过之后这个样本还能用。越界裁剪同理边界略微超出的框裁剪后仍是有效训练样本直接放弃会损失数据量。实际跑批处理时图片和 txt 要严格同名且要检查有没有图片没有对应的 txt、有没有 txt 没有对应的图片两边不一致会导致训练时直接报错。2.3 数据增强与训练集/验证集划分猪脸检测数据量通常不大几百到两三千张不等。这个时候数据增强是必要的但不要无脑全开。YOLOv5 的增强配置很多在 data/hyp.scratch-low.yaml 里可以调。对猪脸这个任务我习惯开的是HSV 色域扰动hsv_h 0.015、hsv_s 0.7、hsv_v 0.4、随机水平翻转fliplr 0.5、随机缩放和平移scale 0.5、translate 0.1。不建议开 mosaic 到 1.0 的默认值因为猪脸在 mosaic 拼接图里会被裁切得很碎小目标特征丢得厉害一般设 0.5 即可。如果图片中有大量远景猪脸比如一个栏里十几头猪mosaic 过高反而会让模型学到一堆半张脸的模式。划分训练集和验证集时核心原则是同一头猪的照片不能同时出现在两个集合里。养殖场的图片序列经常是连着拍的如果不按“个体”而是按“图片”随机划分模型会记住个体特征而不是猪脸共性验证集精度会虚高。常见做法是先按文件名或时间戳找出每个个体的图片集合按个体分桶以个体为单位做 8:2 划分。下面这个脚本演示了如何按图片前缀分组划分import os import random from collections import defaultdict image_dir ./pigface/images train_out ./pigface/train.txt val_out ./pigface/val.txt # 假设文件名格式为subject_日期_序号.jpg比如 pig001_20250101_001.jpg subject_groups defaultdict(list) for img_name in os.listdir(image_dir): if not img_name.lower().endswith((.jpg, .jpeg, .png)): continue subject_id img_name.split(_)[0] # 取第一个下划线前缀作为个体标识 subject_groups[subject_id].append(os.path.join(image_dir, img_name)) subjects list(subject_groups.keys()) random.seed(42) random.shuffle(subjects) split_idx int(len(subjects) * 0.8) train_subjects set(subjects[:split_idx]) val_subjects set(subjects[split_idx:]) train_list, val_list [], [] for sid, img_paths in subject_groups.items(): target_list train_list if sid in train_subjects else val_list target_list.extend(img_paths) with open(train_out, w) as f: f.write(\n.join(train_list)) with open(val_out, w) as f: f.write(\n.join(val_list)) print(f训练图片数: {len(train_list)}, 验证图片数: {len(val_list)}) print(f训练个体数: {len(train_subjects)}, 验证个体数: {len(val_subjects)})运行完这个脚本会得到两个 .txt 文件每个文件里是图片路径按行排列。注意源码里用的是随机种子固定划分保证每次跑结果一致方便复现。设计数据目录时还有一点要留意训练脚本 train.py 会读取你的数据集 YAML 配置文件里面指向的路径最好用相对路径或使用环境变量拼接不要写死绝对路径否则换机器跑就要改一遍。3. 环境与训练配置Pytorch 下的训练参数详解3.1 环境依赖与版本匹配YOLOv5 的依赖安装在 requirements.txt 里列得很清楚但最容易出问题的不是依赖缺失而是版本冲突。常见情况是电脑上已经装了 Pytorch 1.13 或 2.xYOLOv5 的某些老版本代码在 torch 2.x 上会有算子兼容问题特别是 focus 层的实现。我用过一套比较稳的组合Python 3.8 以上、Pytorch 1.12.1、torchvision 0.13.1、opencv-python 4.6 以上、numpy 1.21 以上。如果你用的 Pytorch 版本更高比如 2.1YOLOv5 官方最新代码一般能跑但建议第一次先跑通 detect.py 的 COCO 预训练权重再做替换避免上来就把锅甩在模型上。安装依赖时建议开一个干净的虚拟环境不要直接装在 base 环境里conda create -n pigface python3.8 -y conda activate pigface # 安装 pytorch注意 cuda 版本要与本机驱动匹配 conda install pytorch1.12.1 torchvision0.13.1 cudatoolkit11.3 -c pytorch -y # 安装 YOLOv5 剩余依赖 pip install -r requirements.txt这里有个参数值得展开说cudatoolkit 的版本不要只看本机驱动支持的最高版本而要看你显卡算力对应的稳定版本。老一点的显卡比如 1080Ti装 11.3 比装 11.8 稳定新一点的卡比如 3090可以上 11.6 以上。装完最好跑一段官方检测脚本验证环境确认 CUDA 真的可用而不是 CPU 在兜底训练——肉眼判断方法是看训练日志每轮迭代时间GPU 下通常几秒一轮CPU 下要几十秒甚至更久。3.2 训练命令与关键超参说明数据准备好之后核心动作是写一个数据集配置文件。在 YOLOv5 的 data 目录下新建 pig_face.yaml# pig_face.yaml # 数据集配置示例 path: ../pigface # 相对于当前运行时工作目录的路径 train: images/train # 训练图片目录 val: images/val # 验证图片目录 nc: 1 # 类别数只有 pig_face 一类 names: [pig_face] # 类别名称列表顺序必须与标签文件里的编号一致这个 yaml 文件的 path 字段很容易翻车。如果你的数据集实际放在 /home/user/data/pigface但运行时当前目录是 /home/user/yolov5那么 path 应该写 ../data/pigface。它支持绝对路径但不建议因为后续换机器部署时又要改。names 列表里只有一个类别时标签文件编号只能是 0如果标注时错误地写成了 1训练时模型会把它当成背景精度必然崩。接着执行训练命令python train.py \ --data data/pig_face.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 100 \ --device 0 \ --workers 4 \ --name pig_face_exp1每个参数在这个任务里都有讲究。img 尺寸用的是 640因为训练图上猪脸普遍在 50-150 像素的范围内640 足以保留特征再小比如 320虽然速度快但小脸的特征会丢失再大1280对精度提升有限显存占用却翻了几倍。batch 大小受显存限制16 是一个中等卡12GB 显存能跑的量级显存不够就降到 8 甚至 4同时调低 workers 数量。epochs 100 看起来不少但这个数据集几百张的情况下前 30 个 epoch 就会收敛到 90% 左右的 mAP后面是在修正边角样本。训练时监控哪些指标值得说一下。训练日志里最关键的是 Pprecision、Rrecall、mAP0.5、mAP0.5:0.95 四个值。第一个 epoch 结束时 mAP0.5 如果比 0.05 还低属于正常因为模型刚开始学不要急着停。关键看 loss 曲线是否单调下降train loss 下降但 val loss 在某个 epoch 后反弹说明过拟合开始提前终止就设在那个点上不用硬跑完 100 个 epoch。3.3 早停与断点续训YOLOv5 自带早停机制在 train.py 里通过 --patience 参数控制。默认值是 100意思是连续 100 个 epoch 验证集指标没有提升就停止。在实际猪脸任务里数据量少模型很容易在 40 个 epoch 后就开始过拟合建议把 patience 设小一点比如 20。这样既能把最优权重留下来又不会浪费时间。如果训练中途中断了——断电、显存溢出、手动停掉——不需要从头再来。YOLOv5 会在 runs/train/expN/weights/ 下定期保存 last.pt 和 best.pt断点续训的命令要显式指定上次的权重路径python train.py \ --data data/pig_face.yaml \ --weights runs/train/pig_face_exp1/weights/last.pt \ --img 640 \ --batch 16 \ --epochs 100 \ --device 0 \ --resume--resume 参数会把 epochs、optimizer 状态、学习率调度都接回来不需要手动改任何配置。这是一个容易误用的点有人为了“续训”会重新下载预训练权重再跑一遍完整训练既浪费时间又破坏了模型已经学到的特征。记住跨项目用预训练权重是这个习惯的例外——如果换数据集、换类别数必须从 COCO 预训练重新开始不能直接拿上次训练的 best.pt 去跑新数据集因为类别数变了最后的输出层维度对不上。4. 模型推理与效果验证从权重到可视化检测结果4.1 单张图片推理用官方 detect.py 还是自定义脚本官方 detect.py 作为快速验证入口很方便python detect.py \ --weights runs/train/pig_face_exp1/weights/best.pt \ --source data/pigface/test/P001.jpg \ --conf 0.4 \ --iou 0.45 \ --imgsz 640 \ --save-txt \ --save-conf这里 conf 参数是置信度阈值iou 参数是 NMS 的 IoU 阈值。猪脸检测场景里 conf 设为 0.4 比较合理如果调太高比如 0.7侧脸、遮挡情况下的猪脸会被漏检调太低比如 0.1会出现大量误检比如把食槽边缘、墙上的斑点当成猪脸。iou 0.45 是默认值处理猪脸重叠较多的情况下够用如果视野里猪群拥挤可以调升到 0.5 减少重复框。要注意 --save-txt 会把检测结果保存成 txt 文件里面每行是“类别 置信度 x_center y_center w h”这个格式适合后续做批量评估。如果只想看图不加 --save-txt 即可。输出默认在 runs/detect/expN/ 下--name 可以自定义输出目录名。4.2 批量推理与视频流写一个可复用的检测函数官方 detect.py 喂视频流时需要依赖 ffmpeg 和 opencv 的编解码能力这块偶尔会出问题。更可控的方式是写一个自定义推理脚本直接调用模型方便嵌入到你的管理系统里。下面这段代码可以当作检测服务的基础模块import cv2 import torch import numpy as np from pathlib import Path # 加载训练好的权重 model torch.hub.load(ultralytics/yolov5, custom, pathruns/train/pig_face_exp1/weights/best.pt, force_reloadFalse) # 关键参数置信度0.4NMS IoU阈值0.45 model.conf 0.4 model.iou 0.45 model.classes [0] # 只检测猪脸类别 model.max_det 50 # 单帧最多输出50个框防止密集误检 def detect_pig_faces(image, save_dir./detect_out): 输入BGR图像返回检测框列表和可视化图 框格式: [x1, y1, x2, y2, confidence, class_id] results model(image, size640) bboxes results.xyxy[0].cpu().numpy() # 可视化并保存 save_path Path(save_dir) save_path.mkdir(parentsTrue, exist_okTrue) rendered results.render()[0] # 已经画好框的图像 out_name save_path / fdet_{cv2.getTickCount()}.jpg cv2.imwrite(str(out_name), rendered) return bboxes, rendered if __name__ __main__: img cv2.imread(data/pigface/test/P001.jpg) boxes, vis detect_pig_faces(img) print(f检测到 {len(boxes)} 个猪脸) for box in boxes[:5]: x1, y1, x2, y2, conf, cls box print(f框: ({x1:.0f},{y1:.0f})-({x2:.0f},{y2:.0f}) 置信度: {conf:.3f})这段代码里 model.conf 和 model.iou 直接挂在模型对象上是 YOLOv5 推理接口特有的属性比每次调用时传参简洁。classes 限制检测类别的做法在猪场里有实际意义如果数据里不小心混入了误标的“人”或“猪耳”类别这里可以直接屏蔽不用重新训练。max_det 限制最大检测数则是防止猪栏里密密麻麻的误检框把显存和CPU打满。4.3 用验证集评估真实精度单张图片看完效果后需要用验证集整体测一遍 mAP评估模型是不是真的能上线。官方 val.py 可以直接复用训练得到的权重python val.py \ --data data/pig_face.yaml \ --weights runs/train/pig_face_exp1/weights/best.pt \ --conf 0.4 \ --iou 0.45 \ --img 640跑完后会输出一张 PR 曲线图和一张混淆矩阵到 runs/val/exp/ 目录同时终端会打印每个类别的 P、R、mAP0.5。结合猪脸场景需要额外看一个指标遮挡目标上的召回。如果验证集里有意识地标了被食槽、栏杆挡住的猪脸而这个类别的召回明显低于整体均值说明要多补这类负样本或增强数据。正是因为目标检测模型在正样本充足但遮挡场景不足时表现差异很大所以我在验证阶段会比较同一批数据在 conf 0.3、0.4、0.5 下的召回变化找到让两三个关键难例都检出的临界点来定线上参数。5. 避坑猪脸检测项目里踩过的五个典型问题5.1 标签类别编号出错导致模型完全不收敛现象训练 loss 下降极慢最后 mAP 一直卡在 0.1 上下。原因标注工具导出的类别名与 YAML 配置的 names 列表顺序不一致比如工具里类别叫 “pig” 而代码里写 “pig_face”或者编号从 1 而不是 0 开始。解决转换脚本里打印每类文件的数量做交叉验证统计 txt 里第一列数字的分布必须全部是 0 才对。之后再遇到 mAP 极低的情况先跑这段统计再排查模型结构。5.2 训练时显存溢出但调低 batch 后效果骤降现象batch 设为 32 时显存爆掉改成 8 后训练能跑完但精度比预期低很多。原因batch 减小后BN 层的统计量估计不准小数据集上更明显同时学习率没有跟着调。解决batch 从 32 降到 8 后学习率也同步从 0.01 降到 0.0025并且开启 -–sync-bn 选项YOLOv5 支持跨卡同步 BN单卡时不用。在显存不足的情况下另一个选择是保持 batch 为 16、把图片尺寸从 640 降到 512而不是粗暴减 batch。5.3 检测结果中猪脸框偏大或偏小偏离真实标注现象模型训练完验证集 mAP 很高0.95但实际部署时拍摄角度不同检测框总比真实猪脸轮廓大一圈。原因训练数据里标注框大多紧贴面部但测试时拍摄距离更近或更远模型学到的是标注的“平均尺度”没有学到尺度不变性。解决训练时开启多尺度训练YOLOv5 在 train.py 的 -–img 参数可以传入一个范围比如--img 640 512 736会随机在 [512, 736) 之间缩放图片让模型看到不同尺度的猪脸同时把 fliplr 打开避免模型只在左脸方向上学到特征。5.4 多张猪脸重叠时漏检严重现象单头猪的测试图效果很好一到猪栏里三五头猪聚在一起漏检率接近 30%。原因NMS 把重叠框抑制掉之后模型只输出置信度最高的框叠加猪脸之间的遮挡模型学到的“完整脸”模式匹配不上。解决调整 iou 阈值策略从 iou 0.45 提高到 0.6让重叠框保留更多同时数据增强里加入更大幅度的随机裁剪在 mosaic 中缩小单块区域面积让模型强行学习残缺脸。如果效果还不够就在损失函数上考虑使用 CIoU 而不是默认的 GIoUCIoU 对重叠目标的回归更敏感YOLOv5 的模型代码里可以直接指定。5.5 换了机器后模型推理结果与训练时不一致现象在同一份权重与同一张测试图下本地机器检测框正常部署服务器或另一台开发机上框的位置偏移了几个像素置信度也略有变化。原因推理时图片的预处理细节不一致特别是letterbox的 padding 填充值、颜色通道顺序BGR vs RGB、以及是否启用了半精度推理。解决部署时固定使用官方 detect.py 里的预处理方式或在自己脚本中用模型内置的预处理函数不要手动用 cv2.resize 加灰度填充。半精度FP16只在 GPU 上开启CPU 推理要强制转成 FP32否则数值误差会在某些模型层中被放大。6. 部署进阶用小模型量化提速与边端适配猪脸检测项目跑通之后下一步通常是往嵌入式设备上迁移比如养殖场的边缘盒子、带 NPU 的摄像头。常见做法是把 Pytorch 权重先转成 ONNX再经过 ONNX Runtime、TensorRT 或各家 NPU 工具链做量化。这里有一个实际用过的经验直接用torch.hub.load加载权重在 PC 上测速没意义因为它没有做任何推理优化真正提速的关键是把模型导出为 ONNX 并启用动态轴同时把数据精度量化到 INT8。量化后模型体积缩到约原来的四分之一推理速度翻倍以上对猪脸这类单类任务来说精度损失一般能控制在 2% mAP 以内。导出 ONNX 时有一个参数要注意opset 版本选 11 到 13 之间比较稳太新比如 17在一些老设备 NPU 上反而不支持。量化方式上优先做静态校准量化找一个包含各个角度的验证子集标注好框来做校准而不是随意挑几张图片。在嵌入式设备上如果发现输出结果与 PC 端差异变大先检查输入图片是否经过了同样的 letterbox 预处理尤其是填充值是否为 114YOLOv5 默认的灰度填充。差一个像素填充INT8 模型的结果就可能出现明显偏置。有一次在某边缘盒子上做调试发现同一张图片在 PC 上检测到三个猪脸设备上只检测到两个。排查到最后不是量化的问题而是输入尺寸不一致——PC 端 640代码里写死了 480。量化模型对输入尺寸非常敏感固定好输入尺寸后再做量化校准这个问题就消失了。从那以后我每次做部署验证都强制走一遍同样的流程先用 FP32 模型在目标设备上跑通再转 ONNX 做精度对拍最后才做 INT8 量化部署三步一步不跳。希望这个流程能让你在猪脸检测或者类似的单类目标检测项目里少走些弯路。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询