简介:面向猪圈场景的猪只行为识别数据集,专为需要真实养殖图像标注的目标检测研究者与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 "fps=1,scale=1280:720" -q:v 2 frame_%04d.jpg这里fps=1表示每秒抽1帧,scale=1280: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(f"class={class_map[cls_id]}, " f"box=({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(parents=True, exist_ok=True) # 假设文件名前缀是来源视频的段编号,如 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会输出mAP@0.5、mAP@0.5:0.95以及Precision、Recall指标。但标题里说的92.6%平均正确识别率,实际更贴近按类别统计的识别正确率平均值。具体计算方式是在测试集上逐类统计被正确识别的行为样本数除以该类总样本数,再对四类取平均。
理解这个口径很重要,因为直接用mAP@0.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", path="best.pt") window_size = 5 vote_buffer = deque(maxlen=window_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_size=5对于25fps的视频相当于0.2秒的决策窗口。如果视频帧率更低或者行为切换更急促,可以适当调小。对猪行为识别这个场景,窗口太大会导致短促的喝水行为被站立吞掉,太小则起不到平滑效果。实际部署时我会设置两个阈值:连续3帧出现新行为才切换当前状态,单帧新行为只记录不生效,这样对喝水这类短时动作更友好。
最后一个经验:这份数据集适合作为基线,但直接拿去部署到具体猪场前,一定要在你的目标猪圈录两小时视频做一次小样本测试。我接过一个模拟项目X,对方直接用公开数据集训练完就上线,结果因为现场机位高度和训练数据不一致,前三天正确率一直徘徊在80%出头,后来采集了现场120帧做微调才回到92%水平。模型在静态帧上的分数只是起点,行为识别最终比拼的从来不是单帧精度,而是时序稳定性。希望帮到你。
本文还有配套的精品资源,点击获取