猪行为识别数据集实战:1272张图从COCO转YOLO到92.6%复现
2026/9/23 22:31:11 网站建设 项目流程

简介:这份猪圈猪只行为识别数据集面向智慧养殖、动物行为分析与计算机视觉方向的研究者及算法工程师,可用于构建猪只日常行为分类模型,覆盖喝、吃、睡觉、站立等典型动作,平均正确识别率约92.6%,适合目标检测与行为识别任务的训练、验证与迁移学习。资源包共1275个文件,其中1272张jpg图像与3个json标注文件,压缩包约85.57MB,json采用coco格式,可直接对接主流检测框架,省去格式转换环节。目前已有214人学习下载,具备一定参考热度。图像按视频帧序列命名,覆盖多段猪圈监控场景,便于按时间序列划分训练集与测试集,读者可据此复现行为识别流程、调整类别映射并评估模型在真实养殖环境下的表现,为后续部署与优化提供数据基础。

1. 猪圈里的猪行为识别数据集:1272 张图、92.6% 识别率,这套数据到底能不能直接上手

猪行为识别这件事,真正卡住人的从来不是模型结构,而是数据。你打开搜索引擎搜「行为识别」,跳出来的多半是人体的、监控场景的、体育动作的,跟猪圈里那几头猪的吃喝睡站没半点关系。这套数据集的价值就在这儿:1272 张真实猪圈场景图片,标注了喝、吃、睡觉、站立等行为类别,官方给出的平均正确识别率在 92.6%,标注格式是 coco json。对做智慧养殖、动物福利监测、边缘端行为分析的团队来说,它省掉的是最贵的那一步——从零采集和标注。这篇文章不讲空泛概念,只讲这套数据拿到手之后怎么读、怎么转、怎么训、怎么避坑,以及 92.6% 这个数字在你自己场景里还能不能保住。

2. 先看懂 coco json 标注:1272 张图里到底存了什么

2.1 coco json 的字段结构和猪行为类别的对应关系

coco json 不是一张表,而是五个顶层字段拼起来的字典:imagesannotationscategoriesinfolicenses。做行为识别的人最容易犯的错,是把它当成纯检测数据直接喂给检测头,结果发现「喝」和「吃」两个类别在空间上高度重叠,模型学不动。先看清楚这套数据是怎么组织的。

import json with open("pig_behavior/annotations/instances.json", "r", encoding="utf-8") as f: coco = json.load(f) # 顶层字段 print(coco.keys()) # dict_keys(['info', 'licenses', 'images', 'annotations', 'categories']) # 类别表:行为类别在这里 for c in coco["categories"]: print(c["id"], c["name"], c.get("supercategory")) # 1 drink behavior # 2 eat behavior # 3 sleep behavior # 4 stand behavior # 单张图的标注数量分布 from collections import Counter per_img = Counter(a["image_id"] for a in coco["annotations"]) print("图片总数:", len(coco["images"])) print("标注总数:", len(coco["annotations"])) print("单图最多标注数:", max(per_img.values()))

逻辑说明:categories里的name就是行为标签,supercategory一般写 behavior 或 action,用来和普通目标检测类别区分。annotations里每条记录包含image_idcategory_idbboxareaiscrowd。参数上要重点看iscrowd,猪圈场景里猪只互相遮挡极多,如果iscrowd=1的比例超过 15%,后面训练时就要考虑用 crowd 忽略策略,否则框回归会被带偏。

2.2 用 pycocotools 做一次数据体检,别急着开训

拿到任何 coco json,我一般先跑一遍体检脚本,而不是直接转 YOLO。体检要看四件事:类别是否均衡、框的宽高比分布、是否有零面积框、图片路径是否全部可读。

from pycocotools.coco import COCO import numpy as np coco = COCO("pig_behavior/annotations/instances.json") # 1. 类别均衡度 cat_ids = coco.getCatIds() for cid in cat_ids: ann_ids = coco.getAnnIds(catIds=[cid]) print(coco.loadCats(cid)[0]["name"], len(ann_ids)) # 2. 宽高比分布,猪的行为框通常偏扁 ratios = [] for ann in coco.loadAnns(coco.getAnnIds()): x, y, w, h = ann["bbox"] if h > 0: ratios.append(w / h) ratios = np.array(ratios) print("宽高比 中位数/均值:", np.median(ratios), ratios.mean()) # 3. 零面积框 bad = [a["id"] for a in coco.loadAnns(coco.getAnnIds()) if a["area"] <= 0] print("零面积框数量:", len(bad))

