多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

猪行为识别数据集实战:COCO标注、YOLOv8训练与92.6%准确率复现

猪行为识别数据集实战:COCO标注、YOLOv8训练与92.6%准确率复现 简介这份猪圈猪行为识别数据集面向计算机视觉学习者、畜牧智能化研究者与目标检测开发者用于训练和验证猪只日常行为分类模型可识别喝、吃、睡觉、站立等典型动作平均正确识别率达92.6%适合作为课程设计、算法对比或养殖场景落地的数据基础。资源包共1275个文件以1272张jpg图像为主体另附3个json标注文件采用coco json格式可直接对接主流检测与分类框架压缩包整体约85.57MB体量适中便于快速下载与本地训练。目前已有214人学习下载具备一定参考热度。图像覆盖多段视频抽帧场景标注结构完整读者可据此完成数据加载、模型微调、精度复现与行为统计等任务也能借助标注文件快速构建自己的训练流水线减少从零采集与标注的成本。1. 猪行为识别数据集1272 张图、92.6% 准确率这套数据到底能不能直接上生产猪的行为识别听起来像是农业物联网里最不起眼的一个分支但真正做过养殖场智能化改造的人都知道这里面的坑比城市道路目标检测还密集。猪圈光照不均、猪只互相遮挡、躺卧姿态高度相似再加上猪本身活动范围小、动作幅度低通用行为识别模型直接迁移过来准确率往往掉到七成以下。这个数据集给出的 92.6% 平均正确识别率配合 1272 张图片和 COCO JSON 标注格式恰好卡在一个很微妙的位置数据量不算大但标注格式足够标准类别定义也足够聚焦——喝、吃、睡觉、站立这几类核心行为基本覆盖了猪只日常活动的主要状态。如果你正在做养殖场行为监测、猪只健康预警或者只是想找一个垂直领域的小规模数据集来验证行为识别 pipeline这套数据值得认真评估。它适合两类人一类是手里有猪场摄像头、想快速跑通行为识别原型的工程师另一类是研究小样本行为分类、需要真实场景数据做对比实验的学生和研究员。不适合指望拿它直接训练一个通用猪行为大模型的人1272 张图的体量决定了它更适合做微调、验证和快速迭代而不是从零预训练。2. COCO JSON 标注拆解1272 张图里到底标了什么2.1 COCO JSON 的结构与猪行为类别的映射关系COCO JSON 格式在目标检测领域几乎是通用语言但用在行为识别上很多人第一次打开标注文件会愣一下行为类别到底标在category_id里还是靠keypoints或者额外字段这套数据集的做法很直接——把行为类别作为检测类别来处理。也就是说categories数组里放的不是“猪”这一个类而是“喝”“吃”“睡觉”“站立”这些行为类。每张图里的猪只个体被框出来框对应的category_id就是它当前的行为标签。这种标注方式的好处是你可以直接用 YOLOv8、Faster R-CNN 这类检测框架来训练不需要额外设计时序模型。坏处也很明显单帧图像无法表达行为的时间连续性同一头猪在连续两帧里可能被标成不同行为训练时会出现标签噪声。我一般会建议在数据加载阶段加一个简单的时序平滑或者至少在做验证集评估时按视频片段而不是单帧来算准确率。先看一个典型的 COCO JSON 结构片段{ info: { description: Pig Behavior Dataset, version: 1.0, year: 2024 }, images: [ { id: 1, file_name: pig_0001.jpg, width: 640, height: 480 } ], annotations: [ { id: 101, image_id: 1, category_id: 3, bbox: [120, 85, 210, 160], area: 33600, iscrowd: 0 } ], categories: [ {id: 1, name: drink, supercategory: behavior}, {id: 2, name: eat, supercategory: behavior}, {id: 3, name: sleep, supercategory: behavior}, {id: 4, name: stand, supercategory: behavior} ] }bbox是标准 COCO 格式的[x, y, width, height]原点在左上角。category_id直接对应行为类别area是框面积iscrowd一般设为 0因为猪只个体之间虽然会遮挡但标注时通常还是按单个体独立框选。这里要注意如果同一头猪同时出现“站立”和“吃”的复合行为标注里只会保留一个主导行为标签这是数据集设计上的取舍训练时不用纠结。2.2 用 pycocotools 做标注质量检查与类别分布统计拿到数据集第一件事不是急着训练而是先做标注质量检查。1272 张图不算多但人工标错、框漏、类别写反的情况一定存在。用pycocotools可以快速把类别分布、框大小分布、每张图的实例数拉出来。from pycocotools.coco import COCO import matplotlib.pyplot as plt import numpy as np # 加载标注文件路径按实际存放位置改 ann_file annotations/instances_train.json coco COCO(ann_file) # 统计每个类别的实例数 cats coco.loadCats(coco.getCatIds()) cat_names [c[name] for c in cats] cat_ids coco.getCatIds() counts [] for cid in cat_ids: ann_ids coco.getAnnIds(catIds[cid]) counts.append(len(ann_ids)) for name, cnt in zip(cat_names, counts): print(f{name}: {cnt} instances) # 统计每张图的实例数分布 img_ids coco.getImgIds() inst_per_img [] for iid in img_ids: ann_ids coco.getAnnIds(imgIds[iid]) inst_per_img.append(len(ann_ids)) print(f平均每张图实例数: {np.mean(inst_per_img):.2f}) print(f实例数中位数: {np.median(inst_per_img)}) print(f空标注图片数: {sum(1 for x in inst_per_img if x 0)})这段代码跑完你会得到几个关键数字。如果某个类别实例数明显偏少比如“喝”只有几十个框那训练时就要考虑类别加权或者过采样。如果空标注图片超过 5%说明有些图可能漏标了需要人工复查。平均每张图实例数在 3 到 8 之间是比较健康的太低说明猪只密度小太高说明遮挡严重训练难度会上升。参数上getAnnIds的catIds和imgIds可以组合使用比如你想看“睡觉”这个类别在哪些图里出现就同时传两个参数。iscrowd在统计时建议过滤掉虽然这个数据集里基本没有 crowd 标注但养成习惯没坏处。2.3 从 COCO 到 YOLO 格式转换脚本与四个边界坑COCO JSON 转 YOLO txt 是绕不过去的一步。YOLOv8 虽然支持直接读 COCO但实际训练时用 txt 格式更稳尤其是做数据增强和自定义 sampler 的时候。转换逻辑不复杂把[x, y, w, h]归一化成[x_center, y_center, w, h]然后每个类别映射到 0 到 N-1 的索引。import json import os from pathlib import Path def coco_to_yolo(coco_json, img_dir, out_dir): with open(coco_json, r) as f: data json.load(f) # 建立 image_id 到文件名的映射 img_map {img[id]: img for img in data[images]} # 类别 id 重映射为 0 起始的连续整数 cat_map {cat[id]: idx for idx, cat in enumerate(data[categories])} os.makedirs(out_dir, exist_okTrue) for ann in data[annotations]: img_info img_map[ann[image_id]] w_img, h_img img_info[width], img_info[height] x, y, bw, bh ann[bbox] # 边界裁剪防止框超出图像范围 x max(0, min(x, w_img - 1)) y max(0, min(y, h_img - 1)) bw min(bw, w_img - x) bh min(bh, h_img - y) x_center (x bw / 2) / w_img y_center (y bh / 2) / h_img bw_norm bw / w_img bh_norm bh / h_img cls_id cat_map[ann[category_id]] line f{cls_id} {x_center:.6f} {y_center:.6f} {bw_norm:.6f} {bh_norm:.6f}\n txt_name Path(img_info[file_name]).stem .txt with open(os.path.join(out_dir, txt_name), a) as f: f.write(line) # 生成 classes.txt with open(os.path.join(out_dir, classes.txt), w) as f: for cat in data[categories]: f.write(cat[name] \n)四个边界坑分别是第一bbox的x w可能刚好等于图像宽度归一化后x_center w/2会超过 1YOLO 训练时直接报错所以必须做min裁剪第二同一张图有多条标注时用a追加模式写 txt不要用w否则只剩最后一条第三classes.txt的顺序必须和cat_map的索引一致否则类别全乱第四图像文件名如果有中文或空格Path.stem处理没问题但 YOLO 的 dataloader 可能会出问题建议提前重命名成纯英文数字。转换完记得抽查几张打开原图用 OpenCV 把 YOLO 格式的框画回去肉眼比对一下有没有明显偏移。3. 训练策略选型为什么 1272 张图不适合从零训 YOLOv83.1 小样本行为识别的迁移学习与冻结层策略1272 张图按 8:1:1 切分训练集也就 1000 张出头。这个量级从零训 YOLOv8 基本等于自杀——模型会在训练集上过拟合到 99%验证集上震荡在 60% 到 75% 之间。正确的做法是拿 COCO 预训练权重做迁移学习而且不是简单加载权重就完事冻结层策略很关键。YOLOv8 的 backbone 在 COCO 上学到的底层特征——边缘、纹理、基础形状——对猪只检测同样有效。我一般会冻结前 7 到 10 层只训 head 和最后几个 C2f 模块。具体冻多少看你的验证集准确率曲线如果前 20 个 epoch 验证 loss 不降说明冻太多了解冻两层如果训练 loss 降得飞快但验证 loss 抬头说明冻太少过拟合了。from ultralytics import YOLO # 加载 COCO 预训练的 YOLOv8n小模型更适合小数据集 model YOLO(yolov8n.pt) # 冻结 backbone 前 10 层 for i, (name, param) in enumerate(model.model.named_parameters()): if i 10: param.requires_grad False # 训练参数小数据集用较小学习率避免破坏预训练特征 results model.train( datapig_behavior.yaml, epochs120, imgsz640, batch16, lr00.001, lrf0.01, warmup_epochs5, cos_lrTrue, patience30, augmentTrue, mosaic0.5, mixup0.1, copy_paste0.1, device0 )lr00.001比默认的 0.01 小一个量级因为预训练权重已经在一个大分布上收敛了大学习率会把底层特征打散。mosaic0.5而不是默认的 1.0是因为猪圈场景本身背景单一过度 mosaic 会引入不自然的拼接边缘反而干扰行为特征。copy_paste0.1对猪只遮挡场景有帮助但别开太高否则会出现不合理的猪只重叠。3.2 数据增强的边界哪些增强对猪行为识别是负收益数据增强在目标检测里是双刃剑猪行为识别尤其明显。水平翻转基本安全因为猪的左右朝向不影响行为定义。但垂直翻转要慎用——猪躺卧和站立的姿态在垂直方向上有明确语义翻转后“睡觉”的框可能看起来像“站立”模型会学到错误的姿态先验。颜色抖动HSV 增强可以开但饱和度变化范围别太大。猪圈常见的光照是暖黄光或者冷白光饱和度剧烈变化会让模型把颜色当成行为线索换一个猪场就翻车。旋转增强也要限制角度±15 度以内可以接受超过 30 度会出现大量空框和截断框。我自己的配置是hsv_h0.015, hsv_s0.3, hsv_v0.3, degrees10, translate0.1, scale0.3, shear2, perspective0.0, flipud0.0, fliplr0.5。flipud直接关掉perspective也关掉猪圈摄像头角度固定透视变换没有实际意义。3.3 验证集划分与 92.6% 准确率的复现条件92.6% 这个数字如果不说明验证集怎么划的基本没有参考价值。行为识别数据集的划分有个隐藏陷阱如果按随机帧划分同一头猪的连续帧可能同时出现在训练集和验证集里模型相当于在验证集上“见过”这头猪准确率会虚高。正确的做法是按视频片段或者按时间窗口划分确保验证集里的猪只和场景在训练集里没出现过。复现 92.6% 需要满足几个条件验证集和训练集按视频源隔离输入分辨率不低于 640使用 COCO 预训练权重类别平衡采样。如果验证集准确率明显低于 92.6%先检查划分方式再检查类别分布最后才怀疑模型结构。我见过太多人拿随机帧划分跑出 95% 然后换真实场景掉到 70%问题就出在划分上。4. 避坑与排查猪行为识别训练中最容易翻车的 5 个点4.1 现象训练 loss 正常下降但验证集准确率卡在 70% 不动原因通常是类别不平衡。1272 张图里“站立”和“睡觉”的实例数往往远多于“喝”和“吃”模型倾向于把所有框都预测成多数类。解决方法是先跑一遍类别统计对少数类做过采样或者在 loss 里加类别权重。YOLOv8 的clsloss 默认是 BCE可以在train里传class_weights参数但更简单的做法是复制少数类图片到训练集让每个类别的实例数大致拉平。4.2 现象模型在验证集上表现很好但部署到新猪场后准确率暴跌原因是过拟合了背景。原数据集可能来自固定几个猪圈地面颜色、栏杆样式、光照条件都被模型当成了行为线索。解决方法是训练时加入更强的背景增强比如随机裁剪、随机遮挡或者用 CutMix 把不同图片的背景混合。部署前一定要拿新场景的几十张图做一次零样本测试如果掉得厉害说明背景过拟合严重需要重新标注一批新场景数据做微调。4.3 现象同一头猪在连续帧里被预测成不同行为输出抖动严重单帧检测模型没有时序概念这是结构性问题。解决分两个层面推理层面加滑动窗口投票比如连续 5 帧里取众数训练层面可以引入轻量时序模块比如在 YOLO head 后面接一个 GRU但 1272 张图训时序模块容易过拟合我一般只在推理后处理做平滑。具体做法是维护一个长度为 7 的队列每帧的预测结果入队输出队列里出现次数最多的类别。4.4 现象COCO JSON 转 YOLO 后部分图片的框全部偏移或消失最常见的原因是bbox的x, y是浮点数而某些转换脚本用int()截断导致偏移。另一个原因是图像实际尺寸和 JSON 里记录的width, height不一致比如图片被重新缩放过了但 JSON 没更新。排查方法随机抽 10 张图用 OpenCV 读实际尺寸和 JSON 里的width, height比对不一致就统一重设。转换脚本里所有坐标计算用浮点最后写文件时再格式化不要中间截断。4.5 现象训练到一半突然报 CUDA out of memory小数据集训练时 OOM 往往不是 batch size 太大而是mosaic增强在某个 epoch 拼出了一张超大图。YOLOv8 的 mosaic 会把 4 张图拼成一张如果原图分辨率不统一拼接后的尺寸可能远超imgsz。解决方法是训练前统一把所有图片 resize 到相同尺寸或者在 dataloader 里强制imgsz裁剪。另外cacheTrue在内存小的机器上也会导致 OOM1272 张图不大但如果你同时开了cache和mosaic内存占用会翻倍建议先关cache跑一轮看看。5. 把 92.6% 推到 95% 的进阶技巧从单帧检测到行为片段投票如果你已经跑通了基础训练验证集准确率在 92% 上下想再往上推单靠调参空间很小了。这时候要换思路把单帧检测结果按时间维度聚合用行为片段投票来提升最终准确率。具体做法是对每个视频片段每隔 3 帧取一帧做检测得到一个行为序列然后用一个简单的 HMM 或者滑动窗口投票来平滑输出。from collections import deque, Counter class BehaviorSmoother: def __init__(self, window_size7, min_votes4): self.window deque(maxlenwindow_size) self.min_votes min_votes def update(self, behavior_label): self.window.append(behavior_label) if len(self.window) self.window.maxlen: return behavior_label # 统计窗口内出现次数最多的行为 counter Counter(self.window) most_common, count counter.most_common(1)[0] if count self.min_votes: return most_common # 票数不够时保持上一帧结果避免频繁切换 return self.window[-2] if len(self.window) 2 else behavior_label # 使用示例 smoother BehaviorSmoother(window_size7, min_votes4) raw_predictions [stand, stand, eat, stand, stand, eat, stand] for pred in raw_predictions: smoothed smoother.update(pred) print(fraw: {pred}, smoothed: {smoothed})window_size7对应大约 0.5 秒的视频按 15 fps 算min_votes4意味着窗口内至少 4 帧一致才切换行为。这两个参数需要根据你的摄像头帧率和猪只动作速度调动作快的场景把窗口调小动作慢的调大。投票平滑能把单帧抖动带来的假阳性压下去实测在验证集上能拉 1.5 到 2 个百分点。另一个技巧是难例挖掘。跑完一轮训练后把验证集里预测错误的样本挑出来人工看一遍如果发现是标注错误就修正如果是模型确实分不清的比如“站立”和“吃”的边界模糊就把这些样本复制到训练集里给更高的采样权重。1272 张图里通常有 50 到 100 张难例处理完再训一轮准确率能再涨 1 个点左右。最后说一个我自己的习惯每次训完模型不管指标多好看我都会拿手机去附近养殖场拍 20 张真实场景图做一次盲测。这个习惯帮我省过好几次“验证集 95% 部署 60%”的尴尬。数据集再标准也替代不了真实场景的检验。希望帮到你。本文还有配套的精品资源点击获取
返回列表