ARTICLE DETAIL

资讯详情

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

打架检测数据集实战:格式转换与YOLO11三平台训练避坑指南

打架检测数据集实战:格式转换与YOLO11三平台训练避坑指南 简介一份围绕打架检测任务的数据集配套说明文档PDF面向目标检测算法学习者与安防监控项目开发者。主体数据集包含3000张真实监控场景打架图片覆盖街道、酒吧、商店、公交车、监狱、空旷地等多样场景既有两人互殴也有多人群殴样本经LabelImg规范标注统一为fight类别并已转换成VOC、COCO、YOLO三种主流格式可直接用于YOLO等模型训练。文档同时提供YOLO11一键训练脚本支持GPUGPUs、CPU、MacM芯片多平台运行附博主训练结果日志作参考。由于数据集较大PDF内含百度网盘获取方式及基本情况介绍文件总数1个大小5.63MB。已有853人学习查看适合需要标准化打架检测数据或快速开展监控场景检测工作的开发者。1. 打架检测数据集3000 张图、三套标签一次把训练闭环走通打架检测数据集3000 张图配套 VOC、COCO、YOLO 三种格式标签再加一份能跑 GPU、CPU、Mac 三平台的 YOLO11 一键训练脚本——这套组合真正解决的问题是让「打架检测」这个细分方向不再卡在数据准备和环境配置上。做智能安防的人都知道模型结构反而不是门槛标注边界怎么定、格式怎么对齐、训练环境怎么搭才是消耗时间的大头。这篇笔记从数据集构成开始把格式转换脚本、YOLO11 三平台训练脚本和常见翻车点完整拆开。适合正在做校园、园区、监所等场景的算法工程师也适合手里有视频素材、想自建打架检测数据集但对训练流程还不够熟的人。2. 打架检测数据集怎么才算合格场景构成、类别边界与校验指标打架检测和普通目标检测有一个本质区别检测目标是人模型要学的却是「人的动作交互」。同一个画面里两人靠近可能是聊天也可能是推搡的前奏抱在一起可能是搀扶也可能是互殴的收尾。普通检测模型把「人」框出来就能交差打架检测却要求模型从人框之间的位置关系、肢体重叠程度和姿态变化里读出攻击性。这也是 3000 张图听起来不多、却足够支撑起步的原因——每一张图的信息密度比单人检测数据集高得多但也意味着数据质量直接决定模型上限。2.1 场景构成三成正常、六成交互、一成负样本先给一个我常用的经验比例训练集里大约 60% 是双人或多人肢体接触画面30% 是单人正常行走、多人站立交谈这类无攻击性画面剩下 10% 是场景负样本比如空教室、走廊、楼梯口。负样本不是不能要但占比太高会让模型变得保守到了现场什么都不敢报。3000 张图的体量下负样本控制在 300 张以内是个安全区间。场景多样性比数量更重要。如果 3000 张图全来自同一个摄像头角度模型在换一个机位后精度会明显下降。我见过最典型的翻车数据是训练集里全是监控俯视角度拿到平视枪机上一测mAP50 直接从 0.86 掉到 0.4。素材来源单一的话数据增强里加随机旋转和透视变换几乎是必须的但增强只能缓解不能替代多视角采集。如果原始素材是视频抽帧策略直接决定样本有效性。打架动作通常只有几秒建议从事件开始前 2 秒抽到事件结束后 1 秒把「准备动作」和「收尾动作」也留进训练集这样模型学到的不是单帧的静态姿势而是连续交互中常见的姿态变化。抽帧间隔建议每 3 到 5 帧取一帧连续帧高度相似全部保留只会让训练集看起来很大实际有效信息没增加。2.2 标注规范类别边界定死边框只标攻击主体打架检测数据集的类别建议控制在两类fight打架和 normal正常交互。要不要加第三类「围观群众」我的建议是前期不加。类别越细分摊到每个类别的有效样本就越少3000 张图经不起这么分。先把二分类做稳定模型能准确区分「打」和「没打」之后再考虑拆细分动作比如推搡、拳击、踢踹那是后面的事。标注边界是数据质量的核心。边框标注两个原则必须盯紧第一边框只覆盖发起攻击行为的主要身体区域通常是上半身加手臂不要把整条腿、地面阴影和旁边无关路人扩进去第二打架场景里人和人的框高度重叠是正常现象不要因为重叠就删框。标注时优先保「攻击动作明显」的帧动作模糊但能看出意图的保留完全看不清的帧直接丢掉留着只会给训练加噪声。还有一个容易被忽略的点避免把「正常但亲密的接触」标成 fight。两人拥抱、搀扶、拍肩膀这类动作在语义上和打架完全相反一旦标错模型会在这些动作上产生高置信度的误报。标注完成后建议让第二个人独立抽检重点就是看这些边界帧有没有标反。数据集的类别人为设定但边界一致性靠流程保证。2.3 训练前先校验的 6 项硬性指标拿到数据集后不要直接开训先跑一遍指标检查。下面这张表是我每次开工前都会过一遍的清单阈值可以根据场景微调但检查项不要省。指标合格线检查方式标签文件完整率100%每张图必须有对应标签一个都不能少空标签比例小于 5%空 txt 文件数量占总标签数比例平均目标数/图1.5 以上多数图片只有 1 个框时模型学不好交互小目标占比小于 30%边框面积小于图面积 1% 的数量占比类别分布不失衡fight 与 normal 比例尽量不要超过 7:3坐标越界率0归一化坐标不得小于 0 或大于 1这些指标不需要复杂工具一个脚本统计标签文件行数和坐标范围就能查完。第 3 章会给出可复用的校验脚本。数据集质量不过关就开始调参后面做的所有工作都是在给脏数据买单省不掉也绕不开。3. VOC/COCO/YOLO 三格式互转转换脚本与四个边界坑标签格式这件事看起来是纯体力活实际是打架检测数据集落地时最容易出问题的一环。VOC、COCO、YOLO 三种格式各有各的坐标定义和索引规则数据集发布时虽然给了三套标签但用的时候基本都得按自己的训练框架重新转一遍。转换本身不难麻烦的是那些不写进文档的细节索引从 0 开始还是从 1 开始坐标是角点还是中心点图片扩展名是 jpg 还是 png。这四个坑踩一次就长记性。3.1 三种格式的组织方式差异先把三种格式的核心差异摆出来后面脚本全围绕这张表写。格式存储单位坐标定义类别索引VOC每张图一个 xml 文件左上角 (xmin, ymin) 和右下角 (xmax, ymax)类名文本COCO整个数据集一个 json左上角 (x, y) 加宽高 (w, h)从 1 开始YOLO每张图一个 txt 文件中心点 (xc, yc) 加宽高 (w, h)归一化到 0~1从 0 开始三个关键差异点角点坐标转中心坐标时要除以图像宽高这是最容易算错的地方COCO 类别索引从 1 开始YOLO 从 0 开始直接搬会整体偏移一位图片扩展名不能写死很多数据集的图片是 png 而不是 jpg转换脚本里硬编码扩展名会漏掉一大部分文件。3.2 VOC 转 YOLO 格式转换脚本与类别映射转换前先确认一件事data.yaml 里的 names 顺序就是 YOLO 标签里 class id 的对应顺序。下面这个脚本把 VOC 的 xml 解析成 YOLO 的 txt转换的同时做了坐标裁剪防止标注框越界在训练时造成 loss 变成 nan。import os import xml.etree.ElementTree as ET # 类别顺序必须和 data.yaml 里的 names 完全一致 CLASS_MAPPING {fight: 0, normal: 1} def voc_to_yolo(xml_path, out_txt_path): tree ET.parse(xml_path) root tree.getroot() size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) lines [] for obj in root.findall(object): name obj.find(name).text if name not in CLASS_MAPPING: continue # VOC 存的是左上角和右下角坐标 bndbox obj.find(bndbox) x1 float(bndbox.find(xmin).text) y1 float(bndbox.find(ymin).text) x2 float(bndbox.find(xmax).text) y2 float(bndbox.find(ymax).text) # 归一化时用原始图像尺寸不是缩放后的尺寸 xc (x1 x2) / 2.0 / img_w yc (y1 y2) / 2.0 / img_h bw (x2 - x1) / img_w bh (y2 - y1) / img_h # 对越界坐标做裁剪防止训练时出现 nan xc min(max(xc, 0.0), 1.0) yc min(max(yc, 0.0), 1.0) bw min(max(bw, 0.0), 1.0) bh min(max(bh, 0.0), 1.0) lines.append(f{CLASS_MAPPING[name]} {xc:.6f} {yc:.6f} {bw:.6f} {bh:.6f}) with open(out_txt_path, w) as f: f.write(\n.join(lines))逻辑说明脚本先把 VOC 的角点坐标换算成中心点坐标再用图像宽高做归一化。坐标裁剪那四行是防呆设计标注框即使超出图像边界也保证不会输出负值。一个 xml 对应一个 txttxt 文件名必须与图片名保持一致YOLO 训练目录里 images 和 labels 分开存放对不上就训练不到该图。参数说明CLASS_MAPPING 是整个脚本里最重要的参数里面的数字直接对应后续 data.yaml 里的 names 顺序。如果数据集只有 fight 一类就把 mapping 改成 {fight: 0}并把 names 也改成单类两边必须同步。3.3 YOLO 转 COCO 格式坐标恢复与 JSON 组装COCO 格式要求整个数据集打包成一个 json里面同时包含 images 和 annotations 两个数组。转换逻辑是 YOLO 的逆运算归一化坐标乘回图像宽高再从中心点坐标还原成左上角坐标同时 category_id 要加 1。import json import os from glob import glob from PIL import Image def yolo_to_coco(label_dir, image_dir, output_json, class_names): images, annotations [], [] ann_id 0 # 一个 txt 对应一张图按文件名排序保证顺序稳定 for i, txt_path in enumerate(sorted(glob(os.path.join(label_dir, *.txt)))): # 这里假设图片是 jpg如果数据集是 png需要改成 .png base_name os.path.splitext(os.path.basename(txt_path))[0] img_path os.path.join(image_dir, base_name .jpg) if not os.path.exists(img_path): img_path os.path.join(image_dir, base_name .png) w, h Image.open(img_path).size images.append({ id: i, file_name: os.path.basename(img_path), width: w, height: h, }) with open(txt_path) as f: for line in f: if not line.strip(): continue parts line.strip().split() cls_id int(parts[0]) # YOLO 的 xc, yc 是中心点归一化坐标 xc, yc, bw_, bh_ map(float, parts[1:]) x (xc - bw_ / 2) * w y (yc - bh_ / 2) * h bw bw_ * w bh bh_ * h annotations.append({ id: ann_id, image_id: i, category_id: cls_id 1, # COCO 类别从 1 开始 bbox: [x, y, bw, bh], area: bw * bh, iscrowd: 0, }) ann_id 1 with open(output_json, w) as f: json.dump({ images: images, annotations: annotations, categories: [ {id: idx 1, name: name} for idx, name in enumerate(class_names) ], }, f, ensure_asciiFalse)逻辑说明脚本遍历 label 目录下所有 txt对每个 txt 先读对应图片的宽高再把归一化的中心点坐标还原成角点坐标。image_id 从 0 开始连续编号ann_id 全局递增这两个 id 在 COCO 评估时是关联依据漏填会导致评估脚本报 KeyError。参数说明category_id 加 1 这个操作不是可选项COCO 的类别 id 没有 0从 0 开始会与背景类冲突。脚本里对图片扩展名做了 jpg/png 双探测实际使用中如果数据集混用两种扩展名建议先在 data.yaml 的 path 目录下确认图片实际格式再决定要不要扩展探测逻辑。3.4 转换后的三个必查项转换脚本跑完不等于转换正确我习惯再做一轮校验。三个必查项标签文件和图片文件是否一一对应坐标是否都在 0 到 1 范围内类别索引是否在 data.yaml 的 names 长度范围内。下面这个脚本专门查这三件事。import os from collections import Counter def verify_yolo_labels(label_dir, num_classes): bad_files, empty_files [], [] cls_count Counter() for txt in os.listdir(label_dir): if not txt.endswith(.txt): continue path os.path.join(label_dir, txt) lines [l.strip() for l in open(path) if l.strip()] if not lines: empty_files.append(txt) # 空标签文件 continue for line in lines: parts line.split() if len(parts) ! 5: bad_files.append(txt) continue cls_id int(parts[0]) if cls_id 0 or cls_id num_classes: bad_files.append(txt) coords [float(p) for p in parts[1:]] if any(c 0 or c 1 for c in coords): bad_files.append(txt) cls_count[cls_id] 1 print(fempty_files{len(empty_files)} bad_files{len(bad_files)}) print(class distribution:, dict(cls_count)) verify_yolo_labels(./fight_dataset/labels/train, num_classes2)逻辑说明脚本逐行检查每个 txt 的格式、类别索引和坐标范围空标签文件和坏文件分别统计。类别分布打印出来后可以直观看到 fight 和 normal 的比例是否失衡。空标签文件占比超过 5% 时建议检查数据集构成负样本太多会导致模型漏检。参数说明num_classes 要传 data.yaml 里的 nc 值传小了会把合法标签误报成越界。这个脚本是训练前最后一关跑完没问题再进第 4 章的一键训练脚本。4. 用 YOLO11 一键训练脚本打通 GPU/CPU/Mac 三平台训练脚本这个环节最大的痛点不是模型代码而是环境差异NVIDIA GPU 走 CUDAMac 走 MPS纯 CPU 机器什么加速都没有三套环境装出来各自能跑换台机器就可能报错。一键训练脚本的作用就是把设备判断、参数设置、断点续训这些事情全部包住让数据集拿来就能训。4.1 一键训练脚本的整体设计外层 Shell 控制开关内层 Python 选设备脚本分两层设计Shell 脚本处理命令行参数和训练开关Python 脚本负责设备识别和实际训练逻辑。这样拆分的好处是日常使用只需要改 Shell 顶部的几个变量不用动训练代码。#!/bin/bash # 用法: ./train_fight_3000.sh # 快速测试: ./train_fight_3000.sh test set -e DATASET_DIR./fight_dataset EPOCHS100 BATCH16 IMGSZ640 TEST_MODE${1:-off} if [ $TEST_MODE test ]; then EPOCHS3 echo 快速测试模式: epochs3, 验证数据路径和训练流程 fi python train_yolo11.py \ --data $DATASET_DIR/fight.yaml \ --epochs $EPOCHS \ --batch $BATCH \ --imgsz $IMGSZ逻辑说明set -e 让脚本在 Python 报错时立即退出避免错误堆叠后难以定位。test 模式用 3 个 epoch 快速验证数据路径、标签文件和训练流程是否通畅确认没问题再跑完整训练。这个习惯能省下大量反复试错的时间第一次跑数据集时强烈建议先用 test 模式。参数说明DATASET_DIR 指向数据集根目录脚本内部会把路径传给 Python 的 --data 参数。BATCH 默认 16Mac 和 CPU 上要调小具体见 4.3 的表格说明。IMGSZ 默认 640YOLO11 网络结构对输入尺寸不敏感640 是最稳妥的默认值显存紧张时可以改 512。4.2 fight.yaml 的数据组织与路径写法YOLO11 训练时通过一个 yaml 文件描述数据集路径、类别数量和类别名。目录结构按 images 和 labels 分开组织train、val、test 各一份。# fight.yaml path: /data/fight_dataset # 数据集绝对路径Windows 用户写 D:/fight_dataset train: images/train val: images/val test: images/test nc: 2 names: 0: fight 1: normal目录结构对应如下fight_dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── fight.yaml逻辑说明ultralytics 在读取这个配置时会用 path 拼上 train 和 val 的相对路径找到图片后自动去同级目录的 labels 下找对应标注。图片和标签的父目录名必须一个是 images 一个是 labels否则训练时标注缺失。path 建议写绝对路径相对路径在切换工作目录时容易踩坑。参数说明如果数据集只有 train 和 val没有 test就把 test 一行注释掉训练不会受影响。Windows 下 path 要写成 D:/fight_dataset 这种正斜杠格式反斜杠在 yaml 解析时会被当成转义字符处理。4.3 关键训练参数与平台差异训练一般用的是 Ultralytics 的 YOLO11 接口环境配置直接 pip install ultralytics卡在环境配置上的人大多卡在 Python 和 PyTorch 版本不匹配。建议用 Python 3.10 或更新的版本PyTorch 装 2.2 以上Mac 用户装 PyTorch 时会自带 MPS 支持不需要额外配置。import argparse import torch def resolve_device(): # NVIDIA GPU 优先其次是 Apple MPS最后退回 CPU if torch.cuda.is_available(): return 0 # MPS 后端在部分 PyTorch 版本上算子支持不全 if hasattr(torch.backends, mps) and torch.backends.mps.is_available(): try: t torch.ones(1) _ t.to(mps) return mps:0 except Exception: pass return cpu def main(): parser argparse.ArgumentParser() parser.add_argument(--data, requiredTrue) parser.add_argument(--epochs, typeint, default100) parser.add_argument(--batch, typeint, default16) parser.add_argument(--imgsz, typeint, default640) args parser.parse_args() from ultralytics import YOLO model YOLO(yolo11n.pt) # 第一次运行会自动下载预训练权重 device resolve_device() print(f正在使用设备: {device}) # 训练中断后恢复现场把下一行取消注释并改成 last.pt 路径 # model YOLO(runs/detect/fight_train/weights/last.pt) model.train( dataargs.data, epochsargs.epochs, batchargs.batch, imgszargs.imgsz, devicedevice, patience20, workers4, projectruns/detect, namefight_train, exist_okTrue, ) if __name__ __main__: main()逻辑说明resolve_device 是脚本里最核心的一段逻辑它先探测 CUDA再探测 MPS最后兜底 CPU。MPS 探测外面包了一层 try/except因为有些 PyTorch 版本在 MPS 上运行时不是启动就报错而是跑到某一个算子才崩溃提前做一次张量设备迁移可以过滤掉这类环境问题。device 返回值里 0 表示第一张 NVIDIA 显卡这是 ultralytics 能直接识别的格式。参数说明patience20 表示验证集指标连续 20 个 epoch 没有提升就提前停止训练能省时间workers4 是数据加载线程数CPU 较弱时改 2 更稳exist_okTrue 允许重复使用 fight_train 这个目录避免每次启动脚本都新建一个目录。关键参数对整个训练效果的影响如下参数GPU 推荐值Mac/CPU 推荐值说明epochs10060小数据集 100 轮已够收敛Mac 上 100 轮耗时太长batch168 或 4显存不足时先降 batch不要直接缩 imgszimgsz640512降低分辨率能明显加快 CPU 训练device0mps:0 / cpuMac 上 MPS 报错就手动改 cpucacheTrueFalseGPU 显存足够时开 cache 预加载图片cacheTrue 这个参数很多新手会忽略。显存够的情况下它会把图片提前缓存进内存训练时不再频繁读硬盘3000 张图的数据集能让每个 epoch 缩短不少时间。Mac 和 CPU 上不建议开内存占用会暴涨。4.4 断点续训与快速验证训练中断不用从头再来训练中断是家常便饭电源断开、内存溢出、系统休眠都可能让训练中断。YOLO11 每次训练都会在输出目录下保存 last.pt恢复训练时只需要把模型加载路径指到它并加上 resume 参数。# 从断点恢复训练 python train_yolo11.py \ --data ./fight_dataset/fight.yaml \ --epochs 100 \ --batch 16 \ --imgsz 640恢复时把 train_yolo11.py 中的 model YOLO(runs/detect/fight_train/weights/last.pt) 这行注释打开训练脚本会自动读取 last.pt 中已经完成的 epoch 数从断点继续。如果脚本本身崩溃了直接用命令行恢复也行yolo detect train resume modelruns/detect/fight_train/weights/last.pt逻辑说明resume 的原理是把 last.pt 里保存的训练状态恢复包括优化器状态、当前 epoch、数据增强参数。这就是为什么断了之后不要手动改训练参数优化器状态会被重置相当于从零开始。训练日志里看到 epochs 显示的是已完成的轮数不是剩余轮数别误解成训练计数出错了。快速验证模式下用 3 个 epoch 跑完整个流程后去 runs/detect/fight_train/weights/ 目录看有没有生成 last.pt 和 best.pt。两个文件都有说明数据读取、标签解析、训练循环全部通畅可以放心跑完整训练了。5. 打架检测训练避坑5 个最常见的翻车现场5.1 训练两三轮 loss 直接变成 nan现象训练日志里 box_loss 和 cls_loss 在第 2、3 个 epoch 后变成 nan之后指标全部异常。原因标签文件里存在宽高为 0 的边框或者归一化坐标出现了负数优化器在反向传播时把梯度炸掉了。这种情况多出现在 VOC 转 YOLO 时原始 xml 里存在空 bndbox 标签。解决训练前用 3.4 节的 verify_yolo_labels 脚本扫描全部标签重点看 bad_files 列表。扫描结果为空就再检查数据增强配置关闭 mixup 后重试个别情况下 mixup 生成的合成框会超出边界。5.2 训练正常但验证 mAP 全是 0现象loss 正常下降训练能跑完但 val 阶段每个类别的 mAP50 和 mAP50-95 都是 0。原因标签文件的类别索引和 data.yaml 里的 names 顺序没对齐。比如数据集的 fight 在标签里是 0但 data.yaml 里 names 写成了 {0: normal, 1: fight}模型学到的类别分布全错位了。解决重新确认 CLASS_MAPPING 和 data.yaml 的顺序一致然后打印一个类别对照表人工核对。转换脚本跑完后用 validate 脚本随机抽 5 张图把标签坐标画回到图片上肉眼看类别名字和框是否匹配。这一步看着土却是查索引错位最有效的手段。5.3 Mac 上 MPS 训练中途报算子不支持现象训练在某个 epoch 突然报 NotImplementedError 或 one of the arguments is requiredMac 上跑 MPS 模式时尤其常见。原因PyTorch 的 MPS 后端覆盖度不是 100%某些卷积算子在特定输入形状下没有实现触发崩溃的位置不固定有时在第一个 epoch有时在第 20 个 epoch。解决在 resolve_device 函数里做能力探测先跑一次张量迁移看 MPS 是否真的可用。如果训练中途崩溃就把 device 强制改成 cpuMac 上用 CPU 训练 3000 张图虽然慢但至少能出一个可用的模型。另一个缓解办法是降低 batch 和 imgsz某些算子在特定形状下才会触发崩溃改变形状能绕过去。5.4 Windows 下路径带中文或空格训练到一半报文件不存在现象训练启动时一切正常跑到某个 epoch 突然 FileNotFoundError报错指向的图片路径里中文或空格被错误编码。原因ultralytics 在缓存图片路径时会把相对路径拼接成字符串Windows 下中文字符和空格在部分 Python 环境下编码不一致导致路径拼接后找不到文件。解决数据集的根目录、项目路径、训练输出路径全部使用纯英文、无空格的命名。Windows 用户最稳妥的做法是直接把数据集放在 D:/fight_dataset 这种盘符根目录不要在路径里包含用户名因为 Windows 用户名经常是中文。这个问题的排查成本远高于规避成本建议从项目一开始就约定好命名规范。5.5 显存明明够用训练却总是中途崩溃现象nvidia-smi 查看显存还剩不少但训练跑一发多小时就崩报 CUDA out of memory。原因显存碎片化。训练过程中数据加载、缓存、BatchNorm 统计都会临时占用显存峰值出现在 epoch 切换的时候单纯看空闲显存判断够不够不准确。另一个常见原因是开了 cacheTrue 后图片全部缓存进内存数据加载线程占用的内存随着 epoch 增加而累积。解决把 batch 降一半先确认是不是显存问题如果降 batch 后稳定了说明是峰值超限。想保持大 batch 的话把 cache 从 True 改成 False减少内存占用过大导致的进程被杀。Mac 和 CPU 上内存占用更容易爆建议在代码里加一行 torch.cuda.empty_cache() 在每轮 epoch 结束后调用GPU 上能明显减少碎片。6. 从训练到现场用 mAP 验证效果并导出部署模型训练完只是拿到一个 best.pt它能不能用得先过验证这关再把模型导出成部署格式。验证不是看训练日志里的 loss要看独立验证集上的 mAP50 和 mAP50-95这两个指标才是模型真实能力的标尺。打架检测这个场景有个特点它对召回比精度更敏感。漏一次报警可能造成严重后果误报一次顶多让安保人员多看一眼。所以验证时关注的核心指标是 mAP50 和 recall 而不是 precision。用下面的命令对 best.pt 做验证yolo detect val \ modelruns/detect/fight_train/weights/best.pt \ data./fight_dataset/fight.yaml \ batch1 \ imgsz640 \ conf0.25验证结果会打印每个类别的 precision、recall、mAP50 和 mAP50-95。打架检测里 mAP50 达到 0.75 以上才算具备现场测试的资格0.85 以上可以部署试点。如果 recall 偏低说明漏检多优先检查负样本比例是否过高如果 precision 偏低说明误报多检查 normal 类别的样本是否足够丰富。模型验证通过后导出成 ONNX 格式用于部署。打架检测通常跑在监控机的 CPU 上ONNX 可以在各种平台运行导出命令很简单yolo export modelruns/detect/fight_train/weights/best.pt formatonnx opset12导出后在现场测试阶段我会固定打一个 tag推理时把置信度阈值从默认的 0.25 往下调到 0.15因为打架场景宁可多报也不漏报。然后做一个帧间抑制逻辑连续 5 帧里有 3 帧报警才触发告警这个逻辑能过滤掉大量单帧抖动造成的误报又不影响真正的打架事件被捕获。我现在的习惯是每个新数据集都先跑一个 3 epoch 的 test 模式确认数据路径没问题再开完整训练。整套流程跑下来真正耗时间的往往不是训练本身而是格式转换和数据校验。数据层面的问题越早暴露后面越省心。希望这些踩过的坑对你有一点帮助。本文还有配套的精品资源点击获取
返回列表