逻辑说明:类别均衡度决定你要不要用重采样或 focal loss。宽高比中位数如果明显大于 1,说明猪只多为侧躺或趴卧,锚框尺寸要相应调扁。零面积框必须清掉,否则训练时 loss 会出现 nan。参数上,area是 coco 官方按w*h算的,但有些标注工具会写错,所以要以bbox重算为准。

提示:1272 张图属于小数据集,体检阶段发现的任何类别不均衡,都会在训练后期被放大成某几个行为识别率骤降。

3. 把 coco json 转成 YOLO 格式:脚本、参数和四个边界坑

3.1 转换脚本:从 coco bbox 到 YOLO txt 的完整实现

现在主流做法还是拿 YOLOv8 或 YOLOv5 训练自己的数据集,所以 coco json 转 YOLO txt 是绕不开的一步。YOLO 格式要求每行class_id x_center y_center width height,且全部归一化到 0~1。下面这个脚本我用了很多次,处理过猪、牛、羊多个养殖数据集。

import json, os from pathlib import Path def coco2yolo(json_path, img_dir, out_dir): with open(json_path, "r", encoding="utf-8") as f: coco = json.load(f) # 类别 id 重映射为 0 起始连续整数 cats = sorted(coco["categories"], key=lambda x: x["id"]) cat_map = {c["id"]: i for i, c in enumerate(cats)} names = [c["name"] for c in cats] img_map = {img["id"]: img for img in coco["images"]} Path(out_dir).mkdir(parents=True, exist_ok=True) # 按 image_id 聚合标注 from collections import defaultdict anns = defaultdict(list) for a in coco["annotations"]: anns[a["image_id"]].append(a) for img_id, img in img_map.items(): w, h = img["width"], img["height"] lines = [] for a in anns.get(img_id, []): x, y, bw, bh = a["bbox"] # 边界裁剪,防止越界 x = max(0, min(x, w - 1)) y = max(0, min(y, h - 1)) bw = min(bw, w - x) bh = min(bh, h - y) if bw <= 1 or bh <= 1: continue xc = (x + bw / 2) / w yc = (y + bh / 2) / h nw = bw / w nh = bh / h lines.append(f"{cat_map[a['category_id']]} {xc:.6f} {yc:.6f} {nw:.6f} {nh:.6f}") stem = Path(img["file_name"]).stem with open(os.path.join(out_dir, stem + ".txt"), "w") as f: f.write("\n".join(lines)) with open(os.path.join(out_dir, "classes.txt"), "w") as f: f.write("\n".join(names)) print("类别顺序:", names) coco2yolo( "pig_behavior/annotations/instances.json", "pig_behavior/images", "pig_behavior/labels" )

逻辑说明:cat_map把原始 category_id 重映射成从 0 开始的连续整数,这是 YOLO 的硬性要求,不重映射会出现类别索引跳号导致训练报错。边界裁剪那几行是血泪经验,猪圈图片里猪只经常贴着画面边缘,标注框会超出图像边界,不裁剪的话归一化后坐标大于 1,YOLO 会直接忽略该框甚至报错。bw <= 1 or bh <= 1过滤掉极小框,避免噪声标签污染训练。

参数说明:xc/yc是框中心点归一化坐标,nw/nh是宽高归一化值,保留 6 位小数足够。classes.txt的顺序必须和cat_map一致,后面写 data.yaml 时names要按这个顺序填。

3.2 data.yaml 怎么写,以及训练前必须核对的三个路径

转换完只是第一步,YOLO 训练读的是 data.yaml。这个文件写错一个路径,训练能启动但 mAP 全是 0,属于最隐蔽的翻车点。

path: /data/pig_behavior train: images/train val: images/val test: images/test nc: 4 names: 0: drink 1: eat 2: sleep 3: stand

逻辑说明:path是数据集根目录,train/val/test是相对路径。nc必须等于类别数,names的键必须从 0 连续。常见错误是names写成列表但顺序和classes.txt不一致,模型会把「喝」学成「吃」。

