简介:一套面向智慧养殖场景的猪行为识别数据集,适合计算机视觉研发人员、农业工程研究者及畜牧智能化项目开发者使用。数据集聚焦猪圈内猪只的日常行为自动识别,覆盖喝、吃、睡觉、站立四个关键动作类别;标注文件采用COCO JSON格式,可直接用于目标检测、行为分类等模型的训练与评测,免去自行采集和标注的繁琐流程。整套资源共1275个文件,包含1272张JPG图像与3个JSON标注文件,压缩包约85.57MB;图像主要来自猪舍监控视频抽帧,视角和光照条件多样,有助于提升模型在实际场景下的泛化能力。数据集的平均正确识别率达92.6%,说明标签质量和可训练性较好,在同类小型行为数据集中具有较高的参考价值。目前已有213人学习浏览,适合需要快速获得带标注猪行为样本、开展算法对比实验或搭建智慧养殖监测系统的用户。
1. 猪行为识别数据集:从 1272 张图到 92.6% 正确率的落地路径
当摄像头对着猪栏时,真正难的不是检测出一头猪,而是判断这头猪正在喝、吃、睡觉还是站立。猪圈光线暗、猪体互相遮挡、目标尺度变化大,通用检测器在这一场景下的行为分类精度很容易掉到 85% 以下。这套猪行为识别数据集正好补上这块缺口:1272 张猪圈实拍图像,标注覆盖喝、吃、睡觉、站立四类常见行为,平均正确识别率达到 92.6%,标注格式为 COCO JSON,可直接用 pycocotools 读取,也可以作为 YOLOv8 等检测框架的训练语料。本文先从数据组织方式说起,再给出基于 CVAT 的标注、COCO 转 YOLO 切分、YOLOv8 训练评估,最后落到一个复现高正确率的验证技巧。适合正在做智慧养殖、目标检测或行为识别落地的算法工程师和数据集生产者。
2. 解构猪行为识别数据集:COCO JSON 的类别、标注与统计
2.1 四类行为标注的定义与数据分布
行为识别的第一件事是定义“谁在干什么”。在本数据集中,四类行为的标注口径并不仅看猪的静态姿态,还会参考头部所在位置和运动状态:
喝:猪的嘴部靠近饮水器或水槽,头部有规律抬起下压动作;吃:嘴部位于料槽内,伴随咀嚼,有时前腿跨入料槽;睡觉:身体趴卧或侧躺,头部贴地或闭眼,持续数秒不动;站立:四腿支撑,躯干直立,移动或静止都算。
从数据分布看,站立往往占比最高,因为猪在栏内的日常时间大量处于站立调温、观望状态;睡觉次之;喝和吃是短时行为,数量相对少。表里是常见的数据分布形态,具体数字以标注文件为准:
| category_id | 行为类名 | 典型姿态 | 易混淆行为 |
|---|---|---|---|
| 0 | drinking | 嘴部接触水嘴/水槽 | eating(头部压低) |
| 1 | eating | 嘴部在料槽内并咀嚼 | drinking(头部位置低) |
| 2 | sleeping | 卧倒、闭眼、持续静止 | standing(四肢蜷缩) |
| 3 | standing | 四肢支撑、身体直立 | eating(低头时) |
如果训练时发现类别严重不平衡,常见做法是给少数类提高 loss 权重,或者在图像增强阶段对包含 drinking/eating 的样本做随机裁剪复制,让模型不会把所有矮姿态都判定成睡觉。
2.2 用 Python 统计 COCO JSON 中的类别与框信息
拿到 COCO JSON 后,别急着灌进模型。先用一段脚本确认类别 ID 是否连续、单图标注密度是否合理。这里用纯 json 解析,避免额外安装 pycocotools:
import json from collections import Counter def inspect_coco(coco_path): with open(coco_path, 'r', encoding='utf-8') as f: data = json.load(f) cat_name = {cat['id']: cat['name'] for cat in data['categories']} ann_count = Counter() img_annotations = {img['id']: [] for img in data['images']} for ann in data['annotations']: ann_count[cat_name[ann['category_id']]] += 1 img_annotations[ann['image_id']].append(ann) print('总图像数:', len(data['images'])) print('总标注框数:', len(data['annotations'])) print('每类标注数:', dict(ann_count)) print('平均每图标注数: {:.2f}'.format( sum(len(v) for v in img_annotations.values()) / len(data['images']))) inspect_coco('pig_behavior_coco/annotations/instances_train.json')逻辑说明:data['images']保存图片宽高,data['annotations']里的 bbox 字段是[x, y, width, height],坐标原点在图像左上角,单位是像素。代码先把 category_id 映射成可读名称,再统计各类标注总数与单图密度。
参数说明:如果 annotation 文件很大,可以考虑将 images/annotations 分别读入后用 image_id 建立索引,而不是每次循环都遍历 images;COCO 官方推荐用 pycocotools 的 loadRes,但在工程脚本里用字典索引更快。若打印出某类数量为 0,说明标注文件类别不均衡或导出的 categories 字段没有包含该类,需要回到标注工具里检查。
2.3 用宽高比和面积评估小目标与长条框
行为识别对框的形状也很敏感。卧睡的猪通常框宽而矮,站立的猪框高而窄;如果数据里这类极端长宽比过多,模型对锚框尺寸的选择就需要相应调整。下面这段脚本可以输出 bbox 面积分布和长宽比分布:
import json from statistics import mean, median def bbox_stats(coco_path): with open(coco_path, 'r', encoding='utf-8') as f: data = json.load(f) areas, ratios = [], [] for ann in data['annotations']: x, y, w, h = ann['bbox'] areas.append(w * h) ratios.append(w / h if h > 0 else 0) print('平均框面积:', int(mean(areas))) print('框面积中位数:', int(median(areas))) print('宽高比中位数:', round(median(ratios), 3)) bbox_stats('pig_behavior_coco/annotations/instances_train.json')参考指标:如果面积中位数小于整张图面积的 1%,说明小目标比例高;这时建议把 YOLO 的 imgsz 调到 1280,或者用切片推理来召回漏检。宽高比中位数如果明显偏离 1,说明大部分行为是卧倒姿态,后续对锚框比例做针对性聚类会有效果。
3. 用 CVAT 标注猪行为数据到 COCO JSON 并切分训练集
3.1 CVAT 标注工具的标签配置与导出流程
标注是数据集质量的最大变量。猪圈监控视频里猪只叠在一起,单帧标注需要统一“哪个目标算有效”:通常只标注身体面积超过 20×20 像素的猪,且躯干可见度超过一半才打框;被遮挡面积大于一半的猪不标或标为 ignore 类别。这里推荐使用开源标注工具 CVAT 做矩形框标注。
操作流程按以下顺序:
- 创建项目,在 Labels 里依次添加四个行为标签,名称写成单数小写:drinking、eating、sleeping、standing。
- 创建任务时上传猪圈抓拍图片,建议每张图包含不同光照、不同栏位,确保行为模式多样。
- 进入标注界面用矩形框圈住整头猪,注意框不要刻意收得很贴身,给头颈动作留出余量。
- 对同一头猪连续多帧标注时,保持框大小一致,避免同一行为类别的框尺寸剧烈抖动。
- 完成后点击导出任务数据集,格式选 COCO JSON 1.0,下载包解压后得到 annotations/instances_default.json 和 images 目录。
导出后的 JSON 里,licenses 和 info 字段会写默认值,categories 通常按添加标签的顺序重新编号。一个常见坑是:标签名为中文时导出文件会出现编码问题,因此建议始终使用英文标签,中文名称只写在备注里。如果你手头已经有 YOLO 矩形框的预标注结果,可以在 CVAT 里用“自动标注”功能加载模型,让人工只修正错误框,这能明显缩短“吃/喝”这类小目标的标注工时。
3.2 将 COCO JSON 转换为 YOLO 格式的数据划分脚本
YOLOv8 训练时不直接读 COCO JSON,它要求每个图像对应一个同名的 txt 文件,每行是“类别 中心x归一化 中心y归一化 宽度归一化 高度归一化”。下面脚本把 COCO 标注按 8:1:1 划分,并输出 YOLO 格式标签:
import json import random from pathlib import Path def coco_to_yolo_split(coco_json, output_dir, split_ratio=(0.8, 0.1, 0.1)): with open(coco_json, 'r', encoding='utf-8') as f: data = json.load(f) cats = {cat['id']: idx for idx, cat in enumerate(data['categories'])} imgs = {img['id']: img for img in data['images']} anns_by_img = {} for ann in data['annotations']: anns_by_img.setdefault(ann['image_id'], []).append(ann) img_ids = list(imgs.keys()) random.seed(42) random.shuffle(img_ids) n = len(img_ids) train_end = int(n * split_ratio[0]) val_end = train_end + int(n * split_ratio[1]) split_map = {'train': img_ids[:train_end], 'val': img_ids[train_end:val_end], 'test': img_ids[val_end:]} for split_name, ids in split_map.items(): out_dir = Path(output_dir) / split_name / 'labels' out_dir.mkdir(parents=True, exist_ok=True) for img_id in ids: img_info = imgs[img_id] stem = Path(img_info['file_name']).stem with open(out_dir / f'{stem}.txt', 'w', encoding='utf-8') as f: for ann in anns_by_img.get(img_id, []): x, y, w, h = ann['bbox'] dw, dh = img_info['width'], img_info['height'] cx = (x + w / 2) / dw cy = (y + h / 2) / dh f.write(f"{cats[ann['category_id']]} {cx:.6f} {cy:.6f} {w/dw:.6f} {h/dh:.6f}\n") coco_to_yolo_split('instances_default.json', 'pig_dataset/labels')逻辑说明:代码先建立 category_id 到连续索引的映射,因为 YOLO 类别下标必须从 0 开始。随后把每个 image_id 对应的标注框换算成归一化中心坐标和宽高,写入以图像名命名的 txt。划分时用随机种子固定顺序,保证训练、验证、测试三部分没有重叠图像。
参数说明:split_ratio 三个数值按顺序对应 train/val/test;如果只需要训练和验证,可以设置 (0.9, 0.1, 0.0)。1272 张图按 8:1:1 大约得到如下划分:
| 数据集 | 图像数(按 8:1:1) | 说明 |
|---|---|---|
| train | 1018 | 用于模型参数学习 |
| val | 127 | 用于调整超参数和早停 |
| test | 127 | 用于最终正确率评估 |
图像文件需要与 labels 目录保持同样的子目录结构:
pig_dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ └── labels/ ├── train/ ├── val/ └── test/3.3 标注质量校验:处理空标签与越界框
转换完成后,常见问题是某些图像没有任何标注,但 train 目录里会有对应的空 txt。YOLO 训练会忽略空标签,若空标签占比过高(超过 5%),模型容易把背景学成目标。建议用下面命令统计空标签数量:
find pig_dataset/labels/train -name "*.txt" -empty | wc -l如果统计结果偏大,回到 COCO JSON 里检查是图片没有标注还是导出时丢失了 annotations。越界框也要处理,COCO 允许框超出图像边界,但 YOLO 归一化后坐标可能在 0 到 1 之外,训练时容易报错。下面一段 Python 用来裁剪越界框到图像范围内再归一化:
def clamp_bbox(x, y, w, h, img_w, img_h): x2, y2 = min(x + w, img_w), min(y + h, img_h) return max(x, 0), max(y, 0), x2 - max(x, 0), y2 - max(y, 0)对每张图像先读取宽高,再用 clamp_bbox 修正,最后写入 txt。这一步放在模型训练前,能省掉大量“训练中途出现 nan”的排错时间。注意如果框被裁剪后面积小于原面积 30%,建议直接丢弃该框,因为它们通常是遮挡严重的伪标注。
4. 基于 YOLOv8 训练猪行为识别模型的关键参数与评估
4.1 配置 data.yaml 与模型选型
行为识别在工程上通常拆成“检测+帧级行为分类”,YOLO 这类目标检测器天然适合这个任务。模型选型要看部署端算力:边缘盒子用 YOLOv8n 或 YOLOv8s,服务器训练可以上 YOLOv8m。下面是不同模型的参考对比,具体数值会随数据分布变化:
| 模型 | 参数量 | 预期 mAP50 区间 | 推理平台建议 |
|---|---|---|---|
| YOLOv8n | 3.2M | 0.80-0.88 | 嵌入式设备 |
| YOLOv8s | 11.2M | 0.84-0.92 | 工控机/单卡 |
| YOLOv8m | 25.9M | 0.86-0.94 | 带 GPU 的服务器 |
数据配置文件是训练入口,建议放在数据集根目录下:
path: /data/pig_dataset train: images/train val: images/val test: images/test names: 0: drinking 1: eating 2: sleeping 3: standing逻辑说明:path 指向包含 images 和 labels 的根目录,train/val/test 使用相对 path 的子目录。第一次在本地跑通时,使用绝对路径可以减少相对路径错误;模型发布时再改为相对路径,方便部署到其他机器。
参数说明:names 顺序必须和 3.2 转换脚本中 enumerate(data['categories']) 的顺序一致。如果 CVAT 导出的 classes 顺序是 drinking、eating、sleeping、standing,那么这里固定不变;训练中途不要随意改类别顺序,否则会导致旧的 txt 标签全部错位。
4.2 YOLOv8 训练命令与关键参数
使用 YOLOv8 官方 CLI 训练自己的数据集,命令如下:
yolo detect train \ data=pig_behavior.yaml \ model=yolov8m.pt \ epochs=150 \ imgsz=1280 \ batch=16 \ device=0 \ patience=30 \ project=./runs \ name=pig_behavior逻辑说明:model=yolov8m.pt 会加载 COCO 预训练权重,迁移学习能大幅缩短训练时间。epochs 设 150 是为了让“喝/吃/睡觉”这类细微姿态充分收敛;patience=30 表示验证集指标连续 30 轮不提升就早停。imgsz 从默认 640 提到 1280,是因为猪圈图像中单头猪可能只占很小面积,提高输入分辨率能直接提升小目标召回率。
参数说明:batch 根据 GPU 显存调整,12GB 显存跑 1280 分辨率建议 batch=8,24GB 可以到 16;如果出现“CUDA out of memory”,优先降低 batch、保留 imgsz,因为低分辨率对行为识别的影响比小 batch 更致命。device=0 使用第一块 GPU,没有 GPU 可以改成 cpu,但训练速度会慢很多。
训练完成后,权重保存在 runs/detect/pig_behavior/weights/best.pt。选择 best 而不是 last,因为 best 保存的是验证集上 mAP 最高的那一轮。用下面的命令看训练曲线:
tensorboard --logdir runs/detect/pig_behavior4.3 评估指标:正确识别率 92.6% 到底衡量什么
多数目标检测项目用 mAP 代表精度,而标题里的“平均正确识别率 92.6%”更像是对每个检测框做行为分类的准确率,或者对每一帧图像做行为判别的准确率。两者口径完全不同,复现时要先定清楚。
如果按逐帧图像评估,流程是:先用检测器输出每个框的行为类别,再与标注框做 IoU 匹配,IoU≥0.5 的框计入分母;将预测类别与真值类别一致才算正确。按逐实例评估,则不需要考虑视频连续性。以下是一段计算逐实例行为准确率的演示代码:
import numpy as np def behavior_accuracy(pred_boxes, pred_cls, gt_boxes, gt_cls, iou_thr=0.5): correct = 0 total = 0 # 这里用简单的一对一贪心匹配,正式评估可用 pycocotools for pb, pc in zip(pred_boxes, pred_cls): best_iou, best_g = 0, -1 for gi, (gb, gc) in enumerate(zip(gt_boxes, gt_cls)): iou = compute_iou(pb, gb) if iou > best_iou: best_iou, best_g = iou, gi if best_iou >= iou_thr: total += 1 if pc == gt_cls[best_g]: correct += 1 return correct / total if total > 0 else 0逻辑说明:该函数先用 IoU 把预测框匹配到真值框,再比较类别是否一致。由于没有排除已经匹配过的真值框,这里只是演示;实际评估建议直接用 pycocotools 的 COCOeval,它会处理多标签、IoU 阈值变化和平均 AP 指标。
参数说明:iou_thr 针对猪这种遮挡多的场景,往往从 0.5 放宽到 0.4 更能反映实际可用性;但对外报告精度时建议统一用 0.5,否则数字不可比。如果你在自己的测试集上复现准确率低于 92.6%,优先检查两部分:一是验证集是否有与训练集同帧的图片,二是标注类别是否错位。
5. 复现 92.6% 正确率的验证技巧:混淆矩阵与行为判定边界
5.1 用混淆矩阵定位喝/吃/站立三类易混行为
如果你在测试集上跑出的结果与 92.6% 有差距,先不要急着调网络结构,构建一份混淆矩阵往往能直接指出问题。喝和吃都是低头靠近食槽,站立的猪低头也容易被判成喝。建议把测试集的预测结果整理成下面格式,然后绘制混淆矩阵:
import matplotlib.pyplot as plt from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay y_true = [...] # 真值行为类别id y_pred = [...] # 模型预测行为类别id classes = ['drinking', 'eating', 'sleeping', 'standing'] cm = confusion_matrix(y_true, y_pred) disp = ConfusionMatrixDisplay(cm, display_labels=classes) disp.plot(cmap='Blues', xticks_rotation=45) plt.savefig('pig_behavior_cm.png', dpi=150)该脚本不需要额外标注,只要记录每个测试框的预测类别和真值类别即可。混淆矩阵中如果 eating 的行上出现大量 drinking,说明两者在视觉特征上高度相似,可以通过“观察持续帧数”做后处理:喝水时头部会周期性抬起,吃料时头部连续低位更久;利用跟踪算法关联前后帧,对短于 N 帧的 drinking 抖动做类别平滑,能够把准确率再提高 2~3 个百分点。
5.2 真正值得盯的两个验证细节
第一,测试集里不应出现训练集中同一段视频的连续帧。猪行为变化慢,相邻帧高度相似,如果切分时没有按视频时间戳分组,验证集会虚高。常见做法是将视频按时间片段做分组,再按组切分,而不是按单帧随机切分。第二,对小目标单独算一类准确率。把框面积小于 16×16 像素的检测结果单列统计,如果这部分准确率低于 80%,建议使用切片推理或在训练时加入 Mosaic 增强,而不是整体调大模型。
以上两个验证点做到位后,再回头看 92.6% 这个数字,它应该是在固定 IoU 阈值、固定类别顺序、固定测试集划分下的可复现结果。将这些判断规则写进后处理,配合 YOLOv8 输出的类别置信度,可以让模型在真实猪栏中更稳定。
本文还有配套的精品资源,点击获取