资讯动态

基于YOLOv5的猪行为识别实战:数据集、训练与避坑指南

发布时间:2026/10/18 5:34:59 来源:尧图企业网站定制
简介面向猪圈场景的猪只行为识别数据集专为需要真实养殖图像标注的目标检测研究者与YOLOv5/PyTorch学习者设计可支撑猪舍监控、行为统计与异常状态预警等应用开发。数据涵盖饮水、进食、睡觉、站立四类常见行为平均正确识别率达92.6%在同类猪只行为数据集中具备较强可用性。压缩包内共2000个文件包括1272个txt标注文件、727个jpg原始图像和1个yaml类别配置文件整体大小85.86MB标注采用YOLO格式文本导入YOLOv5训练环境后可按常规流程完成格式检查、类别映射与模型训练。目前已有314人学习下载适合作为课程设计、本科毕业设计或农业智能化项目的起步数据。拿到资源后可直接用于模型训练与验证省去自行采集图像、人工标注和整理格式的时间便于将精力集中在网络调参和检测效果优化上。1. 猪行为识别数据集1272张图如何支撑92.6%的正确率第一次拿到这个数据集说明时我第一反应是怀疑1272张图片要识别喝、吃、睡觉、站立四类猪行为平均正确识别率还能到92.6%做过养殖监控的人都知道猪圈场景光照差、目标互相遮挡、行为交替又快很多项目丢进去几万张图都未必能稳定跑起来。但仔细看这份数据集的约束条件后我意识到它选了一个非常聪明的切入点场景封闭、机位固定、单目标姿态差异大行为类别之间重叠少。这使得小数据量也能训练出可用的检测模型。对正在做智能养殖、畜牧视觉监控或者边缘端行为分析的开发者来说这份数据集的价值在于给你一条完整的、可复现的落地路径四类行为怎么定义边界、标注格式怎么组织、YOLOv5s 最少调哪几个参数、以及哪些位置最容易翻车。这篇文章就按这条路径完整拆开讲。2. 行为定义与数据构成四类行为的边界在哪里2.1 喝、吃、睡觉、站立的视觉特征与判定标准做行为识别数据集第一步不是标框而是定标准。同一帧画面交给不同的人标可能一个人认为是站立另一个人认为是吃食。这个数据集把行为收敛成四类背后的视觉特征其实非常清晰。喝水的典型姿态是猪嘴部接触饮水器或水嘴头部下压颈部伸长身躯基本静止。吃食的特征是头部在食槽区域内有规律咀嚼动作嘴部贴近槽底身体可能轻微晃动。睡觉的特征最直观猪处于躺卧姿势侧躺或伏卧眼睛闭合呼吸平稳头部不抬离地面。站立则是四肢支撑身体头部抬起或水平没有明显位移和进食动作。单看描述好像很容易区分但落到真实视频帧里边界是模糊的。猪从站立到低头喝水之间有一串过渡帧喝水间歇抬头张望时算什么吃食过程中突然停下来发呆算什么这类模糊帧在标注时必须有一条硬规则。常见做法是每个标注人员拿到一份行为判定手册里面用连续视频片段截图对每个行为给出起止判定规则。比如喝水行为必须以“嘴部接触水嘴”为起始标志以“嘴部离开水嘴超过3秒”为终止标志中间抬头张望不打断喝水类别。吃食则以“嘴部进入食槽区域且伴随咀嚼动作”为准头部离开食槽区域即视为结束。2.2 1272张图片的分布逻辑与类别平衡问题1272张图做四类检测平均每类约318张。但实际采集过程中行为天然不均衡。猪一天的作息里睡眠时间最长站立次之喝水和吃食时间相对短。如果按时间均匀抽帧睡眠类别会占到四成以上喝水类别可能只有不到两百张。类别失衡对YOLO训练的影响在后面的训练章里会专门讲这里先说数据层面的应对手段。我在处理类似数据集时一般不会从原始视频里均匀抽帧而是先在视频时间轴上做行为区间标注再按行为区间分别抽帧。睡眠区间抽帧间隔拉大比如每3秒抽1帧喝水和吃食区间抽帧密度加大每0.5秒抽1帧。这样1272张图里四类行为基本能控制在25%上下的占比。实际操作中你会发现喝水类样本哪怕刻意加密采集数量还是偏少。这时候不要硬抽重复帧充数重复帧不仅不会提升泛化能力还会让验证集分数虚高。2.3 从视频抽帧到图片筛选的完整流水线数据准备阶段可以用ffmpeg完成抽帧。假设原始视频是猪舍固定机位的24小时监控录像常见做法是先按时间段切分把白天和夜间红外段分开处理避免风格混在一起。抽帧命令通常长这样ffmpeg -i input.mp4 -vf fps1,scale1280:720 -q:v 2 frame_%04d.jpg这里fps1表示每秒抽1帧scale1280:720统一分辨率-q:v 2控制输出JPG质量为高质量。分段抽帧结束后还需要做一轮人工筛选剔除严重运动模糊的帧、强反光导致目标丢失的帧、以及几乎完全相同的重复帧。运动模糊帧是检测模型的大敌尤其猪快速转头时标注框内的猪头轮廓是糊的模型学到的是残影特征推理阶段遇到清晰帧反而容易漏检。筛完一轮一般能砍掉20%左右的图剩下接近1272张就是可用的标注素材。3. 标注格式解析与数据集划分YOLOv5 PyTorch格式的完整结构3.1 标签文件的存储格式与类别映射规则这份数据集支持YOLOv5 PyTorch格式标注意味着所有标注文件是txt格式与图片同名存放在对应的labels目录下。每行标注一个目标格式为class_id x_center y_center width height其中坐标值是相对于图片宽度和高度的归一化数值取值范围在0到1之间。在行为识别数据集里类别映射需要与训练配置保持一致。如果想在YOLOv5上直接训练类别顺序建议固定为0代表喝水1代表吃食2代表睡觉3代表站立。顺序一旦确定后续在模型配置文件和推理脚本里都不得改动否则会出现类别标签错位。下面这段Python脚本可以快速解析一个标注文件验证坐标是否在合理范围内并还原出像素坐标方便可视化检查# parse_yolo_label.py import os label_path labels/train/0001.txt img_w, img_h 1280, 720 # 与训练图片分辨率保持一致 with open(label_path, r) as f: lines f.readlines() class_map [drink, eat, sleep, stand] for line in lines: parts line.strip().split() cls_id int(parts[0]) x_center float(parts[1]) * img_w y_center float(parts[2]) * img_h width float(parts[3]) * img_w height float(parts[4]) * img_h x1 x_center - width / 2 y1 y_center - height / 2 x2 x_center width / 2 y2 y_center height / 2 print(fclass{class_map[cls_id]}, fbox({x1:.1f}, {y1:.1f}) - ({x2:.1f}, {y2:.1f}))这段脚本的核心作用是做标注合法性检查。归一化坐标常见的问题是小于0或大于1多数情况下是因为标注工具导出时勾选了错误的图片尺寸。另一类问题是宽高为0通常是标注时误点了零尺寸框。跑一遍脚本能快速把这些脏数据找出来避免训练过程中出现NaN loss。格式检查之后还要核对类别ID是否越界。YOLOv5在读取标签时如果遇到class id大于配置文件中nc-1的情况会直接跳过该目标你的数据集里就少了一个正样本指标莫名下降却找不到原因。这类问题用上面的脚本逐文件扫一遍就能暴露出来。3.2 标注工具与操作要点常见做法是用labelImg完成这批数据的标注。labelImg支持YOLO格式输出设置好类别文件后每画一个框自动写入txt。这里有一个容易踩的细节labelImg默认保存的坐标是归一化格式但如果图片路径或类别文件配置不对会保存成Pascal VOC的xml格式训练脚本直接读不到。启动标注前要把标注格式明确切换到YOLO模式并确认类别顺序和上文一致。标注框的绘制范围也影响模型表现。猪行为识别中我们关注的是行为相关区域而不是整头猪。喝水行为的标注框应该框住头部和嘴部区域而不需要包含整个身体站立行为的框则可以框全身因为站立姿态的特征分布在四肢和躯干。混合标注尺度会让检测器陷入维度冲突性能难以稳定在92%以上。标注完成后还需检查标签文件中每张图的目标数量。数据集中单张图片可能存在多头猪但行为发生的主体往往只有一两个。如果画面上三头猪都在睡觉就标三个睡觉框如果一头猪在吃食另外两头站着就要分别标注对应类别。漏标框会被自动当作背景模型在推理时即使看到也倾向于忽略。3.3 数据集划分按图片还是按来源视频切分划分train和val的方式直接影响最终正确率这也是最容易出现虚高分数的地方。如果你按所有图片随机划分1272张图有训练集和验证集来自同一视频的相邻帧画面背景、光圈、猪的位置极度相似。模型相当于开卷考试验证正确率会冲到95%以上但实际部署到新视频上就掉到85%甚至更低。我的做法是按视频片段划分而不是按单张图片划分。先把视频按时间段分成多个片段对片段编号让同一片段的帧只进入训练集或验证集。对1272张图这个规模一般来说训练集取80%、验证集取20%是比较合理的比例。分割脚本如下# split_dataset.py import random import shutil from pathlib import Path random.seed(42) image_dir Path(images_all) label_dir Path(labels_all) train_img Path(images/train) val_img Path(images/val) train_lbl Path(labels/train) val_lbl Path(labels/val) for d in [train_img, val_img, train_lbl, val_lbl]: d.mkdir(parentsTrue, exist_okTrue) # 假设文件名前缀是来源视频的段编号如 vid02_0001.jpg images sorted(image_dir.glob(*.jpg)) segment_ids sorted({im.stem.split(_)[0] for im in images}) random.shuffle(segment_ids) split_point int(len(segment_ids) * 0.8) train_segments set(segment_ids[:split_point]) val_segments set(segment_ids[split_point:]) for im in images: seg im.stem.split(_)[0] lbl label_dir / (im.stem .txt) if seg in train_segments: shutil.copy(im, train_img / im.name) shutil.copy(lbl, train_lbl / lbl.name) else: shutil.copy(im, val_img / im.name) shutil.copy(lbl, val_lbl / lbl.name)代码里最关键的是第10行按文件名前缀的段编号区分视频来源。如果你的命名没有这个前缀划分前需要先把帧归属写到元数据里。这样划分后验证集面对的才是“新场景”下的目标得到的指标才接近真实部署水平。4. 基于YOLOv5s训练行为识别模型关键参数与复现路径4.1 环境准备与模型选型理由拿到这份数据集后我首选YOLOv5s而不是更重的YOLOv5l或x。原因有三个一是行为检测目标本身是猪这一类形体结构稳定的动物不需要超大模型来捕捉极端细节二是很多养殖场部署环境是Jetson或者普通工控机算力有限s模型在保证精度的同时推理速度快不少三是1272张数据量本身也不足以驯服参数量更大的模型用l级模型很容易陷入过拟合。环境准备遵循YOLOv5官方仓库的标准流程即可。依赖装好后需要重点确认PyTorch版本与CUDA版本匹配。常见的翻车点是torch.cuda.is_available()返回False但代码不报错模型在CPU上慢慢磨训练速度慢了十倍。启动训练前先执行一行检查命令python -c import torch; print(torch.cuda.is_available(), torch.cuda.get_device_name(0))输出为True并显示显卡型号再进入正式训练流程。4.2 数据集配置与训练命令数据集配置文件是训练入口的枢纽。在YOLOv5的data目录下新建pig.yaml内容如下# pig.yaml train: /data/pig_dataset/images/train val: /data/pig_dataset/images/val nc: 4 names: [drink, eat, sleep, stand]这里的train和val建议写成绝对路径避免相对路径在不同工作目录下解析出错。nc是类别数量names的顺序必须与标注文件里的class_id对应。如果标注时用的类别顺序是drink、eat、sleep、stand这里就要保持完全一致。训练命令采用如下形式python train.py --img 640 --batch 16 --epochs 100 --data pig.yaml --weights yolov5s.pt --cache参数说明--img 640将训练与推理的输入分辨率统一为640x640。猪圈视频多为固定机位目标大小相对稳定640足够捕捉嘴部区域细节继续调到1280对显存要求翻倍收益却有限。--batch 16在显存允许的情况下可以调大数据量小的场景batch越大收敛越稳。--epochs 100对这个数据规模而言是起步值后面可以根据验证集曲线决定是否继续。--weights yolov5s.pt使用COCO预训练权重做迁移学习起点比从零训练收敛快很多。--cache将图片加载进内存小数据集下能显著减少IO等待。训练过程中要盯两个曲线BoxLoss和ClsLoss是否稳定下降以及验证集的mAP是否出现平台期。如果Loss在下降但验证mAP连续20个epoch不涨说明模型开始记住训练集的细节而非泛化特征应该提前用Early Stopping截断训练。4.3 正确率指标与混淆矩阵的读法训练结束后YOLOv5的val.py会输出mAP0.5、mAP0.5:0.95以及Precision、Recall指标。但标题里说的92.6%平均正确识别率实际更贴近按类别统计的识别正确率平均值。具体计算方式是在测试集上逐类统计被正确识别的行为样本数除以该类总样本数再对四类取平均。理解这个口径很重要因为直接用mAP0.5去和92.6%对比会发现数值偏低。mAP考虑了置信度阈值变化而正确识别率是选一个固定置信度阈值后的结果。如果你想在推理端复现92.6%这个数值先让val.py输出混淆矩阵然后选定置信度阈值让四类的查准率达到均衡。混淆矩阵的读法也有讲究。行列交叉处的高数值意味着行为误判方向。如果eat列被大量归入drink说明两个行为的嘴部动作特征在模型内部没有完全分开需要回到标注侧检查边界样本而不是盲目增加训练轮数。这类错误通常增加模糊帧样本比单纯加数据更有效。5. 行为识别落地的六个避坑点从类别失衡到夜间帧翻车5.1 睡眠帧占比过高导致类别失衡现象模型训练完成后睡觉行为的Recall非常高但喝水、站立行为的Recall明显偏低。原因数据采集环节没有控制类别分布睡眠帧占比超过40%。YOLOv5的损失函数会倾向学习样本量大的类别小类别虽然也在训练但梯度贡献被大类别稀释。解决重新抽样把睡眠帧砍到与最少类别相当的数量级。如果砍掉后总图数不足再补采一段专门覆盖喝水和吃食的视频。抽样时保留时间间隔避免连续帧堆积。5.2 夜间红外帧与白天可见光帧混用现象训练集同时包含白天彩色帧和夜间红外灰度帧训练结束验证集正确率尚可实际部署到夜间监控画面时漏检率飙升。原因YOLOv5的输入是RGB三通道红外帧用伪彩色映射后纹理分布和白天帧差异极大。模型学到的特征倾向于白天的颜色纹理红外帧在特征空间里落在分布边缘。解决按光源类型分别训练两个模型或者只保留一种光源的数据做训练。如果只能有一个模型将白天帧数据增强中加入灰度化让模型弱化颜色依赖但效果不如分开训练稳定。5.3 多头猪互相遮挡导致漏检现象猪群挤在一起吃食时检测框数量少于实际猪头数行为识别率明显下降。原因遮挡后相邻猪的检测框IoU过大NMS后处理阶段把两个框合并成一个丢失了一个目标。解决在推理时将NMS的IoU阈值从默认的0.45下调到0.3允许低重叠框同时保留。同时检查标注阶段是否确实给遮挡猪标了两个框。如果标注时只标了可见部分模型学到的就是“可见半头猪”特征预测时反而不会输出完整框。5.4 按帧随机划分导致验证集虚高现象train和val同源视频帧val的mAP比最终现场测试高5到8个百分点。原因同一视频前后帧高度相似随机划分后训练集里包含了验证帧的邻近帧模型在验证时看到的几乎是与训练样本同构的画面。解决按视频片段划分数据或者按时间窗口划分保证同一视频片段只出现在一侧。这个坑在数据集规模越小时越致命1272张图如果不按来源切割验证指标基本没有参考价值。5.5 行为边界模糊导致的标注不一致现象混淆矩阵里drink和eat互相误判的比例偏高且多发生在头部低垂的帧画面。原因猪喝水和吃食时头部都向下低垂嘴部区域在画面中形态相似标注阶段不同人员的判据不一致导致同形样本被贴上不同标签。解决在标注规范里补充“接触水嘴”和“接触食槽”两个强判定条件并安排同一人对争议帧复标。也可以用录像回放确认前后几秒的动作趋势来修正单帧标签而不是只看静态图。5.6 背景过拟合导致新场景崩溃现象在A猪圈训练出的模型换到B猪圈后正确率从92%掉到70%。原因猪圈围栏、食槽、饮水器在画面中占比大模型学到很多背景纹理特征而非猪行为本身的姿态特征。解决训练阶段使用较大的随机裁剪和HSV增强降低背景纹理的权重。更有效的手段是换场景采集100到200张背景图作为负样本加入训练集让模型学会忽略背景。也可以在推理前对图像做自适应对比度归一化减弱不同猪圈光照差异的影响。6. 从单帧到行为序列滑窗投票提升识别稳定性单帧检测模型只能给出一张图片里的行为类别但实际养殖监控里我们更关心一段连续时间内的行为状态。单帧误判不可避免猪抬头瞬间可能被模型误判为站立喝水间歇也可能被判成吃食。把这些抖动输出直接用于统计会发现行为时长记录来回跳变报表没法看。我常用的方案是滑窗投票。维护一个长度为N的检测结果队列对每一帧送入得到的行为类别加入队尾同时弹出队首最旧的一帧然后对队列内N个结果做多数投票以票数最高的类别作为该时刻的最终行为。这样单帧偶发误判被平滑掉行为切换需要连续多帧确认后才生效。脚本如下# sliding_window_vote.py from collections import deque, Counter import torch model torch.hub.load(ultralytics/yolov5, custom, pathbest.pt) window_size 5 vote_buffer deque(maxlenwindow_size) def infer_behavior(frame): results model(frame) # 取置信度最高的检测框作为当前帧行为 detections results.pandas().xyxy[0] if detections.empty: return None top detections.loc[detections.confidence.idxmax()] return top[class] # 每帧调用一次 frame_behavior infer_behavior(frame) if frame_behavior is not None: vote_buffer.append(frame_behavior) if len(vote_buffer) window_size: final_behavior Counter(vote_buffer).most_common(1)[0][0] # final_behavior 作为该时刻的实际行为输出代码里的window_size5对于25fps的视频相当于0.2秒的决策窗口。如果视频帧率更低或者行为切换更急促可以适当调小。对猪行为识别这个场景窗口太大会导致短促的喝水行为被站立吞掉太小则起不到平滑效果。实际部署时我会设置两个阈值连续3帧出现新行为才切换当前状态单帧新行为只记录不生效这样对喝水这类短时动作更友好。最后一个经验这份数据集适合作为基线但直接拿去部署到具体猪场前一定要在你的目标猪圈录两小时视频做一次小样本测试。我接过一个模拟项目X对方直接用公开数据集训练完就上线结果因为现场机位高度和训练数据不一致前三天正确率一直徘徊在80%出头后来采集了现场120帧做微调才回到92%水平。模型在静态帧上的分数只是起点行为识别最终比拼的从来不是单帧精度而是时序稳定性。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