参数说明:1272 张图建议按 7:2:1 划分,即约 890 张训练、254 张验证、128 张测试。如果某些行为样本特别少,划分时要用分层抽样,保证每个 split 里四类行为都有。

注意:划分完一定要写脚本核对 train/val/test 三个目录下的图片数和标签数是否一一对应,缺标签的图片会被 YOLO 当成纯背景图,直接拉低召回。

4. 用 YOLOv8 训练这套猪行为数据集:参数怎么设、92.6% 怎么复现

4.1 从预训练权重起步的最小训练命令

小数据集不要从零训,这是铁律。1272 张图从零训,模型根本学不出「喝」和「吃」的细微姿态差异。正确做法是加载 COCO 预训练权重做迁移学习。

yolo detect train \ data=/data/pig_behavior/data.yaml \ model=yolov8s.pt \ epochs=150 \ imgsz=640 \ batch=16 \ lr0=0.01 \ lrf=0.01 \ patience=30 \ cache=True \ project=runs/pig \ name=exp1

逻辑说明:model=yolov8s.pt是速度和精度的平衡点,猪行为识别不需要超大模型,n 或 s 足够。epochs=150配合patience=30,验证集 30 轮不提升就早停,避免过拟合。cache=True把图片缓存到内存,1272 张图完全放得下,能显著加快训练。

参数说明:lr0=0.01是初始学习率,迁移学习常用值;lrf=0.01是最终学习率系数,配合余弦退火。imgsz=640是标准输入尺寸,如果猪只目标偏小可以提到 800,但显存要够。batch=16在 8G 显存上跑 yolov8s 比较稳。

4.2 影响 92.6% 识别率的四个关键参数

官方给的 92.6% 是在特定划分和参数下测出来的,你自己复现时如果差几个点,先查这四个参数。

参数推荐值作用调错后果
imgsz640输入分辨率太小丢小目标,太大显存爆
conf0.25推理置信度阈值太高漏检,太低误检
iou0.7NMS 重叠阈值太低同类框被误删
mosaic1.0马赛克增强关闭后小数据集易过拟合

逻辑说明:confiou是推理阶段参数,训练时用默认即可,但评估 mAP 时这两个值直接决定最终数字。猪只互相遮挡多,iou设太低会把挨着的两头猪的框合并成一个。mosaic增强对小数据集几乎是必开项,它把四张图拼成一张,等效扩充了场景多样性。

参数说明:评估时用yolo detect val跑一遍,看mAP50mAP50-95两个指标。行为识别更关注mAP50,因为框的位置精度对行为分类影响没那么大。如果mAP50到 90% 以上但mAP50-95只有 60%,说明框回归还不够准,可以加box损失权重。

4.3 训练曲线怎么看:判断过拟合和欠拟合的信号

训练跑起来不是等结果,中间要盯曲线。runs/pig/exp1/results.csv里有每轮的 loss 和 mAP。

import pandas as pd df = pd.read_csv("runs/pig/exp1/results.csv") df.columns = df.columns.str.strip() # 看最后 10 轮的验证 mAP 和训练 loss print(df[["epoch", "train/box_loss", "val/box_loss", "metrics/mAP50(B)", "metrics/mAP50-95(B)"]].tail(10))

逻辑说明:如果train/box_loss持续下降但val/box_loss开始上升,就是过拟合,要加数据增强或减模型容量。如果两个 loss 都居高不下,是欠拟合,要加轮数或提学习率。metrics/mAP50(B)是行为识别的核心指标,稳定在 0.92 附近说明复现成功。

参数说明:results.csv的列名带空格,读进来先strip()。看曲线不要只看最后一轮,要看最后 20 轮的波动,波动大说明 batch 太小或学习率太高。

5. 猪行为识别落地避坑:五条踩过的坑和排查路径

5.1 坑一:喝和吃识别混淆,mAP 卡在 80% 上不去

现象:训练完发现「喝」和「吃」两个类别的混淆矩阵里互相误判严重,其他两类正常。

原因:猪喝水和吃食的姿态高度相似,都是低头靠近地面或料槽,单帧图像上差异极小。数据集里如果这两类的拍摄角度单一,模型只能靠背景(水槽 vs 料槽)区分,泛化差。

解决:一是引入时序信息,用连续几帧判断头部运动方向;二是数据层面补充不同角度的喝和吃样本;三是训练时对这两类加大分类损失权重。我一般会先做类别权重重平衡,再考虑上时序模型。

