ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于YOLO v11的家禽行为识别:从484张图到产线部署

基于YOLO v11的家禽行为识别:从484张图到产线部署 简介这份家禽鸡行为数据集面向智慧养殖、动物行为识别方向的算法开发者与高校研究者可用于训练目标检测模型自动区分吃食、喝水、死亡、异常行为与睡觉五类状态为禽舍健康监测与异常预警提供数据基础。资源包共1429个文件包含714张jpg图像与714个同名txt标注文件另附1个yaml配置文件整体约39.33MB采用YOLO v11格式组织标注与图像一一对应可直接接入主流检测框架训练。图像覆盖室内养殖场与特殊场景兼顾不同光照与拍摄角度便于模型学习多样化的鸡只姿态。已有382人学习下载适合作为课程设计、毕业设计或养殖智能化项目的训练素材也可用于对比不同检测模型在细粒度行为分类上的表现帮助读者快速搭建从数据加载到模型评估的完整流程。1. 家禽行为识别数据集484 张图怎么撑起吃食喝水死亡睡觉四类行为养鸡场里最怕的不是行情波动是凌晨三点死了一栏鸡没人知道。等早上巡棚发现死鸡已经被踩踏污染连带周边几只也开始精神萎靡。传统做法靠人每隔两小时巡一次但 5000 只规模的棚一个人走一圈要 20 分钟夜间巡棚更是玄学——手电一晃鸡该睡还是睡异常行为根本看不出来。这个数据集要解决的就是这件事用摄像头 YOLO v11 做家禽行为自动识别把吃食、喝水、死亡、异常行为、睡觉这五类状态从视频流里框出来。484 张训练集图片不算大但标注格式是 YOLO v11 标准意味着你可以直接拿 ultralytics 框架跑训练不用再折腾格式转换。适合两类人一是做智慧养殖落地的工程师需要快速验证行为识别可行性二是学生或研究者想拿真实场景数据跑通 YOLO v11 全流程。数据集小有小的好处——单卡 3060 就能训半天出结果翻车成本低。2. YOLO v11 格式标注拆解从 484 张图到可训练标签2.1 标注文件长什么样为什么是 5 列而不是 8 列YOLO 格式的目标检测标注是每张图对应一个.txt文件文件名与图片同名内容每行代表一个目标框。标准格式是class_id x_center y_center width height五个值全部归一化到 0~1 之间。class_id从 0 开始对应你的类别列表顺序。比如吃食是 0喝水是 1死亡是 2异常行为是 3睡觉是 4。这里有个血泪经验很多人第一次拿到 YOLO 格式数据看到 5 列以为是xmin ymin xmax ymax直接按 VOC 逻辑解析结果框全跑到左上角。归一化的中心点坐标和宽高跟绝对像素坐标是两码事。验证方法很简单——用脚本读一个标注文件把归一化值乘回图片宽高看框是否落在鸡身上。import cv2 import numpy as np img cv2.imread(chicken_001.jpg) h, w img.shape[:2] with open(chicken_001.txt, r) as f: lines f.readlines() for line in lines: cls_id, xc, yc, bw, bh map(float, line.strip().split()) # 反归一化到像素坐标 x1 int((xc - bw / 2) * w) y1 int((yc - bh / 2) * h) x2 int((xc bw / 2) * w) y2 int((yc bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, str(int(cls_id)), (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imwrite(check.jpg, img)这段代码的作用是可视化验证标注是否正确。参数说明xc, yc是框中心点归一化坐标bw, bh是归一化宽高。乘回w, h后得到像素级x1, y1, x2, y2。如果画出来的框偏离鸡只说明标注文件与图片不匹配常见原因是图片被 resize 过但标注没同步更新。2.2 五类行为的标注边界怎么定别让模型学混吃食和喝水的区分是这套数据集里最容易翻车的地方。鸡低头啄料槽是吃食低头啄水线是喝水但俯拍视角下两者姿态几乎一样。标注时如果只框头部模型根本分不清。我一般会框住鸡的头部 前胸区域同时保证料槽或水线在框内边缘可见。这样模型能学到“头部朝向 背景器具”的联合特征。死亡行为的标注要特别注意只框已经倒地不动、姿势僵硬的个体。有些鸡蹲着休息容易被误标成死亡。区分点是死亡鸡只通常侧躺、双腿伸直、头部贴地。异常行为包括扎堆、张口呼吸、翅膀下垂等标注时框住整只鸡不要只框异常部位。睡觉行为的标注争议最大。夜间红外模式下鸡蹲伏闭眼就是睡觉但白天也有鸡蹲着打盹。我的做法是只标注夜间时段补光灯关闭后的蹲伏闭眼个体白天类似姿态归入“异常行为”或直接不标。这样模型学到的睡觉特征更纯粹。2.3 用 Python 做一次标注完整性检查拿到数据集先别急着训练跑一遍完整性检查能省下几小时排错时间。检查三件事图片和标注文件是否一一对应、标注值是否在 0~1 范围内、类别 ID 是否超出预期。import os import glob img_dir images/train lbl_dir labels/train img_files {os.path.splitext(os.path.basename(p))[0] for p in glob.glob(f{img_dir}/*.jpg)} lbl_files {os.path.splitext(os.path.basename(p))[0] for p in glob.glob(f{lbl_dir}/*.txt)} # 检查配对 missing_lbl img_files - lbl_files missing_img lbl_files - img_files print(f缺标注的图片: {len(missing_lbl)} 张) print(f缺图片的标注: {len(missing_img)} 个) # 检查标注值范围 bad_lines [] for lbl_path in glob.glob(f{lbl_dir}/*.txt): with open(lbl_path) as f: for i, line in enumerate(f): parts line.strip().split() if len(parts) ! 5: bad_lines.append((lbl_path, i, 列数不对)) continue vals list(map(float, parts[1:])) if any(v 0 or v 1 for v in vals): bad_lines.append((lbl_path, i, 值超出0~1)) if int(parts[0]) not in range(5): bad_lines.append((lbl_path, i, f类别ID异常: {parts[0]})) print(f异常标注行: {len(bad_lines)}) for item in bad_lines[:10]: print(item)逻辑说明先做集合差集找配对缺失再逐行解析标注文件。参数方面range(5)对应五类行为如果你的类别数不同要改这个值。异常行输出前 10 条即可通常问题集中在少数文件上。这一步跑完心里就有底了。3. 用 ultralytics 跑通 YOLO v11 训练从 data.yaml 到 best.pt3.1 data.yaml 的五个字段写错一个就白跑YOLO v11 训练依赖一个 YAML 配置文件告诉框架去哪里找图片、有几类、类别名是什么。最小配置如下path: /home/user/chicken_dataset train: images/train val: images/val nc: 5 names: 0: eating 1: drinking 2: dead 3: abnormal 4: sleepingpath是数据集根目录train和val是相对路径。常见翻车点path用了相对路径但训练脚本的工作目录变了导致找不到图片。我一般写绝对路径省心。nc必须等于names的条目数多一个少一个都会报索引越界。names的顺序必须和标注文件里的class_id严格对应否则模型学出来的类别是错位的。验证data.yaml是否写对用 ultralytics 自带的检查yolo checks这条命令会输出环境信息包括数据集路径是否可达。如果提示找不到数据集优先检查path和train拼接后的目录是否存在。3.2 训练命令与关键参数imgsz、batch、epochs 怎么定484 张图属于小数据集训练策略和大数据集完全不同。直接上大 epoch 容易过拟合模型会把每张图背下来。我一般这样起步yolo detect train \ data/home/user/chicken_dataset/data.yaml \ modelyolo11n.pt \ imgsz640 \ epochs100 \ batch16 \ lr00.001 \ patience20 \ projectchicken_runs \ nameexp1参数逐个说modelyolo11n.pt用 nano 版本参数量小适合小数据集快速验证。imgsz640是输入分辨率鸡只目标在图中占比不大时可以提到 960 或 1280但显存占用会翻倍。batch16在 8GB 显存上比较稳如果 OOM 就降到 8。lr00.001是初始学习率小数据集建议比默认值低一个数量级减少震荡。patience20表示 20 个 epoch 验证指标不提升就早停这是防过拟合的第一道闸。训练过程中重点看mAP50和mAP50-95。小数据集上mAP50能到 0.7 以上就算可用mAP50-95通常低不少因为框的精度要求更高。如果训练集 loss 持续下降但验证集 loss 开始上升说明过拟合了早停会帮你截住。3.3 训练完先别高兴用验证集跑一遍混淆矩阵训练结束会生成best.pt和last.pt。best.pt是验证指标最好的权重但别直接拿去部署。先跑验证yolo detect val \ modelchicken_runs/exp1/weights/best.pt \ data/home/user/chicken_dataset/data.yaml \ imgsz640 \ conf0.25 \ iou0.5conf0.25是置信度阈值低于这个值的框不输出。iou0.5是 NMS 的 IoU 阈值控制重叠框的合并。验证完会输出混淆矩阵重点看吃食和喝水之间有没有大量互错。如果这两类混淆严重说明标注边界确实有问题需要回去重新审视标注规则而不是调参能解决的。混淆矩阵里死亡类如果漏检多检查训练集中死亡样本数量。484 张图里如果死亡只有十几张模型学不好是正常的。可以考虑对死亡类做过采样或者用 copy-paste 增强合成更多死亡样本。4. 小数据集训练避坑484 张图踩过的五个坑4.1 现象训练 loss 正常下降但验证 mAP 始终为 0原因data.yaml里names的顺序和标注文件里的class_id对不上。比如标注里 0 是吃食但names里 0 写成了睡觉。模型学到的“吃食”特征被映射到“睡觉”类别验证时自然全错。解决用 2.3 节的检查脚本确认标注文件里的类别 ID 分布再对照data.yaml的names逐项核对。改完后重新训练不要接着之前的权重继续。4.2 现象训练到 30 epoch 后验证 loss 突然飙升原因学习率太大小数据集上模型在最优解附近震荡越震越远。默认lr00.01对 484 张图来说太激进。解决把lr0降到 0.001 甚至 0.0005同时加cos_lrTrue让学习率按余弦退火衰减。如果已经跑了一半可以从last.pt恢复并调低学习率yolo detect train \ data/home/user/chicken_dataset/data.yaml \ modelchicken_runs/exp1/weights/last.pt \ lr00.0005 \ cos_lrTrue \ epochs504.3 现象模型把蹲着的鸡全标成死亡原因死亡和蹲伏在俯拍视角下姿态相似标注时没有严格区分。模型学到的是“蹲着死亡”的捷径特征。解决回到标注环节把死亡样本限定为侧躺、腿伸直、头部贴地的个体。蹲伏但头部抬起的归入睡觉或异常。重新标注后死亡类样本可能减少但特征更干净。如果死亡样本太少用数据增强里的copy_paste把死亡鸡只粘贴到不同背景上合成新样本。4.4 现象夜间图片检测效果远差于白天原因训练集中夜间红外图片占比低模型没见过足够多的红外模式。484 张图里如果夜间只有几十张模型对红外图像的泛化能力很弱。解决统计训练集里白天和夜间的比例如果夜间少于 30%要么补采夜间数据要么在训练时对夜间图片做过采样。ultralytics 支持在data.yaml里用train指向一个包含重复文件名的列表文件手动控制采样权重。4.5 现象推理时同一只鸡被框了两次原因NMS 的iou阈值设得太高重叠框没被合并。默认iou0.7对密集场景偏松。解决推理时把iou降到 0.5 甚至 0.45yolo detect predict \ modelchicken_runs/exp1/weights/best.pt \ sourcetest_video.mp4 \ conf0.3 \ iou0.45 \ saveTrue如果降iou后正常鸡只被漏框说明conf太低导致大量低质量框参与 NMS把conf提到 0.4 再试。这两个参数需要联合调没有一组万能值。5. 从 484 张到产线可用行为识别的时序后处理技巧单帧检测只能告诉你“这一帧鸡在吃食”但产线需要的是“这只鸡连续吃了多久”“死亡状态持续了几分钟”。484 张图训出来的模型单帧准确率有限但加上时序后处理可用性会大幅提升。我一般会在检测之上加一个轻量跟踪器比如 ByteTrack 或简单的 IoU 匹配。每只鸡分配一个 ID然后对每个 ID 的行为序列做滑动窗口投票。窗口大小取 15 帧约 0.5 秒窗口内出现次数最多的行为作为当前状态。这样单帧的误检会被平滑掉。from collections import deque, Counter class BehaviorSmoother: def __init__(self, window15): self.window window self.buffers {} # track_id - deque def update(self, track_id, behavior): if track_id not in self.buffers: self.buffers[track_id] deque(maxlenself.window) self.buffers[track_id].append(behavior) # 投票取众数 most_common Counter(self.buffers[track_id]).most_common(1) return most_common[0][0] if most_common else behavior smoother BehaviorSmoother(window15) # 假设 tracker 输出 (track_id, behavior) smoothed smoother.update(track_id3, behavioreating)参数说明window15对应约 0.5 秒30fps 视频。如果视频帧率低比如 10fps窗口要相应加大到 30 以上。buffers字典按track_id隔离避免不同鸡只的行为序列混在一起。这个逻辑很轻CPU 上跑几千只鸡的跟踪也没压力。死亡行为的判定要更保守。单帧检测到死亡不能报警需要连续 N 帧比如 30 帧约 1 秒都判定为死亡且跟踪框位置基本不动才触发告警。这样可以过滤掉鸡只短暂蹲伏被误检为死亡的情况。异常行为的判定可以结合区域信息。比如扎堆异常如果多只鸡的跟踪框在短时间内向同一区域聚集即使单帧没检出异常也可以触发预警。这需要把检测结果和简单的空间聚类结合DBSCAN 跑一下就行。最后说一个我踩过的坑时序平滑会引入延迟。窗口 15 帧意味着行为切换后要 15 帧才能反映出来。对于死亡告警1 秒延迟可以接受但对于吃食喝水这种需要实时统计的行为延迟会影响采食量估算。我的做法是双轨输出——平滑后的状态用于告警和长时间统计原始单帧结果用于实时可视化。两者并存各取所需。这套方案从 484 张图起步训练半天后处理代码不到 100 行就能在 5000 只规模的棚里跑起来。模型精度不是终点工程上的平滑和逻辑兜底才是让产线愿意用的关键。希望帮到你。本文还有配套的精品资源点击获取
返回列表