简介这套家禽鸡行为数据集面向智慧养殖、动物行为识别与计算机视觉初学者提供YOLOv8格式标注的家禽行为检测数据可识别吃食、喝水、死亡、异常行为和睡觉等关键状态适用于目标检测、智慧畜牧监控等研究或演示项目。资源包共1429个文件内含714张JPEG图片、714个txt标注文件与1个yaml配置文件压缩后约39.33MB其中484张图片划为训练集图片与标签同名配对yaml描述类别配置可直接用于主流YOLO训练流程。图像素材多来自多个室内鸡舍场景的监控视频抽帧包含不同时间段和光照条件下的鸡只状态异常行为与正常状态并存有助于训练模型适应实际养殖环境。已有419人学习下载适合快速验证行为识别算法也可作为搭建家禽异常监测原型的起步数据。1. 家禽鸡行为数据集是什么484张图能撑起几个检测任务第一次拿到“家禽鸡行为数据集”这个项目名时我下意识算了一笔账484张训练图片、五类行为、YOLO v8格式标注。这个规模放在通用目标检测里不算大但放在养殖场景里刚好是两个人一周的标注量。它想解决的是鸡舍里最实际的四个问题——哪些鸡在吃食喝水、哪些鸡已经死了、哪些鸡状态不对劲、哪些鸡只是睡着了。把这套行为识别模型跑通鸡舍管理员每天巡栏的工作量能省掉大半。这篇笔记写给准备用YOLO v8训练自己的鸡舍数据集的人这484张图该怎么组织、训练参数怎么定、为什么死亡和睡觉会互相误判、小样本下哪些坑一定躲不过我会按自己的落地经验从头拆到尾。2. 把鸡行为数据接到YOLO v8训练链路数据结构、超参与首次训练2.1 labels与images目录怎么摆YOLO v8需要的最终目录结构拿到这个“家禽鸡行为数据集”之后第一步不是急着训练而是先把目录结构摆对。YOLO v8ultralytics 框架对数据目录的约定非常死板图片和标注必须分开放训练集和验证集必须分目录标注文件名要和图片名完全一致后缀从.jpg换到.txt。目录摆错后面所有脚本都会在找文件这件事上报错。常见做法是把数据集整理成下面这个样子dataset/ ├── images/ │ ├── train/ # 484张训练图片jpg/png均可 │ └── val/ # 验证图片原数据集没给就自己从train里切 ├── labels/ │ ├── train/ # 与train图片同名的一批.txt文件 │ └── val/ └── data.yaml # 类别名、路径、类别数量声明图片和标注的对应关系是“同名不同后缀”IMG_001.jpg对应IMG_001.txttxt 里每一行代表一个目标框格式是class cx cy w h四个坐标值全部归一化到 0~1 之间。注意cx cy是中心点坐标不是左上角坐标这一点和 COCO 的 JSON 格式完全不同很多人第一次转格式就栽在这里。原数据集只提供了484张训练集图片验证集需要自己处理。我的习惯是拍摄一段视频后按时间顺序排好每隔N帧抽一张进验证集而不是随机抽。随机抽帧会让同一只鸡的不同姿态同时出现在训练集和验证集里测出来的是“记忆”而不是“泛化”。data.yaml 的内容也很短但类别顺序写错会直接导致训练时标签错乱# data.yamltxt里的类别数字和这里的顺序严格绑定 path: /home/user/dataset train: images/train val: images/val names: 0: eating 1: drinking 2: dead 3: abnormal 4: sleeping这里有一点必须养成的习惯先写 names再让标注者按这个顺序标注。如果某天你在 txt 里看到3而 data.yaml 里3是 sleeping那模型学到的就是“睡觉就是异常”整个实验结果全部作废。类别顺序是数据集的“契约”后改一次等于重新标一批。2.2 一份可复跑的YOLO v8训练脚本从data.yaml到train目录结构摆好后直接跑训练。用 ultralytics 的 Python API 比命令行更容易控制参数也方便后面做交叉验证。这是我会用的第一版脚本参数针对家禽行为识别这个场景单独调过from ultralytics import YOLO # 用COCO预训练权重起步而不是随机初始化原因见第3章 model YOLO(yolov8s.pt) results model.train( datadata.yaml, epochs120, # 小数据集靠增加epoch弥补别只看50轮 batch8, # 显存小就调到4批量太小要配合调低学习率 imgsz640, # 鸡在画面里通常不大别降到320 patience20, # 连续20轮验证集指标不涨就早停 mosaic0.5, # 马赛克增强对“死亡/睡觉”有副作用见3.2 hsv_h0.02, # 颜色扰动调小鸡舍光源本身就不稳定 fliplr0.5, # 水平翻转安全鸡左右对称为主 flipud0.0, # 垂直翻转会让站着的鸡变成躺姿必须关 pretrainedTrue, # 加载yolov8s.pt里的COCO权重 )这就是用 YOLO v8 训练自己的数据集最常见的第一版配置。几个参数为什么这么设epochs120是给足学习时间。484张图不算多但五类行为里“吃食、喝水、睡觉”这三个类别出现频率高模型需要更多轮次去收敛边界框。batch8属于折中640分辨率下8张图约占用9~11GB显存如果你的显卡只有8GB改成4同时把lr0从默认的0.01调低到0.005。mosaic0.5是我特意留的口子。ultralytics 默认会把马赛克增强概率开到1.0也就是每张训练图都有50%概率是由4张图拼出来的。在通用检测里这个增强很强但在“死亡”这类静态目标上问题很大——一只死鸡被马赛克裁掉一半标签框里只有半条鸡腿模型学到的特征就是“半只鸡死亡”。先设0.5跑一版等baseline出来再决定关不关不要一上来就默认全开。flipud0.0是我在这个数据集上最坚持的设定。鸡舍摄像头通常是俯视或者斜视翻转后鸡变成“脚朝天”的仰躺姿态这和“死亡”类别的视觉特征几乎一样。模型会把所有仰躺的鸡都判成死鸡而实际上它们只是被数据增强弄成了仰躺的活鸡。训练完成后看runs/detect/train/目录下的results.png重点看val/box_loss和val/cls_loss两条曲线是否在训练后期还在下降。如果两条曲线在40轮后就走平甚至回升把patience改小到10~15早停比硬跑完120轮更稳。2.3 吃食、喝水、死亡、异常、睡觉五个检测任务怎么拆这五类行为在检测难度上不是平均的分类讨论比一锅炖更实际。“吃食”和“喝水”属于常规目标检测特征是头部低垂、喙部接触食槽或水线目标框小但视觉特征明确。这类任务用标准检测头就能做难点主要在密集场景——一圈鸡围在食槽边互相遮挡边界框会大量重叠。此时conf阈值别设太低我习惯设0.45以上宁可漏检也别让框互相打架。“死亡”和“睡觉”是一对麻烦组合。两者都是静态目标都躺在地上俯视视角下死鸡和睡鸡的轮廓几乎重合。单张图片上区分这两类本质上是“猜”因为有些死鸡就是安安静静蜷缩着和睡觉的姿态一模一样。这个任务不能只靠检测器要靠后端的时序逻辑第5章会专门讲。“异常行为”是最需要警惕的类别。这个标签如果定义不严标注者会把“转圈、抓挠、喘气、走路跛”全塞进一类结果这一类里面的视觉特征比其余四类加起来还多。模型能学会“框住一只行为古怪的鸡”但学不会“什么算异常”。这也是这个数据集的真正难点所在——类别数量少但类间边界模糊。我的一个具体建议5类里吃食、喝水可以当普通检测任务做睡觉和死亡必须连同帧间逻辑一起设计异常行为单独做消融实验——砍掉这一类的模型mAP大概率比留着高。如果项目目标是快速上线先做 eating、drinking、dead、sleeping 四类abnormal 放到第二阶段再补。3. 484张训练图到底够不够用样本量估算与数据增强的边界3.1 目标检测小样本场景的起步线每类目标至少放多少框484张图这个数字看起来很紧张但真实的约束不是图片数量而是“每一类行为到底有多少个标注框”。一张广角鸡舍图里可能有5只鸡在吃食、3只在睡觉、1只在喝水但死亡和异常行为的出现频率可能低到一两百张图里才有一例。标注框数量才是决定模型能不能收敛的关键。假设这484张图平均每张出现吃食4个、喝水2个、睡觉3个、死亡0.2个、异常0.1个总框数大约是吃食1936、喝水968、睡觉1452、死亡97、异常48。这个数字分布其实是养殖场景数据集的典型状态常见行为上千框稀有行为不到一百框。行为类别估算总框数小样本起步底线是否够用eating1500500够drinking800~1200500勉强够sleeping1000~1500500够dead50~150300不够必须增补abnormal20~100300严重不够建议合并或砍掉注意看dead和abnormal它们作为行为类别是养殖户最关心的两个信号但在标注数据里又是最稀有的两类。如果原数据集的死鸡只有几十个框模型只会把死鸡当背景甚至会把死鸡学习成“背景的一部分”因为训练时负样本里包含大量死鸡区域。一个我在动手训练前必做的步骤先写脚本统计每类框数。这一步能避免你带着“484张图足够训练”的错误自信往下走。统计方法很简单遍历labels/train下的每个txt按行首的类别数字计数import os from collections import Counter label_dir labels/train class_count Counter() for name in os.listdir(label_dir): if not name.endswith(.txt): continue with open(os.path.join(label_dir, name)) as f: for line in f: line line.strip() if not line: continue cls line.split()[0] class_count[cls] 1 for cls, cnt in class_count.most_common(): print(fclass {cls}: {cnt} boxes)跑完这个脚本你会得到一份真实的家底如果dead只有82个框那么在动训练之前应该先补数据而不是调参。补数据的方式我建议两条路同时走一条是从已有视频里抽帧并半自动标注另一条是用预训练模型先跑一遍伪标注人工修正后并入训练集。伪标注能快速把稀有类的框数拉起来但注意修正质量——死鸡和睡觉的误标会让模型更混乱。3.2 增强别乱上马赛克、旋转、亮度对“死亡/异常行为”的副作用数据增强是YOLO v8训练自己的数据集时最容易被误用的环节。通用目标检测社区推荐的“mosaic、mixup、随机旋转”在鸡行为场景下会制造出大量不存在的训练样本。关键在于增强不是为了增加图片数量而是为了模拟真实部署时的拍摄变化。鸡舍摄像头的真实变化只有三种光照变化、视角微变、鸡群的移动。先说马赛克。它把4张图拼成一张框跨图边界会被裁剪甚至丢弃。对“吃食、喝水”这种目标密集的图马赛克会丢失大量小目标反而降低召回。对“死亡、异常”这类本身框就少的类别马赛克一裁稀有框直接消失。我的做法是第一版把mosaic设为0跑出baseline再开0.5对比。如果开着mosaic的版本mAP更低不要惊讶这是小样本稀有类场景下的常见结果。再说旋转和翻转。fliplr0.5水平翻转是安全的鸡的躯干左右对称翻转后框仍然合理。flipud必须为0原因2.2里说过垂直翻转把活鸡变成仰躺姿态直接污染“死亡”类的视觉效果。旋转超过90度也是一样的道理鸡不会头朝下站着这些增强出来的样本在现实里不存在模型学到的是伪规律。最后说亮度与颜色扰动。鸡舍的光照不稳定白天强光、夜间红外、逆光剪影所以hsv_h、hsv_s、hsv_v可以保留一点让模型对光照变化更鲁棒。我给的值是hsv_h0.02, hsv_s0.5, hsv_v0.3比默认值低因为鸡舍整体的色调单一太强的颜色扰动会让鸡和背景的颜色关系错乱。3.3 用公开预训练权重还是从头训COCO权重对鸡行为检测的实际帮助这个数据集有必要用预训练权重但用哪个规格很有讲究。COCO预训练权重里有“bird”这个类别模型已经见过鸟类的基本轮廓、羽毛纹理、双足姿态。迁移到鸡的行为识别上骨干网络对鸡的“形状先验”是有的——它知道这是一只鸟形的物体只是不知道它是吃食还是睡觉。这比从头随机初始化强得多尤其只有484张训练图时随机初始化的模型连“鸡长什么样”都还没见过就要学五类行为收敛极慢且容易过拟合。规格选择上yolov8s.pt是我在这个规模下的默认选择。yolov8n太轻骨干网络特征提取能力不足小目标水线旁边的喙部动作容易漏掉yolov8m以上的参数量在484张图上更易过拟合除非你有很强的正则化手段。如果显卡一般yolov8s在batch8、imgsz640下约占用9~11GB显存是性价比最高的档位。训练策略上不要一上来就全量微调。先用freeze10冻结前10层骨干只训练检测头和深层特征跑60轮让模型先学会“定位鸡”这个更粗的任务然后解冻全部层用很小的学习率继续微调30轮。第二步的解冻是必须的——冻结时分类特征还没对齐直接部署会看到大量的漏检和误检。我补充一个对比技巧把“从头训练”和“用yolov8s.pt预训练微调”各跑一版只看metrics.box.map50。如果两者差距小于0.1说明你的数据集与自然图像差异很大预训练帮助有限此时应当把精力放到数据补充上而不是继续调学习率。这个对比能帮你判断问题到底出在“数据量”还是“模型初始化”。4. YOLO v8格式标注的完整闭环转换、校验与类别顺序的坑4.1 从常见标注格式转到YOLO v8一个最小转换脚本处理数据集用于YOLO v8训练最容易翻车的不是模型而是txt标注文件。市面上大部分标注工具导出的是VOC格式的XML或者是LabelMe的JSON直接拿来训练会被 ultralytics 拒绝。常见做法是写一个转换脚本把XML或JSON里的坐标换算成归一化的中心点宽高。下面这个脚本是我自己常备的VOC转YOLO工具改动量小、能处理绝大部分情况import os import xml.etree.ElementTree as ET # 类别名与data.yaml的names顺序严格一致顺序错一个全部玩完 CLASS_MAP {eating: 0, drinking: 1, dead: 2, abnormal: 3, sleeping: 4} def voc_xml_to_yolo(xml_path, out_txt_path, img_w, img_h): tree ET.parse(xml_path) root tree.getroot() lines [] for obj in root.iter(object): name obj.findtext(name).strip() if name not in CLASS_MAP: continue # 跳过不在类别表里的目标比如person box obj.find(bndbox) x1 float(box.findtext(xmin)) y1 float(box.findtext(ymin)) x2 float(box.findtext(xmax)) y2 float(box.findtext(ymax)) # 把左上角右下角坐标转成中心点 宽高再除以图片尺寸归一化 cx ((x1 x2) / 2) / img_w cy ((y1 y2) / 2) / img_h w (x2 - x1) / img_w h (y2 - y1) / img_h # 钳制到[0,1]浮点误差会导致个别框越界 cx min(max(cx, 0.0), 1.0) cy min(max(cy, 0.0), 1.0) w min(w, 1.0 - cx) h min(h, 1.0 - cy) lines.append(f{CLASS_MAP[name]} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) with open(out_txt_path, w, encodingutf-8) as f: f.write(\n.join(lines))这个脚本的核心逻辑就四步读XML里的目标框 → 把xmin ymin xmax ymax换算成中心点 → 除以图片宽高完成归一化 → 按class cx cy w h写入txt。需要解释两个容易被忽略的点。第一w min(w, 1.0 - cx)这一行的意思是如果框的右边超出图片右边界就把宽度截断到图片内。严谨的做法其实应该先对x2做min(x2, img_w)再算宽但写成min(w, 1.0 - cx)在结果上是等价的。第二类别名字符串必须和CLASS_MAP里的key完全一致包括大小写和空格。VOC格式里标注者经常写成Eating或者eating带空格不先做清洗直接转换这些框会被静默跳过训练时某个类别莫名少掉一批样本。4.2 标注文件最常见的三类错误坐标越界、类别错位、空标签转换完并不代表标注就合格了。我见过太多数据集训练时 loss 正常下降但验证集 mAP 低得离谱最后查出来是标注文件里有几百个空txt和越界框。写一个校验脚本在训练前把整个 labels 目录扫一遍成本极低收益极大import os label_dir labels/train errors [] total_files 0 for name in os.listdir(label_dir): if not name.endswith(.txt): continue total_files 1 path os.path.join(label_dir, name) with open(path) as f: lines [ln.strip() for ln in f if ln.strip()] if not lines: errors.append(f{name}: 空标注文件) continue for ln in lines: parts ln.split() if len(parts) ! 5: errors.append(f{name}: 列数不为5 - {ln}) continue cls, cx, cy, w, h parts try: cls_id int(cls) cx_f, cy_f, w_f, h_f float(cx), float(cy), float(w), float(h) except ValueError: errors.append(f{name}: 含非数字内容 - {ln}) continue if not (0 cls_id 4): errors.append(f{name}: 类别索引越界 - {cls}) if not (0.0 cx_f 1.0 and 0.0 cy_f 1.0): errors.append(f{name}: 中心点坐标越界 - {ln}) if not (0.0 w_f 1.0 and 0.0 h_f 1.0): errors.append(f{name}: 宽高越界 - {ln}) print(f共检查 {total_files} 个标注文件发现 {len(errors)} 个问题) for e in errors[:30]: print(e)三类错误要重点讲透。空标注文件图片里确实可能一只鸡都没有此时txt应该是一个零字节文件。但更常见的情况是转换脚本出错导致某些图片的标注没写进去。空文件会让YOLO v8在训练时把整张图当负样本如果空文件集中在某一类行为比如所有死鸡图都空那这个类别就永远学不到。坐标越界上面转换脚本做了钳制但如果原始标注框本身超出图片范围钳制后框的形状会变形。一个跨越图片右边界的框被截断后剩下的区域可能只有鸡的尾巴模型学到的特征就是“一段羽毛死鸡”。出现越界一般说明标注时画框太草率或者图片被resize过而标注没有跟着变。类别错位这类错误通常不是标注写的错误而是data.yaml的names顺序和txt里的数字对应不上。比如标注工具里“eating”在第0位但你data.yaml里第0位写的“sleeping”那模型的脑子就彻底乱了。校验脚本能发现“类别索引越界”但发现不了“类别索引与实际类别不符”——这一条只能靠可视化抽检来抓。4.3 标注质量抽检用一张热力图判断边界框是否对齐机器校验只能查格式错误查不了“框得准不准”。一个边界框歪到鸡脖子上的标注格式完全合法但模型学到的就是错误的中心点。我的做法是写一个批量画框脚本把每张图的标注框画出来抽样人工检查。你不用看完全部484张每类抽20张喝杯咖啡的功夫就能看完import cv2 # 每个类别一个颜色和data.yaml顺序对应 COLORS [(0, 255, 0), (255, 200, 0), (0, 0, 255), (0, 255, 255), (255, 0, 255)] CLASS_NAMES [eating, drinking, dead, abnormal, sleeping] def draw_boxes(img_path, txt_path, out_path): img cv2.imread(img_path) h, w img.shape[:2] with open(txt_path) as f: for line in f: parts line.strip().split() if len(parts) ! 5: continue cls, cx, cy, bw, bh parts cls int(cls) cx, cy, bw, bh float(cx), float(cy), float(bw), float(bh) # 归一化坐标转回像素坐标 x1 int((cx - bw / 2) * w) y1 int((cy - bh / 2) * h) x2 int((cx bw / 2) * w) y2 int((cy bh / 2) * h) color COLORS[cls] cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) cv2.putText(img, CLASS_NAMES[cls], (x1, max(0, y1 - 5)), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2) cv2.imwrite(out_path, img)抽检时重点看两类场景。一类是“两只鸡离得很近框是不是把两只都包进去了”——如果框把相邻鸡的尾巴也框进来标签的中心点就会偏向两只鸡的中间偏右模型学到的定位特征就会是“鸡群中心点”。另一类是死鸡框——死鸡和地面颜色接近标注者很可能把框画大一圈把地面也包进来这会让模型把“地面纹理”也当成死鸡特征。提醒一个细节标注抽检要用原图分辨率来画不要在缩略图上检查。640x640缩略图里差5个像素的边界框误差在原图上可能差出30个像素这在小目标检测里是致命影响。5. 家禽行为识别避坑小样本、类间混淆与鸡舍部署的四个翻车现场5.1 死亡行为被误判成睡觉躺姿与闭眼的特征冲突现象训练结束后单独测“死亡”这个类别precision 很高但 recall 惨不忍睹接近一半的死鸡被框成“睡觉”。反过来有些睡觉的鸡又被框成“死亡”导致养殖员收到一堆假警报。原因俯视摄像头里死鸡和睡觉鸡的视觉特征高度重合——都是趴着不动都是闭眼羽毛颜色没有明显区别。单帧图像里这两类的可分性很低模型只能靠姿态猜而姿态本身并不区分这两类。这个不是模型调参能解决的是任务定义的问题。解决我的做法是把“是否死亡”的判断从单帧检测挪到时序逻辑上。检测器只负责输出“这只鸡趴着不动”后端用一个跟踪器记录同一个目标框的静止时长。鸡的正常睡眠有周期会偶尔动一下翅膀、换个姿势死鸡在更长的时间窗口内完全不动。所以后端逻辑是连续N帧同一目标未移动且置信度稳定才把类别从“sleeping”改判为“dead”。这个N值需要根据鸡舍的观察标定常见思路是先统计正常睡眠鸡的最长静止帧数再取它的1.5倍作为死亡判定阈值。这样的方案不会让“死亡”类检测精度变高但会让最终报警准确率高很多。5.2 异常行为类别过杂把“异常”拆开还是合并现象训练时带着abnormal这一类整个模型的mAP上不去尤其验证集上abnormal的AP只有0.1左右几乎等于随机猜。原因这是典型的“类别过宽”问题。标注者把转圈、抓挠、喘气、跛行、仰头张嘴全部标成abnormal这些行为的视觉特征差异天差地别——转圈是运动模式异常喘气是静态姿态异常跛行是步态异常。模型在484张图上要学的是一个内部差异极大的类学到的是“各种奇怪东西的叠加平均”自然学不好。解决如果abnormal框数不足300最稳妥的办法是砍掉这一类先跑其余四类的检测上线后单独针对养殖户最关心的异常行为比如喘气补数据专训。如果项目定义里abnormal必须保留那就拆成可量化的子类比如“abnormal_move”和“abnormal_still”分别对应运动异常和静态异常让每一类的视觉特征尽量统一。如果连子类的框数也不够还有一个妥协方案用“正常类检测”来反推异常——训练一个只检测eating、drinking、sleeping的模型在推理时凡是鸡所在位置没有被这三个类别覆盖且置信度低于阈值就标为“待观察”。这种方法在行为识别项目里很常见本质上是用“正常”的边界来定义“异常”。5.3 夜间和逆光场景下的漏检要不要补红外图现象白天测试mAP50能到0.75一到晚上开启红外补光后漏检率飙升到40%以上尤其“吃食”这种小目标几乎全部丢失。原因这本质是domain shift。训练集是白天RGB图推理环境是红外灰度图两者在纹理、亮度分布、对比度上差异巨大。模型学到的“鸡的纹理”在红外图像上不存在边界框定位就发散。解决不要在部署时才发现问题训练阶段就混入夜视图。常见做法是把夜间红外视频抽帧灰度化后和白天训练图混合按1:4左右的比例混入。如果拿不到夜视数据至少要在推理前做预处理把输入图像灰度化并做CLAHE自适应直方图均衡化拉大局部对比度给模型一个更接近训练分布的输入。用OpenCV几行就能做import cv2 img cv2.imread(night.jpg, cv2.IMREAD_GRAYSCALE) clahe cv2.createCLAHE(clipLimit3.0, tileGridSize(8, 8)) img_clahe clahe.apply(img)这个预处理不能100%解决问题因为红外图的亮鸡和暗背景关系可能和RGB正好相反但能缓解一部分漏检。真正彻底的方案还是收集一批夜间红外样本标注入训练集。另外提一句有些养殖项目的摄像头带红外有的不带部署前先确认摄像头类型再决定模型策略不要默认所有鸡舍拍出来都是同一个样子。5.4 训练集484张验证集50张mAP波动大是数据集玄学现象同样的代码、同样的参数跑两轮训练验证集mAP50一次0.58一次0.74反复横跳。换随机种子结果也跳完全没法说某个改动到底是变好还是变差。原因这是验证集太小导致的评价噪声。只有50张验证图每张图的检测结果对整体指标的影响高达2个百分点一次漏检就能把mAP拉低0.02以上。加上如果验证集和训练集是随机抽帧来的同一群鸡的不同姿态会同时出现在两边模型记忆过的内容混进了验证集测出来虚高。解决第一验证集不要随机抽帧按视频时间段切——取一段连续时间的视频帧做验证这段时间里的鸡群不参与训练才能测出真正的泛化能力。第二用K折交叉验证取代单次划分把整个数据集按视频块分成5份轮流做验证集取5次的平均mAP作为最终指标。第三如果验证集实在太小就用“重训练多次评估”的方式训练3次取最好的一次只用来做方案对比不要用单次结果下结论。在只有484张训练图的小样本场景下任何单次mAP都是带噪声的不能信。6. 用验证集和混淆矩阵验收行为识别模型上产线前的最后一关模型跑完不是结束上线前必须做一次针对性的验收重点不是总mAP而是“死亡”和“睡觉”这对混淆冤家。用 val 目录跑一遍评估打开混淆矩阵看看from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) metrics model.val(datadata.yaml, splitval, batch8) print(mAP50:, metrics.box.map50)跑完后看runs/detect/val/confusion_matrix.png。理想状态是对角线亮、背景列有一些不可避免的误检。重点看第3行第4列dead预测成sleeping和第4行第3列sleeping预测成dead这两个格子的亮度。如果这两个格子颜色和主对角线差不多亮说明模型在做盲猜后端必须配时序逻辑兜底。我还习惯把abnormal那一行单独截图看——如果abnormal几乎全被预测成背景说明这个类别没有学到任何有效特征上线前建议直接去掉别让它拉低整体精度。验收的最后一步是用一段没进过数据集的真实鸡舍视频做长时间推理统计每个类别的帧级检出数。常见坑是模型在单帧上表现不错但视频中同一只死鸡一会儿检出、一会儿丢失报警系统会被这种闪烁信号折磨疯。我现在的习惯是对检测结果做时间窗口滤波同一目标至少连续3帧被检测到才输出连续10帧消失才认为目标离开。这个小技巧成本极低但能让产线上的行为识别结果稳定一大截。如果你以后切到更新的yolo26API基本不变把YOLO(yolov8s.pt)换成YOLO(yolo26s.pt)就行上面的验收流程照用。这类小样本家禽行为数据集值不值得投入我的判断是值得但要换对预期——它做不了“一次性端到端识别所有异常”能做的是“把吃食、喝水、睡觉、死亡这四个状态在鸡舍里稳定检测出来并给异常行为留一个可扩展的口子”。我现在的习惯是拿到数据集先数框、切验证集、关掉危险增强跑baseline再看混淆矩阵决定后端要不要加时序逻辑最后才谈调参。这个顺序救过我很多次。希望帮到你。本文还有配套的精品资源点击获取