5.2 坑二:验证集 mAP 很高,换到新猪圈全崩

现象:在自己划分的验证集上 mAP50 有 92%,部署到另一个猪圈,识别率掉到 60% 以下。

原因:1272 张图大概率来自有限几个猪圈,背景、光照、栏杆样式高度一致。模型学到了背景捷径,而不是猪的行为特征。

解决:划分验证集时按猪圈或时间段划分,而不是随机划分。随机划分会让同一场景的图同时出现在训练和验证集,指标虚高。部署前一定要在目标猪圈采一批图做域适应测试。

5.3 坑三:夜间红外图像识别率断崖下跌

现象:白天识别正常,夜间红外摄像头画面下,睡觉和站立几乎分不开。

原因:训练集里夜间样本占比过低,模型没见过红外成像的灰度分布。猪躺卧时红外图像上轮廓模糊,和站立的区分度下降。

解决:统计训练集的光照分布,夜间样本不足就补采。推理阶段可以对红外图像做直方图均衡化预处理,提升轮廓对比度。如果夜间是刚需,建议单独训一个红外分支。

5.4 坑四:coco json 里 iscrowd 标注被当成正常框训练

现象:训练 loss 正常,但密集猪只场景下框大量重叠,NMS 后只剩一个框。

原因:coco json 里iscrowd=1的标注表示该区域是人群/畜群,不要求精确框。转换脚本如果没过滤,这些框会进入训练,教模型把多只猪当成一只。

解决:转换时跳过iscrowd=1的标注,或在 YOLO 里把它们标成 ignore 区域。体检阶段先统计iscrowd比例,超过 10% 就要处理。

5.5 坑五:图片和标签文件名不一致导致静默丢样本

现象:训练正常启动,但训练集图片数比预期少,模型效果差。

原因:coco json 里file_name可能带路径或扩展名大小写不一致,转换脚本用stem生成标签名,和实际图片名对不上,YOLO 找不到标签就跳过该图。

解决:转换后写脚本核对图片和标签的一一对应关系,缺标签的图片要么补标签,要么从训练集移除。这个检查我每次都会跑,属于后悔药级别的步骤。

from pathlib import Path imgs = {p.stem for p in Path("images/train").glob("*.jpg")} labs = {p.stem for p in Path("labels/train").glob("*.txt")} print("缺标签:", imgs - labs) print("缺图片:", labs - imgs)

6. 把 92.6% 变成你自己的数字:验证方法与一个提点技巧

数据集给的是起点,不是终点。真正决定这套猪行为识别能不能落地的,是你能不能在目标场景里把识别率稳住。验证方法上,我习惯做三件事:第一,按猪圈和时间段做交叉验证,而不是随机划分,这样得到的指标才接近真实部署;第二,单独统计每个行为类别的召回率,喝和吃这两个易混类别要重点看;第三,用混淆矩阵定位误判方向,是喝被判成吃,还是站立被判成睡觉,方向不同,优化手段完全不同。

提点技巧上,分享一个我常用的后处理策略:对连续视频帧做行为投票。单帧识别率 92.6%,但如果对连续 5 帧的识别结果做多数投票,稳定行为的识别率能再提 2 到 3 个点。猪的行为本身有持续性,不会一帧喝一帧吃,利用这个先验能压掉大量抖动误判。

from collections import Counter def vote_smooth(frame_preds, window=5): # frame_preds: 每帧的类别 id 列表 smoothed = [] for i in range(len(frame_preds)): start = max(0, i - window // 2) end = min(len(frame_preds), i + window // 2 + 1) window_preds = frame_preds[start:end] most_common = Counter(window_preds).most_common(1)[0][0] smoothed.append(most_common) return smoothed

逻辑说明:window=5是经验值,太小压不住抖动,太大行为切换会延迟。参数上要根据实际帧率调,25fps 下 5 帧约 0.2 秒,行为切换响应足够快。这个后处理不改变模型,只在推理侧加一层,成本极低。

最后说个我自己的习惯:每次拿到新数据集,先花半天做体检和可视化,把标注框画到原图上逐类看一遍。这半天能省掉后面几天的调参玄学。猪行为识别这个方向,数据质量比模型结构重要得多,1272 张图不算多,但用对了,92.6% 是能摸到的。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询