YOLOv5+PyQt:从数据集标注到界面集成的生猪行为检测落地指南
发布时间:2026/10/1 1:56:10来源:尧图网络
简介一套面向养殖场智能化监控场景的YOLOv5生猪行为状态检测解决方案涵盖训练权重、1000余张标注图像及配套PyQt可视化界面。数据集已按train/val/test划分并配置好data.yaml包含eat、stand、lie、attack四类行为标签采用txt格式可直接用于YOLOv5、YOLOv7、YOLOv8、YOLOv9等主流算法的模型训练。资源包共2000个文件其中1210个txt标注文件与706张jpg图像构成核心训练数据另有34个Python脚本、28个yaml配置、5个shell脚本及多个文档兼顾训练部署与界面运行需求整包约274MB。已有447人学习使用。借助现成权重与清晰目录结构可快速复现猪只进食、站立、躺卧、攻击等行为的实时检测适合从事智慧养殖、目标检测算法应用的研究者或开发者进行二次开发与效果验证。1. 养殖场猪只行为检测为什么这套方案能直接落地凌晨两点的猪舍一盏红外摄像头对着产床画面里十来头猪挤成一团有一头正在咬另一头的耳朵。值班员隔着九宫格屏幕分辨这是打架还是闹着玩——这就是养殖场行为检测每天要面对的现场。标题这套方案把 yolov5 目标检测、生猪行为状态训练权重、1000 张标注数据集和 PyQt 桌面界面捆在一起解决从视频里实时框出进食、饮水、躺卧、争斗这些行为的问题。它适合正在做猪场监控改造、智慧养殖设备和农业算法落地的工程师不再人工盯屏行为变成每分钟可查的数字。这篇就从标注到训练、再包装成界面的完整路径讲透。2. 为什么是YOLOv5行为检测建模思路与1000张数据集的标注很多人一听到猪只行为检测第一反应是上视频行为识别搞个 SlowFast 或者 3D 卷积。真到猪舍里跑一圈就会明白养殖场要的不是这一段视频里有打架而是哪一头猪在打架、持续了多久、发生在几点。这个粒度问题决定了算法选型。2.1 行为检测的本质是目标检测先定位个体再给行为标签YOLOv5 目标检测天然契合这个诉求。模型输出的是每个目标的边界框和行为类别例如一只猪的框配上 fighting 标签。后续所有行为统计都建立在框之上每秒钟有几头猪在进食、哪个栏位争斗频次高——这些数据对养殖管理者才是有意义的。相比之下基于图像的分类网络只能回答整幅画面里存在什么行为没法回答是哪只猪姿态估计则要标注十几万关键点在毛发稀疏、肤色相近的猪身上关键点本来就难标。这几年市面上出现不少完整项目检测对象从鸟类到猪只技术栈几乎都是同一套目标检测算法加训练权重加桌面界面。对象变了建模的底层逻辑没变。2.2 猪舍场景的建模难点遮挡、尺度、类间相似在俯视摄像头画面里一个框经常同时含两三个个体小猪在画面里只有二十几个像素争斗和玩闹在外观上几乎没有差异。这三个难点直接决定后面怎么调参密集遮挡决定 NMS 的 IoU 阈值不能照搬默认值小目标决定输入分辨率从 640 提到 1280 值得一试类间相似决定类别定义不能贪多。另外如果你后续打算融合温湿度、声音等更多维度的多模态数据集图像这层也是必须先守住的底子单模态框图都画不齐多模态只会叠加噪声。我见过不少团队一上来就把数据集规划成图像声音红外结果光是图像标注一致性就没守住最后模型哪个模态都没学好。2.3 行为类别怎么定6类起步别贪多常见做法是定义 6 个基础行为类我一般建议这么分类别说明标注建议eating头部伸向料槽或有咀嚼动作框整个身体不要只框头drinking头部位于饮水器附近和进食容易混注意周围场景lying卧姿腹部贴地最简单别漏标注standing站立不动帧间位移小walking四蹄位移中和站立区分要看前后帧fighting追逐、撕咬、撞击最容易标错宁缺毋滥如果栏位还有明显的蹭痒、排泄可以加但每加一类你要保证该类别的有效实例至少在 150 个以上。1000 张图配 6 类已经是勉强够用的状态扩到 8 类以上每个类的样本量就摊薄了训练时弱势类的 AP 会掉得很难看。2.4 数据采集与标注1000张图的实际产出节奏采集策略从 3 到 4 个不同栏位、不同时段的视频里抽帧间隔 5 到 10 秒一帧避免连续帧高度相似。如果摄像头角度差异大每个角度尽量均匀分布。白天、夜间、红外灯开启与否都要有样本。标注工具用 LabelImg 或者 X-AnyLabeling直接导出 YOLO 格式最省事。如果手里已有 VOC 格式需要转一下。以 XML 转 txt 为例import glob, xml.etree.ElementTree as ET def voc_to_yolo(xml_path, out_txt, class_names): root ET.parse(xml_path).getroot() size root.find(size) w, h int(size.find(width).text), int(size.find(height).text) lines [] for obj in root.iter(object): name obj.find(name).text if name not in class_names: continue cid class_names.index(name) box obj.find(bndbox) x1, y1 float(box.find(xmin).text), float(box.find(ymin).text) x2, y2 float(box.find(xmax).text), float(box.find(ymax).text) x_c, y_c (x1 x2) / 2 / w, (y1 y2) / 2 / h bw, bh (x2 - x1) / w, (y2 - y1) / h lines.append(f{cid} {x_c:.6f} {y_c:.6f} {bw:.6f} {bh:.6f}) with open(out_txt, w) as f: f.write(\n.join(lines)) class_names [eating, drinking, lying, standing, walking, fighting] for xml in glob.glob(annotations/*.xml): voc_to_yolo(xml, xml.replace(.xml, .txt).replace(annotations, labels), class_names)这段逻辑是把 VOC 的绝对坐标换算成归一化的中心点宽高一行一个目标类别 ID 从 0 开始。两个最容易踩的坑一是类别 ID 顺序必须和之后 data.yaml 里的 names 完全一致否则训练标签全部错位二是某些标注软件会把超边界的框写进去转换前先做一次 clip否则训练早期 loss 出现 NaN 不是偶然。标注产出节奏按人头算1000 张图、单张 15 到 30 个框一个人全职干大约两个到三个工作日能完成初版质检再花一天。不用追求一次标完先粗标一轮跑通训练再用模型预测出的错误回去修正效率会高很多。3. 用YOLOv5训练自己的生猪行为数据集环境配置、参数选择与权重产出标题里的训练权重不是拿来一个 .pt 文件直接用而是从你自己的数据训出来的。这一章把环境、数据、训练命令和参数取舍讲清楚。3.1 conda配置YOLOv5环境锁版本比追新重要第一步环境我习惯锁到 v7.0git clone https://github.com/ultralytics/yolov5 cd yolov5 git checkout v7.0 conda create -n yolov5 python3.10 -y conda activate yolov5 pip install -r requirements.txtPython 3.10 加 torch 2.x 跑 v7.0 没有兼容性问题不要盲目 clone 最新 main改动大、相关 issues 多训练同一个模型反而要多花时间排雷。如果 GPU 是 30 系或 40 系CUDA 顺手装 11.8 或 12.1 配套的 PyTorch再装 requirements 里其余依赖。注意 torch 必须先装好否则 pip 会根据 requirements 把 torch 装成 CPU 版后面训练慢到怀疑人生。3.2 数据集文件结构与data.yaml的写法目录推荐这样组织pig_behavior/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── pig_behavior.yamlimages 和 labels 里的 txt 同名对应。data.yaml 内容如下train: pig_behavior/images/train val: pig_behavior/images/val nc: 6 names: [eating, drinking, lying, standing, walking, fighting]路径建议写绝对路径YOLOv5 对相对路径的解析有时会受当前工作目录影响。划分比例上1000 张图我通常按 80/20 或 85/15 切不做单独 test 集val 就是盲测集。labels 里多一个空 txt 没关系但 images 里一定不要有没标注的图漏进 train否则那一张图会被当成背景训练拉低召回。3.3 跑通第一次训练最小命令与预训练权重用 yolov5s 预训练权重开始官方 Release 里下载不要用随机初始化。训练命令python train.py \ --data pig_behavior.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 150 \ --hyp data/hyps/hyp.scratch-low.yaml \ --cache \ --name pig_behavior--cache 把图片缓存进内存1000 张图只要内存够训练提速明显--hyp 用 scratch-low 而不是高增强。第一次训练建议保持默认增强数据少时高增强反而把目标形状破坏。实际训练中 GPU 显存在 8G 以上可以把 --batch 提到 326G 以下降到 8batch 太小 BN 不稳定loss 曲线会抖。有人问为什么不直接用 yolov8 的预训练权重。v8 的权重换过来也能跑但当前任务瓶颈在遮挡和标注一致性不是网络结构差异yolov5s 在这个数据体量下更稳生态里配套的部署工具也更成熟。3.4 训练参数取舍img、batch、epochs、cache的边界参数默认值参考养殖场场景怎么调--img640小猪小目标多时试 1280batch 要减半显存需求约翻 4 倍--batch168G 显存可以到 32显存不足时优先降 batch不要降 img--epochs1001000 张数据建议 150 到 200配合 patience 早停--hypscratch-low数据量小别用高增强增强容易破坏动物形状--patience100设成 30 到 50val mAP 不涨就停避免白跑--device0多卡填 0,1单卡别开 DataParallel麻烦多收益小如果 --weights 换成 yolov5m.pt对遮挡的鲁棒性会好一些但推理速度下降约 40%后面要部署在树莓派这类设备上就得想清楚。我一般先出 s 跑通再在 val 上对比 mmAP 差异小于 1 个点就不要换。3.5 看训练日志从results.csv判断什么时候停训练跑起来后runs/train/pig_behavior/ 目录下会实时更新 results.csv 和 results.png。重点关注 train/box_loss、val/mAP_0.5、val/mAP_0.5:0.95 这三列。如果 val/mAP_0.5 连续 30 个 epoch 不涨果断停掉用 best.pt。注意 best.pt 代表验证集最优last.pt 代表最后一个 epoch。数据少的情况下val 偶尔冲高可能只是偶然不能只看 best.pt 不看趋势——打开 results.png 看曲线尾部是平稳还是还在爬升。如果 train loss 持续下降但 val mAP 停滞说明过拟合回到增强和 epochs 部分调整。4. PyQt界面集成把检测权重变成可交付的桌面工具训练完拿到 best.pt下一步是把它塞进一个能点按钮、能看画面的界面。PyQt 是这个环节最常见的选型它跨平台、控件全和 OpenCV 的衔接也顺。4.1 界面拆成三块视频区、控制区、统计区写 PyQt 之前先拆功能边界避免把界面写成一个大函数。我的做法是 QMainWindow 左侧放 QLabel 显示画面右侧放控制按钮打开视频、打开摄像头、开始暂停、置信度滑条和一个 QTableWidget 统计当前帧各类别数量。更复杂的趋势图可以后续嵌 pyqtgraph但第一次交付不用。4.2 把detect.py的推理逻辑抽成Detector类yolov5 的 detect.py 面向命令行和文件输出直接塞进 PyQt 会非常别扭。核心是把模型加载和推理封装成独立类import torch import numpy as np from models.experimental import attempt_load from utils.general import non_max_suppression, scale_coords from utils.augmentations import letterbox class PigDetector: def __init__(self, weightsbest.pt, device0, conf0.25, iou0.45): self.device torch.device(cuda:0 if device 0 and torch.cuda.is_available() else cpu) self.model attempt_load(weights, deviceself.device) self.stride int(self.model.stride.max()) self.names self.model.names self.conf, self.iou conf, iou def infer(self, frame): h, w frame.shape[:2] im letterbox(frame, (640, 640), strideself.stride)[0] im im.transpose((2, 0, 1))[::-1] im np.ascontiguousarray(im) im torch.from_numpy(im).to(self.device).float() / 255.0 if im.ndim 3: im im.unsqueeze(0) pred self.model(im, augmentFalse)[0] det non_max_suppression(pred, self.conf, self.iou, classesNone)[0] if det is not None and len(det): det[:, :4] scale_coords(im.shape[2:], det[:, :4], (h, w)).round() return det, self.namesletterbox 保持长宽比填充尺度还原必须用 scale_coords 回到原图坐标否则框会整体偏移。attempt_load 支持 best.pt 和 last.ptconf 和 iou 放在类属性里方便界面滑条实时改。4.3 QThread跑推理线程信号回传画面PyQt 界面卡死的经典原因就是推理放在主线程。torch 推理加前后处理在 CPU 上大约 100 到 300ms 一帧界面必然无响应。解法是 QThread 加信号槽from PyQt5.QtCore import QThread, pyqtSignal import cv2, time class InferenceThread(QThread): frame_ready pyqtSignal(object, object) def __init__(self, detector, video_source, fps15): super().__init__() self.detector detector self.cap cv2.VideoCapture(video_source) self.fps fps self.running True def run(self): while self.running: ret, frame self.cap.read() if not ret: break det, names self.detector.infer(frame) self.frame_ready.emit(frame, det) time.sleep(1.0 / self.fps) def stop(self): self.running False self.wait()cap.read 和 infer 都在 run 内串行frame_ready 信号把原始帧和检测结果一起发射给主线程。主线程只做绘制和计数耗时几毫秒界面保持流畅。视频源可以是摄像头索引 0也可以是 rtsp 流或 mp4 路径。摄像头断开时 cap.read 返回 False循环要优雅退出。4.4 检测框绘制与中文标签显示检测框渲染最直接是用 cv2 画到帧上再转 QImagedef frame_to_qimage(cv_frame): rgb cv2.cvtColor(cv_frame, cv2.COLOR_BGR2RGB) h, w, ch rgb.shape return QImage(rgb.data, w, h, ch * w, QImage.Format_RGB888)绘制时如果标签是英文直接用 cv2.putText界面上的中文可以通过名称映射表或者 QPainter 绘制。我一般做法是界面用中文映射表显示画面里保持英文类名避免中文字体路径问题。5. 养殖场场景避坑5个让行为检测项目翻车的典型问题这一章全是真金白银的踩坑记录。每条按现象、原因、解决三步来写都是你在猪舍现场会真实撞上的问题。5.1 争斗和玩闹分不清模型AP崩盘现象训练日志里 fighting 类的 AP_50 只有 0.3 左右val 图片里经常把追逐玩耍标成争斗现场误报多。原因两个行为在单帧外观上几乎没有差异帧间运动特征才分得开单帧检测模型信息量不足。解决采集数据时在视频里明确记录争斗事件时间点标注时宁可把模棱两可的样本去掉类别质量优先于数量。另一个缓解办法是把争斗类拆成追逐和撕咬但更常见的是合并成一类再用运动检测做二次确认。这个类别是最容易让人翻车的我在这上面重置过三版标注。5.2 白天的权重拿到夜间红外下漏检现象白天训练 mAP_0.5 有 0.85夜里现场测试直接掉到 0.6红外画面下漏检一大片。原因训练集里夜间红外帧占比不足红外图像的颜色分布和白天差异极大模型学到的纹理特征不匹配。解决抽帧时按时间段分层保证夜间帧占 20% 以上。通用做法是先在全部数据上训一版再单独用夜间数据微调几十个 epoch产出 night.pt 和 day.pt 两套权重界面上按时间段切换。5.3 俯视小目标框画不全NMS把相邻猪连框现象小猪只有二十几个像素时检测框要么只框住半个身体要么两三个框被 NMS 合并成一个计数比实际少。原因输入分辨率 640 下小猪在特征图里占不到一个网格下采样 32 倍后特征被淹没NMS 默认 IoU 阈值 0.45 对密集场景偏严。解决--img 提到 1280 最直接显存不够时用 yolov5s 加 TensorRT 再压推理时间NMS 的 IoU 阈值下调到 0.3 到 0.35给相邻目标多留一些保留空间。这属于调参玄学最终要在现场视频上抽帧验证计数误差不要只信 val mAP。5.4 界面一开摄像头就白屏无响应现象PyQt 界面点开始检测后整个窗口冻结拖动都没反应几秒后系统提示未响应。原因推理和绘图都在主线程torch 前向阻塞了 Qt 事件循环。解决回看 4.3 节的线程方案worker 跑推理单一信号回传。另有一个隐藏坑在 QThread 里创建 Detector 时要让模型加载发生在 run 内否则跨线程的 CUDA context 会报错或者直接崩。5.5 1000张数据集过拟合val好看换一栋猪舍就废现象自己测的视频里框得不错到另一栋猪舍地面颜色、栏位结构不同漏检率立刻升高。原因1000 张图撑不起全部场景变化模型把地面和栏杆纹理当作猪的上下文特征学进去了。解决采集时尽量覆盖多猪舍、多角度、多光照这是唯一治本的路训练侧用 mosaic 和 mixup 增强预测侧开启 TTA 做验证对比但现场实时推理不建议开 TTA。数据不够时我一般会把新场景自采的视频再抽 100 到 200 帧做半自动标注用已有 best.pt 预标注人工修正后追加训练。这也是为什么标题里强调 1000 数据集——它只够把这个流程跑通距离产品化还差一个持续采集迭代的循环。6. 进阶导出ONNX提速与分钟级行为统计模型和界面跑通后还有两件直接影响验收的事推理速度和行为报表。python export.py --weights best.pt --include onnx --opset 12导出 ONNX 后用 onnxruntime 在 CPU 上推理通常比 PyTorch 前向快 2 到 3 倍这对没有 GPU 的猪场现场非常关键。注意导出后 NMS 后处理要自己实现可以选择把 NMS 放进 ONNX 图里也可以保留 PyTorch 做 NMS、ONNX 只做前向。我实践下来后者更省心运维排错容易。行为统计在 InferenceThread 里做每帧拿到 det 后按类别计数每 60 秒聚合一次写入 CSV。用一个字典维护当前分钟的累计值跨分钟时落盘再清零继续。统计字段包括时间戳、各类别出现次数、参与个体数估算。这份 CSV 就是给养殖场管理者的日报数据源也是检验模型价值的直接物证。最后说一个我自己的血泪经验第一批项目交付时我只盯着 val mAP没有留一段完全没有参与训练的视频做盲测结果现场翻车。后来把验证流程固定成——每次训练完必须抽一段时间连续、场景独立的视频按帧对比自动框和人工框统计 F1这个 F1 才是汇报给养殖场的验收指标。建议你也把这个流程写进项目计划里别等上线了才后悔。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网