
简介面向养殖场智能化管理场景一份整合YOLOv5生猪行为状态检测训练权重、1000余张标注图像与PyQt界面的完整资源包。该方案覆盖数据、配置、脚本与界面代码适用于智慧养殖、计算机视觉方向的研究者以及需要快速搭建猪只行为识别原型的技术人员。压缩包共2000个文件以txt标签、jpg图片为主辅以yaml数据集配置、py脚本、xml注释文件等总体积约274MB。数据集已划分train、val、test并附data.yaml标注类别涵盖eat进食、stand站立、lie躺卧、attack攻击四类行为可直接用于YOLOv5、YOLOv7、YOLOv8、YOLOv9等主流算法的训练省去数据整理与格式转换环节。Python源码中包含界面相关模块可支持可视化检测演示便于观察模型效果与结果输出。资源包内另有训练缓存文件与说明文档帮助使用者快速理解目录结构。已有447人学习下载适合需要完整训练闭环与界面参考的开发者直接上手。1. 养殖场猪只行为检测为什么绕不开yolov5一个能真正跑起来的农业AI方案养殖场里装了几十路监控靠人肉盯屏判断猪有没有打架、不吃食、精神萎靡基本是个不可能完成的任务。我这两年接触过不少养殖场落地需求最后真正能交给饲养员日常用的大多是yolov5加PyQt这套组合yolov5负责把视频帧里的猪框出来并判成站立、卧躺、进食、走动、争斗这几个行为提前训练好的权重文件直接加载推理PyQt把模型封装成一个带按钮、带实时画面的桌面工具。这个方案不需要高配服务器一张普通显卡甚至CPU都能跑配上1000张标注数据就能达到可用的识别精度适合养殖场技术员、农业AI开发者和相关方向的毕设项目。很多人一上来就追新模型但换到实际项目里yolov5的优势是生态太成熟了开源仓库稳定、训练命令简单、权重转换和部署资料多踩坑也都能搜到解决方案。与其在yolov8、RT-DETR里折腾半天调不通环境不如先用yolov5把行为检测的闭环跑起来。下面这几章我就按数据集整理、模型训练、PyQt界面封装、现场验证这条完整链路把能直接照做的步骤和参数写清楚。2. 1000张生猪行为数据集从哪里来、怎么标类别定义与标注格式转换2.1 行为类别怎么定先想清楚要检测什么做行为检测的第一步不是打开标注工具而是把类别定义死。养殖场里猪的行为其实没那么多常见的是站立、卧躺、进食、走动、争斗这五类有的项目会加一个“饮水”。我给养殖场做方案时只保留了前五类因为类别越多1000张数据的平均到每类的样本量就越少模型越容易混。类别行为定义养殖场关注点standing四脚着地静止或原地轻微移动基础正常状态lying侧卧或伏卧腹部贴地卧躺比例异常升高可能预示生病eating头部伸向料槽并低头取食进食量下降是健康预警信号walking在栏内正常走动用于判断发情、应激等特殊状态fighting追逐、撕咬、顶撞需要人工介入防止伤亡各类别的边界要写进标注规范里比如“站立时前腿动了一下算不算走动”这类必须提前定好不然两个人标出来的数据会打架。1000张图标注的工作量不大按每张图平均2到3头猪算一个人两天左右能完成但标注一致性比速度重要得多建议先标50张检查一遍再继续。2.2 标注工具与格式转换从VOC XML到YOLO txtyolov5原生支持两种标注格式一种是YOLO txt每行一个目标内容是类别id、归一化后的中心点坐标和宽高另一种是VOC XML就是常见的PascalVOC格式。用LabelImg标注时可以直接在保存选项里选YOLO格式但很多人不熟悉默认存成了VOC XML所以转换脚本几乎是必写的。import xml.etree.ElementTree as ET import os def xml_to_yolo(xml_file, out_dir, class_map): tree ET.parse(xml_file) root tree.getroot() size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) out_lines [] for obj in root.findall(object): name obj.find(name).text if name not in class_map: continue cls_id class_map[name] box obj.find(bndbox) x_min int(box.find(xmin).text) y_min int(box.find(ymin).text) x_max int(box.find(xmax).text) y_max int(box.find(ymax).text) # 转成归一化的中心点坐标和宽高 x_center (x_min x_max) / 2.0 / img_w y_center (y_min y_max) / 2.0 / img_h w (x_max - x_min) / img_w h (y_max - y_min) / img_h out_lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) if out_lines: out_path os.path.join(out_dir, os.path.basename(xml_file).replace(.xml, .txt)) with open(out_path, w) as f: f.write(\n.join(out_lines))逻辑说明这段代码把XML里的绝对坐标换算成归一化坐标这一步如果分母用错训练时损失函数会直接异常。class_map是一个字典比如{standing: 0, lying: 1, eating: 2, walking: 3, fighting: 4}转换时按标签名字映射成数字id。注意类别id必须从0开始连续不能跳跃否则yolov5的数据加载层会报错。参数说明xml_file是单个XML路径out_dir是输出txt的目录class_map决定类别到id的映射。一个常见的坑是XML里的filename和实际图片文件名不一致建议转换后统一检查一遍。标注完成后的目录结构应该是images和labels两个文件夹里面按train、val子目录分开这个结构yolov5在训练时直接按路径读取。2.3 训练集/验证集划分不要按视频帧抽按图片抽很多人从监控视频里抽帧做数据然后随手按顺序划分训练集和验证集这是训练效果翻车的高发原因。同一个视频连续帧里的猪几乎一模一样如果训练集和验证集里有相邻帧验证指标会虚高部署到新场景马上现原形。正确做法是把所有图片先洗牌再按比例划分。import os import random from shutil import copyfile random.seed(42) img_dir datasets/pig/images label_dir datasets/pig/labels val_ratio 0.2 images [f for f in os.listdir(img_dir) if f.endswith(.jpg)] random.shuffle(images) val_count int(len(images) * val_ratio) val_images images[:val_count] train_images images[val_count:] def move_dataset(imgs, subset): os.makedirs(fdatasets/pig/images/{subset}, exist_okTrue) os.makedirs(fdatasets/pig/labels/{subset}, exist_okTrue) for name in imgs: copyfile(os.path.join(img_dir, name), fdatasets/pig/images/{subset}/{name}) txt name.replace(.jpg, .txt) copyfile(os.path.join(label_dir, txt), fdatasets/pig/labels/{subset}/{txt}) move_dataset(train_images, train) move_dataset(val_images, val)逻辑说明random.seed(42)固定随机种子保证每次跑脚本得到的划分结果一致方便复现和对比实验。洗牌之后取前20%做验证集剩下的做训练集。拷贝而不是移动文件这样原图目录还能留底万一划分不对可以重新跑。参数说明val_ratio0.2是默认经验值数据量少到几百张时可以提到0.25数据量大到几千张时降到0.15也够。划完之后必须检查labels目录里有没有对应的txt文件一张图如果漏标了训练时会被跳过但不会报错等于白白浪费一个样本。这一步看着简单实际翻车率最高我每次都会在划分后打印train和val的文件数做核对。2.4 数据集质量检查空标注、小目标和类别不平衡标注数据里最隐蔽的问题是空标注文件。LabelImg偶尔会生成一个没有任何对象的txt大小为0字节训练时它不影响梯度但val计算时会拉低mAP。还有一类问题是框太小猪离镜头远时目标框可能只有十几个像素yolov5默认下采样32倍这种小目标直接学不到特征。下面这段脚本能一次性检查出空标注和类别分布。import os from collections import Counter label_dir datasets/pig/labels/train cnt Counter() empty_files [] for name in os.listdir(label_dir): path os.path.join(label_dir, name) if os.path.getsize(path) 0: empty_files.append(name) continue for line in open(path): parts line.strip().split() cnt[int(parts[0])] 1 print(空标注文件数, len(empty_files)) print(各类别目标数量, dict(cnt))逻辑说明Counter统计每个类别id出现的次数输出来一眼就能看出有没有类别严重不平衡。比如fighting只有50个框standing有2000个模型学完大概率把所有猪都判成standing。解决方向不是删数据而是针对少数类别做翻转、旋转、亮度变化的数据增强或者多标一些争斗场景的图。这个环节跑完数据集才算真正能进训练流程。接下来第3章的环境配置和训练命令是另一个容易翻车的地方尤其是torch和CUDA的版本对应关系我在下一节里展开写。3. 训练自己的yolov5猪只行为权重conda环境、超参数与训练命令3.1 yolov5环境配置conda建环境与依赖安装yolov5环境配置的坑集中在Python版本、torch版本和CUDA版本三者不匹配上。常见做法是用conda单独建一个环境避免把系统Python搞乱。先拉yolov5源码再在环境里安装依赖这一套顺序别反了否则requirements.txt里的依赖版本可能和源码不匹配。conda create -n yolov5 python3.9 -y conda activate yolov5 cd yolov5 pip install -r requirements.txt逻辑说明python3.9是稳妥选项低于3.7有些新版依赖装不上高于3.10时torch的某些老版本找不到对应轮子。pip install -r requirements.txt会把torch、opencv-python、numpy、pandas这些一次性装好但默认装的是CPU版torch如果你有NVIDIA显卡建议先按显卡驱动对应的CUDA版本装torch再装requirements。参数说明requirements.txt里没有锁死torch版本只是给了下限所以环境配置前先确认显卡驱动支持哪个CUDA版本再用pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118这种方式装对应版本。CUDA装错最常见的现象是模型能加载但训练速度比CPU还慢或者直接报AssertionError: Torch not compiled with CUDA enabled。没有NVIDIA显卡也不用慌这个项目数据量小CPU训练一个晚上也能出结果只是--device参数要改成cpu。3.2 准备data.yaml与数据集路径yolov5靠一个yaml文件来定位数据集路径和类别信息。这个文件放在哪不重要重要的是里面的路径要对。常见做法是放在yolov5源码目录下的data文件夹里名字比如叫pig.yaml。train: ./datasets/pig/images/train val: ./datasets/pig/images/val nc: 5 names: [standing, lying, eating, walking, fighting]逻辑说明train和val路径是相对于yolov5源码根目录的不是相对于yaml文件所在目录这是最容易踩的坑。如果把路径写成绝对路径也能跑但换机器之后必须改不如一开始就用相对路径方便迁移。nc必须和names列表长度一致多写或少写训练时会直接报错。参数说明names的顺序必须和数据集标注时的class_map一致比如你在标注时把standing定义为0那这里names列表第一个就必须是standing否则模型训练时学到的类别名和推理时显示的名字会错位。这个错位不会报错但部署时你会发现框的位置是对的标签文字全乱了。3.3 训练命令与超参数epochs、batch、img、hyp训练命令的核心参数有四个图片尺寸、batch大小、训练轮数和超参数文件。对于1000张图、5类行为的小型数据集推荐配置是img640、batch16、epochs150超参数用yolov5自带的hyp.scratch-low.yaml。这个配置在8G显存的显卡上能稳定跑完。python train.py \ --data data/pig.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 150 \ --hyp hyp.scratch-low.yaml \ --name pig_run逻辑说明--weights yolov5s.pt是拿COCO预训练权重做迁移学习初始点yolov5会自动下载这个文件如果你在离线环境训练需要提前把yolov5s.pt放到源码根目录。用预训练权重不是耍赖而是在1000张数据规模下从零训练很难收敛迁移学习能把收敛时间缩短一半以上。--name pig_run是给这次训练起个名字输出的权重和日志都会放在runs/train/pig_run目录下。参数说明img640是速度和精度的平衡点养殖场监控画面通常有大量小目标降到416会明显损失精度提到1280显存不够且收益不大。batch16在8G显存下刚好显存不够就降到8或4batch和img是两个最直接影响显存占用的参数。epochs150对1000张图来说足够配合早停机制实际跑到120轮左右就会自动停。超参数这个东西有点玄学hyp.scratch-low.yaml里的lr0初始学习率默认是0.01如果你发现loss在训练前几十轮就震荡不降优先把这个值除以10。数据增强相关的参数比如hsv_h、hsv_s在光照不均的养殖场场景里可以适当调高能提升模型对夜间和逆光的鲁棒性。3.4 训练输出解读best.pt、last.pt和results.csv训练结束后yolov5会在runs/train/pig_run/weights目录下生成两个权重文件best.pt是验证集mAP最高的那轮权重last.pt是最后一轮的权重。部署和PyQt界面加载时一定要选best.pt这是很多新手会犯的错误。文件用途建议weights/best.pt验证集指标最好的权重部署、推理、PyQt界面加载都用它weights/last.pt最后一轮权重发生过拟合时不能用一般只用来断点续训results.csv每轮训练损失和mAP记录判断收敛状态和是否过拟合train_batch0.jpg第一个batch的标注预览图检查标注框是否贴合猪体results.csv每一行对应一轮训练包含box_loss、cls_loss、mAP0.5这些列。判断训练是否正常的标准是mAP0.5曲线先快速上升然后趋于平缓同时val/box_loss不再明显下降。如果mAP0.5涨到某个点后开始缓慢下跌基本就是过拟合了此时best.pt已经保存了最佳状态不用回头再调。train_batch0.jpg这预览图特别值得看如果框和猪的位置对不上说明标注转格式时坐标换算出了问题训练出来的权重再怎么调参都没用。4. yolov5训练与推理避坑权重不收敛、漏检误检、显存不够的排查记录4.1 现象loss在训练中途变成NaN训练到第30轮左右终端日志里box_loss突然变成nan然后整条曲线都是nanweights目录下的pt文件全部损坏。把学习率调到0.001之后重新训练问题消失。根因有两层第一是初始学习率0.01对这个小数据集偏大梯度更新直接飞了第二是标注数据里有空txt或者坐标越界的框yolov5在计算损失时除以了异常值。解决方法是训练前先跑一遍第2章的空标注检查脚本再在命令行里加--hyp参数覆盖lr0。python train.py --data data/pig.yaml --weights yolov5s.pt \ --img 640 --batch 16 --epochs 150 \ --hyp hyp.scratch-low.yaml --name pig_nan_fix在hyp文件里把lr0: 0.005改掉注意是修改复制出来的hyp文件而不是改源码里的原文件。如果做了这两步还出现NaN就检查数据集里有没有损坏的jpg图片一张坏图就能让整个训练崩溃。4.2 现象所有猪都识别成同一个行为训练结束后跑测试视频发现所有框都标成standing偶尔出一个lying其他类别完全不出框。查了验证集mAP数值看着还挺高这就是典型的类别不平衡造成的假性收敛。1000张图里standing占了2000个框fighting只有80个框模型学到的决策边界全部偏向多数类。看results.csv里各类别的mAP也能发现问题多数类mAP接近1少数类几乎为0。解决分三步走第一步用第2章的Counter脚本统计类别分布确认不平衡比例第二步减少standing样本中重复度高的图片比如连续帧里动作几乎一样的可以删掉第三步对少数类别做离线增强在训练前用脚本把fighting、walking这些图片各复制5份做左右翻转和亮度变化。处理后多数类和少数类的框数量比控制在10以内模型输出的类别分布才正常。4.3 现象训练后期val mAP不升反降训练日志里验证集的mAP0.5在第90轮到达峰值后开始缓慢下滑但训练集的loss还在下降这是标准的过拟合症状。1000张图训150轮模型开始记忆训练数据里的背景和噪点而不是学习行为特征。解决方法是训练时加早停参数让它在mAP连续20轮不提升时自动保存best.pt并停止。python train.py --data data/pig.yaml --weights yolov5s.pt \ --img 640 --batch 16 --epochs 150 --patience 20 \ --name pig_stop_early参数说明--patience 20表示验证集指标20轮无改善就停止。对1000张图的数据集这个值设置在15到30之间比较合适太小容易在mAP短暂平台期误停太大又省不了时间。另外要明确一点mAP下降后不要回头用last.pt它的效果比epoch 90的checkpoint还差老老实实用best.pt。4.4 现象推理时显存不足OOM训练没问题但用PyQt加载模型后同时跑两路视频显卡直接报CUDA out of memory。原因是推理时yolov5默认batch1、img640但PyQt界面里视频线程每帧都调model.predict短时间无法释放显存。解决方法是加载模型后主动设置推理参数限死img尺寸。model torch.hub.load(ultralytics/yolov5, custom, pathruns/train/pig_run/weights/best.pt) model.conf 0.25 model.iou 0.45 model.max_det 50 model.agnostic True model.cpu() # 显存不足时直接退到CPU推理逻辑说明model.max_det50限制单张图最多检测50个目标防止画面里猪太多时检测框数量爆掉显存。model.cpu()把模型搬到CPU推理速度和GPU差三五倍但胜在稳定养殖场现场的工控机不一定有好显卡。实际上1000张图训练出来的yolov5s模型在CPU上跑640分辨率能做到每秒10帧以上用于观察行为状态完全够用。4.5 现象换机器加载权重报错在训练机器上一切正常把best.pt拷贝到养殖场现场的工控机上import torch后加载权重直接抛RuntimeError: PytorchStreamReader failed reading zip archive。检查后发现现场机器装的torch版本比训练机器低且CPU指令集不支持模型里某些算子。解决方法是统一两边的torch版本或者干脆在训练机器上先把权重导出成TorchScript格式这样加载时不再依赖Python环境和torch版本匹配。import torch model torch.load(runs/train/pig_run/weights/best.pt, map_locationcpu)[model] model.eval() script_model torch.jit.script(model) script_model.save(best_script.pt)逻辑说明TorchScript是把模型结构固化进权重文件部署端只需要torch.jit.load就能加载不看torch版本。这是我在多台机器之间转移模型时最常用的做法比反复对齐环境省事得多。注意加载best.pt时要取[model]这一层直接torch.load整个文件拿到的是训练用的封装对象加载后还要调用model.eval()关闭dropout和BN层的训练行为否则推理结果会有随机性。5. 把训练好的权重装进pyqt界面实时检测、结果展示与线程卡顿处理5.1 pyqt界面该有的功能权重选择、视频源、置信度滑块PyQt界面在这个项目里的角色不是展示技术而是让饲养员愿意用。一个能实际交付的界面至少要包含权重文件选择按钮、图片/视频/摄像头三种输入切换、运行和停止按钮、置信度调节滑块、实时画面显示区、检测结果统计区。这几个功能对应场景特别明确饲养员不会敲命令行他需要开机能用、滑块能调灵敏度、画面里有框和标签。控件类型作用选择权重按钮QPushButton加载训练好的best.pt打开图片按钮QPushButton检测单张图片打开视频按钮QPushButton检测离线视频文件打开摄像头按钮QPushButton连接USB摄像头或RTSP流置信度滑块QSlider实时调整conf阈值范围0到100画面显示区QLabel显示带检测框的视频帧行为统计标签QLabel显示当前帧各行为数量5.2 用QThread做推理线程不卡界面的关键第一次写PyQt界面最容易犯的错误是直接在按钮点击回调里写while循环读视频帧、跑模型、setPixmap刷新画面。结果是一点“开始”按钮界面瞬间无响应鼠标转圈这就是把阻塞操作放进了UI线程。PyQt里的UI线程要即时响应用户操作而模型推理一帧要几十毫秒在处理期间UI消息循环被堵住界面自然卡死。血泪经验是所有耗时的推理循环必须放进QThread子线程UI线程只负责接收结果并刷新控件。import cv2 import time from PyQt5.QtCore import QThread, pyqtSignal class DetectWorker(QThread): frame_ready pyqtSignal(object, object, list) def __init__(self, model, source, conf0.25): super().__init__() self.model model self.cap cv2.VideoCapture(source) self.conf conf self.running True def run(self): while self.running: ok, frame self.cap.read() if not ok: break results self.model(frame, size640, conf_thresself.conf) boxes results.xyxy[0].tolist() # 发送原始帧和检测结果给UI线程 self.frame_ready.emit(frame, results, boxes) self.msleep(30) # 约30帧每秒给UI线程留刷新时间 def stop(self): self.running False self.wait()逻辑说明frame_ready是一个信号emit之后PyQt会把帧和结果打包发送到UI线程UI线程的槽函数里再去做setPixmap和刷新统计文本。self.msleep(30)控制循环频率如果去掉这行视频流会以最高速度跑CPU占用飙升且UI刷新不过来。self.cap用cv2.VideoCapture打开同时兼容本地视频文件路径和RTSP摄像头地址。参数说明source可以是视频文件路径也可以是rtsp://192.168.1.64/stream这种摄像头地址。conf是初始置信度阈值对应界面滑块的默认值。model(frame, size640, conf_thresself.conf)这里必须显式传size参数因为yolov5默认推理尺寸是训练时的640现场画面分辨率可能是1080P不限制尺寸的话每帧都要缩放整个大图耗时翻倍。5.3 结果接收与界面刷新信号槽的正确用法Worker线程把结果发出来后界面部分要做两件事把OpenCV的BGR帧转成QImage显示到QLabel以及统计当前帧各类别的数量更新标签。from PyQt5.QtGui import QImage, QPixmap from PyQt5.QtCore import Qt from collections import Counter class MainWindow(QMainWindow): def __init__(self): super().__init__() # 假设worker已在打开视频时创建 self.worker.frame_ready.connect(self.on_frame_ready) def on_frame_ready(self, frame, results, boxes): rgb cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) h, w, ch rgb.shape img QImage(rgb.data, w, h, ch * w, QImage.Format_RGB888) # 画检测框和标签这里用results.render()快速出图 annotated results.render()[0] rgb_annotated cv2.cvtColor(annotated, cv2.COLOR_BGR2RGB) h, w, ch rgb_annotated.shape img QImage(rgb_annotated.data, w, h, ch * w, QImage.Format_RGB888) self.video_label.setPixmap(QPixmap.fromImage(img)) class_names self.worker.model.names cnt Counter() for box in boxes: cls_id int(box[5]) cnt[class_names[cls_id]] 1 self.count_label.setText(f站立:{cnt.get(standing,0)} f卧躺:{cnt.get(lying,0)} f进食:{cnt.get(eating,0)} f走动:{cnt.get(walking,0)} f争斗:{cnt.get(fighting,0)})逻辑说明results.render()是yolov5自带的方法直接在原图上画好框和类别标签省去手动调cv2.rectangle的麻烦。QImage需要RGB数据所以先做cvtColor转换时注意三通道和步长的对应关系否则图会出现色彩错乱。行为统计用Counter按类别id计数再映射成类别名显示。信号槽的这个设计里有个关键细节on_frame_ready运行在UI线程所以这里的所有操作要轻量不能做任何耗时计算。如果你在槽函数里写了个文件保存操作或者又去调模型推理界面照样卡。复杂逻辑一律放Worker里槽函数只负责刷新画面和更新文本。5.4 置信度滑块怎么接实时调整不重启置信度滑块应当能随时调整而不是改完必须重启界面。做法是把QSlider的valueChanged信号连接到一个槽函数槽函数里直接改worker的conf属性。由于conf只是Python对象的一个属性子线程每帧读取时都会拿到最新值不存在加锁问题。def on_conf_changed(self, value): conf value / 100.0 self.worker.conf conf self.conf_label.setText(f置信度: {conf:.2f})逻辑说明滑块的value范围是0到100除以100映射到0.0到1.0的置信度区间。Worker的run循环里每帧都用self.conf_thres新值推理。这里要注意滑块变化不要把范围拉得太低0.05置信度会输出一堆噪声框画面瞬间被框铺满我一般在界面的滑块上把最小值限在10对应0.1置信度。关闭视频时记得调用worker.stop()否则子线程还在跑界面退出了程序却关不掉这是PyQt新手最容易遇到的后台进程残留问题。6. 在养殖场实地验证接入摄像头、调置信度阈值、长期运行习惯6.1 先在离线视频上验证再接RTSP实时流到养殖场现场后不要直接对着摄像头调参先在工控机上跑一段现场录制好的监控视频把模型在真实光线和栏位环境下的表现看一遍。离线验证通过后再接RTSP流因为RTSP调试涉及网络延迟、画面掉帧容易和模型问题混在一起一个一个问题排查才省时间。import cv2 cap cv2.VideoCapture(rtsp://192.168.1.64:554/stream1) assert cap.isOpened(), 无法连接摄像头先检查网段和RTSP地址逻辑说明RTSP地址里的IP、端口、路径由现场摄像头的品牌和配置决定海康、大华、宇视的默认路径格式不一样入场前先向场区IT拿到可用的RTSP地址别自己猜。6.2 现场阈值调整与长期运行习惯现场调参的核心就两个阈值置信度conf和NMS的iou。白天光线好、猪栏里活动少conf0.35能最大限度减少误报如果现场环境暗模型本身检测分数会下降conf降到0.25才能保证不漏检。iou一般维持0.45不用动除非多头猪紧紧挨着导致框互相吞并才把iou降到0.3。我自己的习惯是每次现场调试后把当天识别结果里框错的画面截图存成一个bad case文件夹攒到两三百张就补充标注、继续用--resume从之前的权重接着训练几轮。长期运行这种事模型一定要定期用新数据微调否则猪长大了、栏位换颜色了识别精度会悄悄掉下去。这套方案最大的价值不是一次训练出来就完事而是形成了一个能持续迭代的闭环我每次做完一段微调后都会把脚本和参数记录下来方便下次直接复用。养殖场行为检测这个方向数据和界面都只是手段真正能稳定用的是那个不断更新的best.pt希望帮到你。本文还有配套的精品资源点击获取