ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

YOLOv8老鼠检测实战:从数据集处理到PyQt界面部署全流程

2026/9/11 23:20:15 拓冰建站 浏览量
YOLOv8老鼠检测实战:从数据集处理到PyQt界面部署全流程 简介这套面向老鼠目标检测任务的YOLOv8权重与数据集整合包适合计算机视觉初学者、算法工程师或动物行为研究者快速搭建检测系统包内直接给出训练好的权重并附上5000多张老鼠图像数据免去自行采集与标注的耗时工序可服务于鼠患监测、实验室动物行为分析等真实场景。资源以压缩包形式交付共2000个文件其中1991个为XML标注文件配合4篇说明文档、3份环境配置教程和2个Python脚本含PyQt界面及资源文件整体约287.8MB。数据集目录已预先划分train、val、test并写好data.yamltxt与XML双格式标签可灵活适配YOLOv5至YOLOv9等主流检测算法无需额外整理即可直接训练。目前已有197人学习下载随包附带的PyQt检测界面和运行步骤可加载权重进行可视化推理有效降低环境配置与代码调试门槛对于希望二次开发或复现老鼠检测方案的研究者压缩包提供了从数据准备、模型训练到界面展示的完整基线。1. YOLOv8老鼠检测从数据集到PyQt界面的完整路径实验室里做动物行为分析粮仓或养殖场做鼠害监测或者只是给科研项目加一个自动计数模块——这些场景的落地形态往往是同一套组合YOLOv8负责从画面里找出老鼠一份质量足够好的数据集决定模型上限最后用一个带界面的程序把推理能力交到不熟悉命令行的人手里。标题里的“5000多张老鼠检测数据集”不是附件而是这套方案的根基PyQt界面也不是点缀它是把权重文件从训练机搬到现场机器的最后一公里。这篇文章会把整条链路拆开讲数据集处理、训练配置、PyQt界面封装、以及真正部署时才会遇到的细节。适合已经跑通YOLOv8基础流程、但还没有完整做过一个垂直场景特定目标、自建数据集、桌面端应用的开发者。看完之后你可以用一份自己的数据按同样的流程产出一个可交付的老鼠检测工具。2. 处理5000多张老鼠检测数据集的标准流程与坑位清单2.1 先明确数据集的三件事分辨率、标注质量、场景分布5000多张听起来不少但在目标检测任务里这个数量级刚好处于“够用但不宽裕”的位置。如果平均每张图有1到3只老鼠那么正样本实例数大约在8000到15000之间——这决定了你不需要迁移学习之外的复杂策略但也别指望模型能泛化到完全没见过的新场景。真正决定模型效果的往往不是图片数量而是这三件事分辨率是否足以让老鼠占据足够像素、标注框是否贴边、场景分布是否覆盖了你的实际部署环境。处理数据集的第一步是统一分辨率。摄像头采集的原始视频帧可能是1920x1080而训练时常用的输入尺寸是640x640。YOLOv8在训练时会做letterbox缩放但如果原图中老鼠占比很小比如全景鼠房监控缩放后目标可能只有十几个像素模型根本学不到有效特征。常见做法是先把原始图片按ROI裁剪让目标占比相对合理再统一到640x640。下面这段脚本可以快速查看数据集中目标尺寸的分布情况import os from collections import Counter from PIL import Image import numpy as np # 假设数据集结构images/存放图片labels/存放对应的txt标注YOLO格式 def analyze_target_sizes(img_dir, label_dir, img_size640): size_buckets Counter() for label_file in os.listdir(label_dir): if not label_file.endswith(.txt): continue img_file os.path.join(img_dir, label_file.replace(.txt, .jpg)) if not os.path.exists(img_file): continue img Image.open(img_file) img_w, img_h img.size # 实际使用时图片会被缩放到640等比计算目标缩放后的大小 scale img_size / max(img_w, img_h) with open(os.path.join(label_dir, label_file)) as f: for line in f: parts line.strip().split() # YOLO格式class_id, x_center, y_center, w, h归一化 w_norm float(parts[3]) h_norm float(parts[4]) # 反算原始像素尺寸并等比缩放 w_px_orig w_norm * img_w h_px_orig h_norm * img_h w_scaled w_px_orig * scale h_scaled h_px_orig * scale area w_scaled * h_scaled if area 16 * 16: size_buckets[tiny (16px)] 1 elif area 32 * 32: size_buckets[small (16-32px)] 1 else: size_buckets[normal (32px)] 1 print(size_buckets) analyze_target_sizes(datasets/rat/images, datasets/rat/labels)这段代码把每个标注框在训练尺度下的像素面积换算出来按小于16像素、16到32像素、大于32像素分桶。如果tiny和small占比超过30%训练时就要考虑开启YOLOv8的safer数据增强、调低anchor的尺度或者干脆增加针对小目标的拼接策略。否则模型会倾向于忽略小目标——因为损失函数里小框的误差占比天然低于大框。2.2 标注格式转换与清洗COCO转YOLO的脚本思路拿到一份数据集最常见的麻烦是格式不统一。公开的老鼠数据集通常是COCO格式一个大的JSON文件包含所有图片的标注而YOLOv8需要的是每张图片对应一个同名的txt文件每行一条标注格式为class_id x_center y_center width height其中坐标是归一化的。以下脚本把COCO格式转换成YOLO格式同时过滤掉无效标注宽高为0、坐标越界、目标面积过小import json import os from PIL import Image def coco_to_yolo(coco_json_path, img_root, label_output_dir): with open(coco_json_path, r) as f: coco json.load(f) # 建立图片id到文件名的映射 img_id_to_info {} for img in coco[images]: img_id_to_info[img[id]] { file_name: img[file_name], width: img[width], height: img[height] } # 只保留标签为“老鼠”的类别根据实际情况调整 cat_id_to_name {} for cat in coco[categories]: # 这里假设类别名为rat、mouse或类似命名 if rat in cat[name].lower() or mouse in cat[name].lower(): cat_id_to_name[cat[id]] 0 # 统一映射为0类 os.makedirs(label_output_dir, exist_okTrue) skipped 0 # 按图片维度聚合标注 anns_by_img {} for ann in coco[annotations]: img_id ann[image_id] if img_id not in anns_by_img: anns_by_img[img_id] [] anns_by_img[img_id].append(ann) for img_id, anns in anns_by_img.items(): if img_id not in img_id_to_info: continue img_info img_id_to_info[img_id] img_w img_info[width] img_h img_info[height] txt_path os.path.join(label_output_dir, os.path.splitext(img_info[file_name])[0] .txt) lines [] for ann in anns: if ann[category_id] not in cat_id_to_name: continue x, y, w, h ann[bbox] if w 0 or h 0: skipped 1 continue # COCO格式的bbox是[x, y, w, h]左上角坐标 x_center (x w / 2) / img_w y_center (y h / 2) / img_h w_norm w / img_w h_norm h / img_h # 过滤坐标越界 if not (0 x_center 1 and 0 y_center 1): skipped 1 continue lines.append(f0 {x_center:.6f} {y_center:.6f} {w_norm:.6f} {h_norm:.6f}) with open(txt_path, w) as f: f.write(\n.join(lines)) print(f转换完成跳过 {skipped} 条无效标注) coco_to_yolo(rat_dataset/annotations.json, rat_dataset/images, rat_dataset/labels)这段脚本里有几个值得注意的点。COCO的bbox定义是左上角坐标加宽高而YOLO需要的是中心点坐标加宽高换算时需要把x w / 2。类别映射部分用字符串匹配来识别老鼠类别这样即使原始数据集的类别命名不统一比如“rat”、“mouse”、“Rodent”也能兼容。最后的越界过滤非常重要——YOLO训练时如果标注框中心点落在图片外会导致损失函数计算出NaN训练直接崩掉。2.3 数据集划分训练/验证/测试的随机种子问题划分数据集时很多人直接shutil随机移动文件到三个目录但漏了设置随机种子导致每次划分结果不同复现实验时对不上。另外如果数据集是按视频帧序列截取的相邻帧高度相似随机划分会造成训练集和验证集里有几乎相同的内容验证分数虚高。这种情况应该按视频序列分组划分而不是按单帧划分。以下脚本按文件名前缀假设同一视频的帧文件名前缀相同分组再按组划分import os import random import shutil random.seed(42) # 固定种子保证可复现 def split_dataset_by_group(image_dir, label_dir, output_root, val_ratio0.15, test_ratio0.05): # 按前缀分组 groups {} for img_file in os.listdir(image_dir): if not img_file.endswith((.jpg, .png, .jpeg)): continue prefix img_file.split(_)[0] # 假设文件名形如 seq001_frame0123.jpg groups.setdefault(prefix, []).append(img_file) group_names list(groups.keys()) random.shuffle(group_names) n_val int(len(group_names) * val_ratio) n_test int(len(group_names) * test_ratio) val_groups set(group_names[:n_val]) test_groups set(group_names[n_val:n_val n_test]) for split in [train, val, test]: os.makedirs(os.path.join(output_root, split, images), exist_okTrue) os.makedirs(os.path.join(output_root, split, labels), exist_okTrue) for group, img_files in groups.items(): if group in val_groups: split val elif group in test_groups: split test else: split train for img_file in img_files: src_img os.path.join(image_dir, img_file) src_label os.path.join(label_dir, img_file.replace(.jpg, .txt)) dst_img os.path.join(output_root, split, images, img_file) dst_label os.path.join(output_root, split, labels, img_file.replace(.jpg, .txt)) shutil.copy2(src_img, dst_img) if os.path.exists(src_label): shutil.copy2(src_label, dst_label) else: # 没有标注的图片需要特殊处理YOLOv8允许空标注 open(dst_label, w).close()这里的关键点是把random.seed(42)放在最前面并且按组前缀打乱而不是按单张图片打乱。如果数据集不是视频帧序列只是独立图片直接改成对图片列表打乱即可。验证集占比15%到20%对5000张的数据集规模是合理的——太少则验证分数波动大太多则训练数据不够。3. 用YOLOv8训练老鼠检测模型从环境到权重调优3.1 环境配置与GPU取舍GTX 1660 Ti能不能跑训练YOLOv8之前先确定你的硬件条件。数据集只有5000多张、单类别目标检测这个任务量对GPU的要求并不高。常见的GTX 1660 Ti6GB显存完全够用使用YOLOv8s模型、批大小16、640x640输入显存占用大约4GB左右。最怕的不是显存不够而是有人为了用大batch把显存全占满导致训练中途OOM然后又不知道从哪一步开始恢复训练。环境配置的常见做法是用conda创建一个干净的Python环境conda create -n yolov8 python3.10 -y conda activate yolov8 pip install ultralytics torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install pyqt5 # 后续做界面时要用CUDA版本要和PyTorch对应。这里装的是cu118版本对应CUDA 11.8如果你的显卡驱动较新也可以装cu121或cu124版本。装完后用python -c import torch; print(torch.cuda.is_available())验证。如果返回False多半是驱动版本太旧或者PyTorch的CUDA版本不匹配而不是代码问题。3.2 模型选择n、s、m、l、x哪个适合老鼠检测YOLOv8有五个规模的预训练权重yolov8nnano、yolov8ssmall、yolov8mmedium、yolov8llarge、yolov8xxlarge。它们之间的差别是深度和宽度的缩放系数。训练自己的数据集时一般不建议一上来就训xlarge——训练速度慢、显存要求高而且模型容量过大时如果数据量不够反而容易过拟合。对于老鼠检测这个任务yolov8s是最稳妥的起点。原因有三单类别任务本身不复杂不需要过大的模型容量5000多张数据对s规模的模型来说刚好在“能收敛但不至于过拟合”的甜点区后续部署到PyQt桌面应用时s规模在CPU上的推理速度也能接受大约200到400毫秒一帧取决于硬件。模型mAP0.5COCO参考值参数量推理速度GPU, ms显存占用batch16, 640pxyolov8n0.3753.2M0.99约1.5GByolov8s0.44711.2M1.20约3.5GByolov8m0.50225.9M1.83约6.5GByolov8l0.53743.7M2.39约10GByolov8x0.55368.2M3.53约14GB表中的mAP是COCO数据集上的参考值不代表你的老鼠数据集上的表现但反映了模型规模的相对能力。可以看出yolov8s到yolov8m的参数量翻倍带来的收益在递减而训练成本在增加。先训s达到预期后再考虑要不要换m是成本最低的迭代路径。3.3 训练命令与关键参数epochs、batch、imgsz、patience准备好数据集后还需要一个data.yaml文件描述数据路径和类别信息# rat_dataset.yaml path: D:/projects/rat_dataset # 数据集根目录的绝对路径 train: train/images val: val/images test: test/images nc: 1 # 类别数量只有老鼠一类 names: [rat]然后运行训练命令yolo detect train \ modelyolov8s.pt \ datarat_dataset.yaml \ epochs100 \ imgsz640 \ batch16 \ patience15 \ workers4 \ projectrat_train \ nameexp1 \ seed42逐项说明参数的含义modelyolov8s.pt加载COCO预训练权重做迁移学习。不要直接写成yolov8s.yaml那样是从头训练收敛速度慢得多而且在小数据集上效果通常不如迁移学习。epochs100训练轮数。单类检测任务一般50到100轮足够配合早停patience生效后会自动提前终止。imgsz640输入分辨率。如果原始数据里目标很小可以试试768但训练时间会显著增加。640是精度和速度的平衡点。batch16受显存限制的batch大小。如果OOM优先降到8而不是降低imgsz。patience15连续15轮验证集mAP没有提升就停止训练。这是防止过拟合的重要机制——很多人的模型过拟合了还在硬训白耗时间。训练结束后runs/detect/exp1/weights/目录下会生成best.pt和last.pt。best.pt是验证集表现最好的权重部署时用它。3.4 损失曲线怎么看判断模型有没有真的学进去训练过程中YOLOv8会在runs/detect/exp1/下生成results.png包含box_loss、cls_loss、dfl_loss三条训练/验证曲线的变化。看懂这张图是判断训练是否正常的关键。训练开始时box_loss应该快速下降然后变缓验证集的loss如果先降后升就是过拟合的信号说明patience没有在合适的时机触发。一个更快的方法是直接看验证集的mAP0.5曲线它应该稳步上升然后趋于平坦。如果mAP0.5始终在0.5以下大概率是数据标注问题框不贴边、漏标严重模型能力本身没问题。如果你想自定义损失曲线图比如只看mAP0.5和mAP0.5:0.95的对比可以直接读取训练保存的results.csv并重新绘图import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(rat_train/exp1/results.csv) # 去掉前两列epoch和时间戳用epoch做横轴 epochs df[ epoch] plt.figure(figsize(10, 6)) plt.plot(epochs, df[ metrics/mAP50(B)], labelmAP0.5) plt.plot(epochs, df[ metrics/mAP50-95(B)], labelmAP0.5:0.95) plt.xlabel(Epoch) plt.ylabel(mAP) plt.legend() plt.grid(True) plt.savefig(rat_mAP_curves.png, dpi150)注意results.csv的列名前面可能有空格读取时需要用实际列名或者用df.columns确认。mAP0.5是IoU阈值0.5时的平均精度mAP0.5:0.95是IoU从0.5到0.95取平均的结果后者更严格也更能反映模型定位精度。老鼠检测这种业务mAP0.5达到0.85以上就算可用而mAP0.5:0.95在0.6左右就不错了。3.5 C2f结构对训练效果的影响为什么YOLOv8比v5更容易收敛YOLOv8的Backbone和Neck中大量使用了C2f模块它替代了YOLOv5中C3模块的位置由torch.nn.Conv2d的变体组成。C2f的核心差异是把输入特征图经过两个分支处理后Concat起来再通过Bottleneck残差块最后再Concat一次。本质上是增加了梯度回传路径让浅层特征更容易收到来自深层loss的梯度信号。这意味着在训练自己的数据集时YOLOv8比YOLOv5更容易收敛、对学习率不那么敏感。实际操作中你不太需要去改C2f的内部结构——默认学习率0.01和默认的优化器SGD在大多数自定义数据集上都能正常收敛。唯一值得尝试的改动是把optimizer参数从auto换成AdamW对某些收敛困难的数据集有奇效但有概率在最后精度上略低于SGD。先用默认的SGD训一轮如果损失曲线抖动剧烈发散再换AdamW。4. 写一个能吃权重的PyQt检测界面从布局到线程模型4.1 界面层次预览区、控制区、信息区的分工一个可交付的老鼠检测工具界面至少需要三个区域视频预览区显示推理结果、控制区打开文件、启动摄像头、模型切换、置信度阈值调节、信息区当前帧率、检测数量、每只鼠的置信度。下面是一个五层起始结构按控件嵌套关系自上而下展开MainWindow (QMainWindow) ├── central_widget (QWidget) │ ├── layout_main (QVBoxLayout) │ │ ├── video_label (QLabel) # 预览区 │ │ ├── control_panel (QWidget) │ │ │ └── layout_control (QHBoxLayout) │ │ │ ├── btn_open_file (QPushButton) │ │ │ ├── btn_open_camera (QPushButton) │ │ │ ├── combo_model (QComboBox) │ │ │ ├── spin_conf (QDoubleSpinBox) │ │ │ └── btn_start_stop (QPushButton) │ │ └── info_label (QLabel) # 信息区预览区用QLabel显示QImage即可不要直接在里面绘制这样代码更清晰。控制区用QComboBox来列出models/目录下的所有.pt权重文件这样调用可以换成其他权重时不需要改代码。置信度阈值用QDoubleSpinBox范围设0.01到0.99步长0.05默认0.25。4.2 推理线程与界面分离为什么不能直接在UI线程里跑YOLOv8这是写PyQt检测工具时最重要的一个设计决策。如果在UI线程里同步执行model.predict()视频推理时每一帧的检测耗时即使GPU上也要10到30毫秒会让界面卡死鼠标拖动窗口都会掉帧。正确做法是把推理放到一个后台线程通过信号槽机制把结果传回主线程刷新UI。以下是一个完整的线程封装示例import threading from PyQt5.QtCore import QThread, pyqtSignal import numpy as np from ultralytics import YOLO class DetectionThread(QThread): # 定义信号帧数据、检测结果、FPS frame_ready pyqtSignal(np.ndarray, list, float) error pyqtSignal(str) def __init__(self, model_path, conf_thres0.25): super().__init__() self.model_path model_path self.conf conf_thres self.model None self.running True self.source 0 # 0表示摄像头也可以是视频文件路径 def run(self): try: self.model YOLO(self.model_path) cap cv2.VideoCapture(self.source) if not cap.isOpened(): self.error.emit(无法打开视频源) return prev_time time.time() while self.running: ret, frame cap.read() if not ret: break results self.model(frame, confself.conf, verboseFalse) # 绘制结果 annotated results[0].plot() # YOLOv8内置可视化方法 curr_time time.time() fps 1.0 / (curr_time - prev_time) prev_time curr_time self.frame_ready.emit(annotated, results[0].boxes.data.tolist(), fps) cap.release() except Exception as e: self.error.emit(str(e)) def stop(self): self.running False self.wait()代码逻辑说明DetectionThread继承QThread在run方法中加载YOLO模型、打开视频源、循环读取帧并推理。results[0].plot()是YOLOv8自带的绘图方法返回画好检测框的numpy数组BGR格式。frame_ready信号携带三个参数绘制后的画面、检测框原始数据每个框是[x1, y1, x2, y2, confidence, class_id]、当前FPS。主线程连接这个信号并更新界面。需要注意的关键点是self.running标志位。关闭窗口时一定记得调用stop()方法否则线程会一直占用摄像头资源。4.3 摄像头选型与多路输入单目、USB摄像头和RTSP流的统一实验室场景下USB摄像头是最常见的输入源。OpenCV中cv2.VideoCapture(0)默认打开第一个USB摄像头如果同时插了多个可能需要指定设备索引0、1、2。工业场景常用RTSP流网络摄像头这时候只需要把self.source改成rtsp://user:passip:554/stream即可其余代码完全不用改。YOLOv8的模型推理逻辑不关心输入来自哪里它只接收numpy数组。如果要做多路摄像头同时检测一个DetectionThread实例负责一个视频源然后多个线程实例共享同一个模型实例——YOLOv8的模型在推理时是线程安全的多个线程同时调用predict不会崩溃只是GPU利用率会提高。界面端需要写一个简单的线程池管理器class MultiCameraManager: def __init__(self, model_path, source_list): self.threads [] for src in source_list: thread DetectionThread(model_path) thread.source src self.threads.append(thread) def start_all(self): for t in self.threads: t.start() def stop_all(self): for t in self.threads: t.stop()多路输入时的瓶颈通常在解码而不是推理。4路1080P的RTSP流同时解码CPU占用会非常高此时优先考虑把cv2.VideoCapture的CAP_PROP_BUFFERSIZE设为1减少解码缓存延迟。4.4 模型更换与置信度调节不重启程序的运行参数部署阶段的权重更换需求比想象中频繁——算法优化后产生了新权重、现场场景光照变化需要调参、甚至要把检测模型从一个换成另一个。为此界面需要支持运行时切换模型。实现方式是在QComboBox的currentIndexChanged信号里重新加载模型def on_model_changed(self, index): model_name self.combo_model.currentText() model_path os.path.join(models, model_name) # 重建推理线程或在线程里加一个replace_model方法 self.det_thread.stop() self.det_thread DetectionThread(model_path, self.spin_conf.value()) self.det_thread.frame_ready.connect(self.update_frame) self.det_thread.start()简单的实现是停止旧线程、创建新线程并启动。这里注意要先disconnect旧信号连接或者直接重新实例化Det虫线程。另一种更优雅的做法是在DetectionThread里加一个load_model方法并用锁保护不过对于这种使用频度不高的操作重建线程更简单直观。置信度阈值调节同理。在valueChanged信号回调里把新阈值通过一个线程安全的属性写入推理线程。因为YOLOv8的predict每次调用时都会从self.conf读取不需要锁也可以工作只是极端情况下可能出现某一帧用了旧值、下一帧用了新值——对视觉检测来说这种差别无感。5. 部署验证与常见事故排查从ONNX导出到漏检清单5.1 把best.pt导出成ONNX推理加速的第一步PyQt界面直接加载.pt模型是可行的但桌面端部署时.pt有两个不友好的地方依赖PyTorch环境一个虚拟环境就几个GB、CPU推理速度不理想。常见做法是先把best.pt导出成ONNX格式然后用onnxruntime做推理速度和体积都有提升。导出命令很简单yolo export modelbest.pt formatonnx opset12 imgsz640然后用onnxruntime替代YOLO类做推理import onnxruntime as ort import cv2 import numpy as np class ONNXDetector: def __init__(self, onnx_path): self.session ort.InferenceSession(onnx_path, providers[CPUExecutionProvider]) self.input_name self.session.get_inputs()[0].name self.input_shape self.session.get_inputs()[0].shape # [1, 3, 640, 640] def preprocess(self, frame): img cv2.resize(frame, (640, 640)) img img[:, :, ::-1].transpose(2, 0, 1) # BGR - RGB, HWC - CHW img np.ascontiguousarray(img, dtypenp.float32) img / 255.0 # 归一化到0~1 img np.expand_dims(img, axis0) # 加batch维度 return img def predict(self, frame): input_tensor self.preprocess(frame) outputs self.session.run(None, {self.input_name: input_tensor}) # outputs[0]的shape是[1, 84, 8400]需要解析 # YOLOv8输出格式4个坐标 80个类别得分COCO但自定义训练时是 4 nc return outputs[0]推理前需要验证导出的ONNX权重和PyTorch权重输出是否一致。方法是在同一张图上分别用model.predict()和onnx推理对比前20个高置信度检测框的坐标差。坐标偏差在1%以内都可以认为是正常的因为ONNX有算子融合和精度优化。5.2 漏检和误检的排查清单先看数据不要急着调参数部署后反馈最多的问题就是漏检。第一反应不要是调低置信度阈值而是按以下顺序排查检查目标在画面中的尺寸。950px的宽屏画面里老鼠只占了30像素置信度0.25过滤后就什么都没了。此时应该做的是裁切画面放大而不是改阈值。检查目标与训练数据的分布差异。模型是在俯视视角数据上训练的部署时是平视视角特征差异太大导致模型完全不认识。这是数据问题调参数解决不了只能补采集数据。检查光照和噪声。红外夜视采集的老鼠数据如果训练集里没有模型大概率失效。可以先用YOLOv8内置的augmentTrue增强推理试试如果效果改善明显说明模型对光照敏感。误检把其他东西识别成老鼠的情况则要检查训练集里负样本是否足够。如果不希望把地上的阴影或电线杆识别成老鼠最好在数据准备阶段加入纯负样本图片里没有老鼠的帧并在训练时把它们标注为空无标注的txt文件让模型学到“没有老鼠”这一模式。5.3 综合调优技巧为你的模型添加自定义后处理一些实际可操作的技巧。比如目标重叠严重时需要非极大值抑制的IoU阈值调低YOLOv8默认NMS IoU是0.7当多只老鼠挤在一起时较低的IoU0.5能保留更多相近的检测框。置信度阈值和IoU阈值是两个独立旋钮前者过滤低分框、后者合并重叠框不要混为一谈。对于“动态画面中会连续漏检”的场景比如老鼠快速跑过摄像头视野代价最小的优化是提升推理的帧率而不是逐帧做temporal smoothing——当FPS从15提升到30的时候连续两帧之间目标的位移减半漏检概率显著下降。而这一项优化只需要用ONNX替代PyTorch推理就能实现。最后用性能基线验证卡不卡顿——最简单的方式就是同时观察界面FPS和GPU占用率。FPS掉到10以下时优先检查是不是视频解码和缩放占了太多CPU而不是模型推理GPU占用率只有20%时瓶颈在解码而不是推理。本文还有配套的精品资源点击获取