2026/9/17 5:22:24

YOLO11打架检测实战:小数据集标签转换与跨平台训练脚本

YOLO11打架检测实战:小数据集标签转换与跨平台训练脚本 简介面向监控场景打架检测需求的标注数据集资料包含1000张真实监控视角的高质量打架图片覆盖街道、酒吧、商店、公交车、监狱及空旷地等多种场景既有两人冲突也有多人群体事件配套fight单类别标注可支撑实际安防项目的数据补充与算法迭代。资料包为1个PDF文档约5.6MB内附数据集整体介绍与百度网盘获取方式便于按需下载原始图片资源不直接内置图像需通过网盘提取。标注采用LabelImg完成同时提供VOCxml、COCOjson、YOLOtxt三种常见格式可直接用于YOLO等模型训练额外附带YOLO11一键训练脚本并给出GPU、CPU、MacM芯片多平台训练方案及博主训练结果日志方便不同算力环境快速上手并参考调参降低工程落地门槛。当前已有691人学习下载适合算法工程师、科研学生与安防领域开发者参考使用。1. 打架检测数据集只有 1000 张图时YOLO11 怎么训才不白费监控场景里识别打架行为本质上是目标检测里的“行为识别”任务但难点不在算法而在训练集和部署环境的匹配。这个项目把三件事打包在一起1000 张打架检测图片、VOC/COCO/YOLO 三种标签格式、以及能同时跑 GPU/CPU/Mac 的 YOLO11 一键训练脚本。对刚接触目标检测的人和安防项目落地工程师来说这套组合解决的是同一件事用最小的数据量把训练流程跑通用统一脚本消除团队内部环境差异。1000 张图是小数据集它更考验标注质量、数据增强和超参数选择而不是盲目加大模型。2. 打架检测数据集准备VOC/COCO/YOLO 三种标签的字段差异与转换2.1 1000 张打架图的标注设计与训练/验证集划分打架检测数据集的核心不是“多”而是“正负样本的比例”。1000 张图里如果 900 张是打架模型很容易学会“有人群就报警”因为负样本不够。安防场景里真正干扰模型的是两人贴得很近的日常行为拥抱、搀扶、握手、从背后搭肩。常见做法是把标签设计成两个类别fight和normal_contact前者是真正的攻击行为后者是近距离接触但不能判为打架的行为。这样模型学的是二者的边界而不是单纯学一个“有人”就能触发的高召回模型。1000 张图按 8:1:1 拆分就是 800 张训练、100 张验证、100 张测试。验证集看起来很小但因为每张图可能有多个目标实际标注框数量会比图片数多不少。一般打架场景每张图 1 到 3 个目标验证集大约有 150 个以上的标注框足以评估 mAP。拆分时要注意同一个视频片段里抽出来的连续帧不能一部分进训练集、一部分进验证集否则验证结果会虚高。我一般会按视频片段分组整个片段只落在同一个集合里。dataset_root/ ├── images/ │ ├── train/ # 800 张 │ ├── val/ # 100 张 │ └── test/ # 100 张 ├── annotations/ │ ├── train_voc/ # 每个 XML 对应 images/train 里的一张图 │ ├── val_voc/ │ ├── train_coco.json │ ├── val_coco.json │ ├── train_yolo/ # 每个 TXT 对应一张图 │ └── val_yolo/这个目录结构是同时维护三种格式的常见方式。推理时底稿是 VOC 的 XML 或 COCO JSONYOLO 的 TXT 从前者生成即可不要手工维护否则三份文件一旦不同步训练和交付就会出现类别编号错位。2.2 VOC/COCO/YOLO 三种标签格式的字段差异对比三种格式的核心差异在两点坐标值的表示方式、类别 id 的传递方式。VOC 是 XML 结构坐标是左上角和右下角的绝对像素值COCO 是 JSON 结构坐标是左上角 x/y 和框宽高也是像素值YOLO 是纯文本文件每行一个目标坐标是中心点 x、中心点 y、宽、高全部归一化到 0 到 1。类别这一列VOC 和 COCO 可以写字符串YOLO 只能写整数索引所以转换时类别列表的顺序必须固定。格式文件类型框坐标含义类别写法数据组织方式VOCXMLxmin, ymin, xmax, ymax绝对像素字符串如 fight一个 XML 配一张图COCOJSONx, y, width, height绝对像素数字 id映射存于 JSON所有标注汇总到一或两个 JSONYOLOTXTx_center, y_center, width, height0~1 归一化整数索引一个 TXT 配一张图用 labelImg 标注出来的默认格式就是 VOC把 XML 直接转成后两种是最省事的路径。转换时最容易出错的是 YOLO 归一化里除的是图片原始宽高而 COCO 的bbox必须保持像素绝对值。如果模型训练时把图 resize 到 640YOLO 归一化坐标仍然有效因为训练加载器会先读归一化坐标再缩放COCO 坐标则完全依赖加载器内部处理。这一点决定了两种格式不能混用同一套坐标字段。2.3 用 Python 脚本把 VOC 标签批量转成 COCO 与 YOLO 格式先把 VOC 转 YOLO这是后续一切训练的基础。转换逻辑不复杂但类别顺序、坐标精度、空文件这三个细节决定了转换完能不能直接用。# voc_to_yolo.py import os import glob import xml.etree.ElementTree as ET CLASSES [fight, normal_contact] # 类别顺序固定转完不能再改 def voc_to_yolo(xml_path, 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.iter(object): name obj.find(name).text if name not in CLASSES: continue # 跳过未定义类别 cls_id CLASSES.index(name) box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) x_center (xmin xmax) / 2.0 / img_w y_center (ymin ymax) / 2.0 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) with open(txt_path, w) as f: f.write(\n.join(lines))这段代码把 VOC 的 bndbox 坐标正确转换为 YOLO 的归一化中心点坐标。参数说明CLASSES的顺序就是 YOLO 类别编号的真相来源训练时fight.yaml里的 names 列表必须和它一致xmin/xmax用 float 转换而不是 int避免某些标注工具导出的小数坐标被截断。如果一张图里有两个 fight 框循环里会追加两行符合 YOLO TXT 每行一个目标的约定。COCO JSON 的构造比 YOLO 复杂需要四个顶层数组images、annotations、categories其中 annotation 必须带id、image_id、bbox、area、iscrowd字段缺一个都有可能让评估代码报错。# voc_to_coco.py import json import glob import os from PIL import Image import xml.etree.ElementTree as ET CLASSES [fight, normal_contact] coco {images: [], annotations: [], categories: [ {id: 0, name: fight}, {id: 1, name: normal_contact}]} ann_id 0 for img_id, xml_path in enumerate(glob.glob(annotations/train_voc/*.xml)): tree ET.parse(xml_path) root tree.getroot() filename root.find(filename).text img_path os.path.join(images/train, filename) w, h Image.open(img_path).size coco[images].append({id: img_id, file_name: filename, width: w, height: h}) for obj in root.iter(object): name obj.find(name).text if name not in CLASSES: continue box obj.find(bndbox) x1, y1 float(box.find(xmin).text), float(box.find(ymin).text) x2, y2 float(box.find(xmax).text), float(box.find(ymax).text) bw, bh x2 - x1, y2 - y1 coco[annotations].append({ id: ann_id, image_id: img_id, category_id: CLASSES.index(name), bbox: [x1, y1, bw, bh], area: bw * bh, iscrowd: 0}) ann_id 1 json.dump(coco, open(annotations/train_coco.json, w))这段代码的 COCOcategory_id用的是类别列表下标和 YOLO 的类别编号保持一致这样两种格式在同一个模型上不会出现类别错位。area如果只给 0 会导致某些评估工具计算 mAP 时数值异常所以这里用宽高乘积填充。iscrowd统一置 0表示这些框都是单个目标打架场景里如果没有人群密集遮挡不需要额外的 crowd 标注。转换完成后要做一个校验统计每张图是否有空标签。训练集里有一些负样本图是正常的但验证集如果出现空标签mAP 评估可能因为缺了负样本而偏高。用一句命令检查find . -name *.txt -size 0把空文件数量打印出来确认比例在预期范围内。3. YOLO11 目标检测模型选型与训练参数从数据 yaml 到超参的设定顺序3.1 1000 张图该用 YOLO11 哪个规格YOLO11 是轻量目标检测算法里迭代较快的一代网络结构上把骨干里的 C3K2 模块和 C2PSA 注意力做了整合检测头继续采用解耦设计分类分支和回归分支分开整体参数量比 YOLOv8 同规格更低。对打架检测这种单类目标、场景又固定的任务模型规模不必大因为模型体积带来的收益远小于标注质量带来的收益。yolo11n 在 1000 张图上的表现通常已经够用。yolo11n 权重文件约 5~6 MBGPU 上单帧推理延迟可以压到几毫秒Mac 的 MPS 后端也能跑到接近实时。如果打架检测需要识别的是 5 米外模糊的人群换 yolo11s 会更稳但代价是训练时间增加 30% 以上Mac CPU 上的推理帧率会明显下降。我做这个规模的项目时会先用 yolo11n 跑通 baseline再看混淆矩阵决定要不要升规格而不是一开始就选大模型。3.2 打架检测的 YOLO11 数据配置train/val 路径与类别命名YOLO11 训练用 YAML 描述数据集路径字段决定数据加载器去哪里找图片和标签。这一步配置错了后面跑多少轮都是空转。# fight.yaml path: /data/fight_detection train: images/train val: images/val names: 0: fight 1: normal_contactpath写项目根目录train 和 val 是相对 path 的子路径。Ultralytics 会自动寻找图片目录/../labels/split/下的 TXT 标签所以 images/train 必须对应 labels/train两个目录在同一级。names 的次序和第 2 章的 CLASSES 完全一致。这里最常见的报错是no labels found原因是只放了图片没放标签目录或者标签目录大小写不一致报错信息不会告诉你缺的是哪个目录只能自己逐项核对。如果所有标签都是从 VOC 转来的先把 labels 目录建好mkdir -p labels/train labels/val python voc_to_yolo.py annotations/train_voc/*.xml labels/train python voc_to_yolo.py annotations/val_voc/*.xml labels/val3.3 YOLO11 训练超参数的三个必调项与设备参数YOLO11 训练超参数里影响最大的不是学习率而是 epochs、batch 和 imgsz。1000 张图、训练集只有 800 张属于典型小数据量微调。epochs 要拉长一般给 150配合 early stopping 的 patience 30。预训练权重提供的是低层通用特征不是打架语义所以微调 150 轮是合理预期不是过拟合的必然来源。设备batch size 建议imgszdevice 参数备注NVIDIA GPU 12GB32640device0显存不足时降到 16CPU4~8416~640devicecpu多线程训练耗时是 GPU 的 5~10 倍Mac MPS16640devicemps需要 PyTorch 1.12 以上from ultralytics import YOLO model YOLO(yolo11n.pt) # 加载预训练权重 model.train( datafight.yaml, epochs150, patience30, batch16, imgsz640, devicemps, # cuda:0 / cpu / mps workers4, optimizerauto, lr00.005, augmentTrue, )这段脚本里的device是训练的核心开关。GPU 机器写cuda:0Mac 写mps纯 CPU 机器写cpu。optimizerauto是 Ultralytics 的自动选择小数据量下它通常选 AdamW显存吃紧可以改成 SGD占用更小但收敛变慢。augmentTrue时 YOLO 默认启用 Mosaic、HSV 变换和随机翻转打架场景里的服装颜色差异主要靠这些增强来放大成模型能学到的视觉多样性。需要留意另一个参数cacheTrue。把 800 张图全部缓存在显存或内存里可以加速训练但大图预处理后的缓存可能占掉好几 GB。建议 CPU 机器不要开 cacheGPU 上开之前先看一眼内存余量数据加载慢一点比 OOM 中断更好。3.4 小目标与模糊打架画面的增强方向安防摄像头通常装在 3 米以上高度画面里的人体高度可能只有几十个像素属于目标检测里的小目标问题。YOLO11 在 640 分辨率下对小于 16×16 像素的目标基本无能为力所以要么提高 imgsz 到 960要么在网络输入端保持 640 但调整标注策略。对打架检测来说把模糊的远处人体框标成normal_contact或忽略掉比强行让模型学一个看不见的小目标更实用。真正要增强的反而是中近距离的动作冲突画面mosaic 增强会让这类目标变大、更连续。4. YOLO11 一键训练脚本GPU/CPU/Mac 三平台的设备检测与参数降级4.1 一键脚本的适配逻辑先用代码把设备探测出来所谓“一键训练”本质上把两个决策自动化当前跑在什么设备上、batch/workers 跟着设备怎么调。如果用一个静态脚本device0在 Mac 上会直接崩devicemps在无 N 卡的机器上也会报错。所以脚本第一段一定是设备探测。import platform def detect_device(): try: import torch except ImportError: return cpu, torch not installed if torch.cuda.is_available(): return cuda:0, nvidia if platform.system() Darwin and torch.backends.mps.is_available(): return mps, mps return cpu, cpu这段探测函数的判定顺序是有讲究的。CUDA 优先因为 GPU 训练效率最高然后判断 MPSApple Silicon 的统一内存跑 YOLO11 训练比 CPU 快很多最后兜底是 CPU。探测的是 PyTorch 的可用设备而不是系统有没有显卡因为训练脚本唯一依赖的就是 torch 的运行时。如果 CUDA 环境有问题但机器有 A 卡这里会正确落到 CPU不会报 CUDA error。4.2 一键训练脚本环境检查、数据完整性与训练启动一个可复用的训练脚本可以直接用 Python 写完跨平台不受 shell 差异影响。Bash 脚本在 Windows 上不可用Python 在 GPU/CPU/Mac 上都能跑这是选择它的第一理由。# train_one_click.py import os import platform import sys from ultralytics import YOLO device detect_device()[0] print(f[INFO] 平台: {sys.platform}, 设备: {device}) # 按设备给参数 if device.startswith(cuda): batch, workers 32, 8 elif device mps: batch, workers 16, 4 else: batch, workers 4, 0 # CPU 不开启多进程加载 # 校验图片和标签数量一致 for split in [train, val]: img_dir ffight_dataset/images/{split} label_dir ffight_dataset/labels/{split} n_img len(os.listdir(img_dir)) n_txt len(os.listdir(label_dir)) assert n_img n_txt, f{split} 图片和标签数量不一致: {n_img} vs {n_txt} model YOLO(yolo11n.pt) model.train( datafight.yaml, epochs150, batchbatch, workersworkers, devicedevice, imgsz640, patience30, cache(device ! cpu), ) print([DONE] 训练结束最优权重: runs/detect/train/weights/best.pt)脚本里有两个容易被忽略的细节。第一个是workers在 CPU 兜底模式下设为 0因为 dataloader 的多进程开销在纯 CPU 上可能抵消训练加速甚至引起内存不足。第二个是训练前先校验n_img n_txt把标签缺失问题挡在模型启动之前而不是跑到某个 epoch 才发现空 batch。首次运行前需要确认依赖。在 Mac 上pip install -U ultralytics torch torchvision默认安装的 PyTorch 就带 MPS 支持。在 GPU Linux 服务器上先看nvidia-smi的驱动和 CUDA 版本再装对应 torch如果训练时torch.cuda.is_available()返回 False问题几乎都出在 CUDA 版本和 torch 不匹配不用改脚本。4.3 训练中断后的恢复与权重复用训练跑一半崩溃是常态150 轮小数据集也可能花掉 4~8 小时OOM、断电、手动 CtrlC 都会中断。Ultralytics 每个 epoch 结束会写last.pt和best.pt所以重启训练应该从上次进度继续而不是重新从预训练权重开始。model YOLO(runs/detect/train/weights/last.pt) model.train(datafight.yaml, epochs150, resumeTrue)或者用命令行yolo detect train resume modelruns/detect/train/weights/last.ptresume 模式会忽略新的 epochs 参数直接从原进度继续。如果只是验证脚本能不能跑通可以先训 30 轮确认数据加载、设备、损失曲线都正常再续跑完整 150 轮。这样即使环境有问题浪费的时间也被控制在半小时内。4.4 三平台并发训练的关键约束团队同时有 GPU 服务器、Mac 和 CPU 旧机器时同一份脚本跑不同规格的模型要改的只有YOLO(yolo11n.pt)里的模型名和 batch。并发训练有两个资源背景问题GPU 机器上同时跑两个 yolo11n 训练、每进程 batch 32显存可能到 16GB 上下容易 OOMMac 的 MPS 使用统一内存同时跑训练和视频推理会看到内存压力拉满系统开始交换分区。平台主要风险处理方式GPU显存 OOM限制每进程显存占比减小 batchMac MPS统一内存占用高batch 不超过 16不要并发推理CPU训练耗时过长workers0cacheFalse调小 imgsz限制 GPU 进程显存的方式是在训练前调用if device.startswith(cuda): import torch torch.cuda.set_per_process_memory_fraction(0.8, 0)这行代码要在YOLO(...)之前执行给其他服务预留 20% 显存。5. 打架检测部署时的验证与样本挖掘帧采样和多级标签5.1 训练完先做一次真实场景视频的帧采样验证mAP 是在静态图片上判断的打架检测最终面对的是视频流。不要直接拿摄像头画面去跑训练好的模型视频帧之间存在时间连贯性同一组动作在连续帧里会反复出现。正确做法是取一段 30 秒真实监控视频按 1 秒 1 帧采样得到 30 帧再跑模型看误报率。如果打架动作很快抽帧率提高到 5 fps否则高速挥拳在低采样率下可能被整个跳过。5.2 mAP50 和 mAP50-95 的差距说明什么打架检测别只看 mAP50。如果 mAP50 很高而 mAP50-95 明显下降说明预测框的位置不太稳定边缘对齐较弱。打架场景对框的 pixel 级精度要求不高但框对齐会直接影响下游跟踪模块比如按 IoU 做跨帧关联时框抖动会导致同一目标的轨迹断裂。建议至少保证 mAP50-95 在 mAP50 的 70% 以上不足时优先调overlap相关的 nms 参数或提高 imgsz。5.3 误报集中时的多级标签处理跌倒、拥抱和打架在 2D 框尺度上可能完全一致模型只能靠纹理和姿态区分。如果 normal_contact 样本不够误报就会集中在拥抱这类动作上。常见做法是把正常类别扩展为更细的标签hug、carry、shake_hand再在推理后处理里做分级过滤只把 fight 置信度高于阈值且连续出现 3 帧以上的动作上报。这比增大模型更有效因为这类误报本质上是时序分类问题不是单帧定位问题。视频流推理的最终评估指标应该是单位时间误报数而不是 mAP记录 10 次报警并过滤重复单帧报警按“次/天”计算低于一次再考虑调阈值。最后一个值得做的技巧是把best.pt导出成 ONNX 和 Core ML 格式在 Mac 上做 CPU 推理时内存占用和延迟都比 PyTorch 原生推理更好尤其对部署在 i5/i7 处理器上的旧款 Mac 设备这个优化比换模型更直接。yolo export modelbest.pt formatonnx imgsz640 yolo export modelbest.pt formatcoreml imgsz640本文还有配套的精品资源点击获取