3000张打架检测数据集:格式转换与YOLO11训练全指南
简介面向监控场景打架检测的数据集资源包含3000张真实监控场景高质量打架图片覆盖街道、酒吧、商店、公交车、监狱、空旷地等场景囊括两人打架与多人群殴等形态。数据采用LabelImg标注提供VOC、COCO、YOLO三种主流格式标签可直接用于YOLO等模型训练。资源包为单个PDF文档大小5.63MB内附数据集完整介绍与百度网盘获取方式。附带YOLO11一键训练脚本支持GPU(GPUs)、CPU、Mac(M芯片)多平台并给出博主训练结果日志参考可帮助算法工程师与安防开发者快速启动打架检测项目。目前已有853人学习下载标注质量高适合作为监控场景通用打架检测数据补充。1. 打架检测数据集别拿通用检测的思路来做这项任务做目标检测的人第一次接触打架检测往往误以为它和行人检测、车辆检测没什么区别——无非是换个数据集训练而已。真正跑过监控视频的人才知道打架检测是典型的“场景复杂度远高于目标本身”的任务两个人扭打在一起时目标之间的遮挡关系瞬间变化肢体轮廓严重变形再加上监控摄像头通常是俯视或斜视角度酒吧、公交、监狱这些场景的光线条件差异极大用通用目标检测的思维去做模型很容易把“拥抱”和“拉扯”这类接近的动作全部漏检或误检。这篇笔记要拆的这份打架检测数据集共 3000 张真实监控场景图片只标注 fight 一个类别覆盖街道、酒吧、商店、公交车、监狱、空旷地等场景配备 VOC/COCO/YOLO 三种格式标签外加一份支持 GPU/CPU/Mac 三平台运行的 YOLO11 一键训练脚本。对正在做监控场景行为识别的从业者来说它直接省掉了最耗时的数据采集和格式转换环节。2. 一套标注三种格式VOC/COCO/YOLO 的字段差异与转换原理2.1 三种格式分别是如何描述一个“框”的很多人在自己的项目里只接触过其中一种标注格式拿到这份数据集时看到三个文件夹第一反应是“是不是重复了”。其实三种格式描述的是同一个目标框只是坐标系和存储结构不同。VOC 格式以 XML 文件存储每个文件对应一张图片核心是object节点下的bndbox用xmin、ymin、xmax、ymax四个绝对像素值表示左上角和右下角坐标。COCO 格式是单个 JSON 文件标注信息集中在annotations数组里每个标注用bbox字段表示注意 COCO 的bbox是[x, y, width, height]即左上角坐标加宽高。YOLO 格式是 TXT 文件每行一个目标格式为class_id x_center y_center width height全部数值用图片宽高做了归一化取值在 0 到 1 之间。# 以一张 1920x1080 的图为例比较三种格式对同一个框的表示 # 假设标注了一个打架目标像素坐标为 xmin400, ymin300, xmax950, ymax800 # VOCXML 内核心内容 # bndbox # xmin400/xmin # ymin300/ymin # xmax950/xmax # ymax800/ymax # /bndbox # COCOJSON 内 annotations 数组中一项 box [400, 300, 550, 500] # [x, y, width, height]其中 width950-400, height800-300 # YOLOTXT 一行 # 0 0.3516 0.5093 0.2865 0.4630 # 计算过程x_center(400950)/2/1920, y_center(300800)/2/1080 # width(950-400)/1920, height(800-300)/1080这段对比想说明的核心问题是同一份标注数据底层的坐标值逻辑完全不同。你在训练 YOLO 时用的是归一化中心点加宽高在跑某些检测框架的评测脚本时可能又需要 VOC 的绝对坐标如果数据集只提供一种格式你迟早要自己写转换代码而这份资源直接把三种格式都给了省掉的就是这一步。2.2 用 labelimg 标注时格式怎么选labelimg 这个工具本身不存数据它只是一个标注前端界面上可以选择保存为 PascalVOC 或 YOLO 格式。常见的标注流程是先用 labelimg 标注默认存成 VOC 的 XML再用脚本批量转成 COCO 和 YOLO。这份数据集的标注说明里明确写了是用 labelimg 标注的所以三种格式的源头应该都是同一批 XML再通过转换生成的。如果你要从零开始标注自己的数据我一般建议直接选 YOLO 格式输出因为 YOLO 格式是文本文件即使标错了也能用脚本批量修正。但要注意一个反向坑labelimg 的 YOLO 模式会把类别列表存成classes.txt这个文件的顺序决定了每个类别的 ID。打架检测只有一个 fight 类别所以classes.txt里只有一行内容无论如何排序 ID 都是 0 或者 1这反而容易让人忽视类别顺序的重要性——后面训练脚本里data.yaml的类别名称必须和classes.txt完全一致。2.3 一个随手可用的 XML 到 TXT 转换脚本虽然数据集已经给了三种格式但实际项目里你大概率会往数据集里补充自己的图片然后用 labelimg 标注成 XML这时候你仍然需要把新标注的 XML 转换成 YOLO 的 TXT。这里给一个我常用的转换脚本也顺便说明转换过程中最容易出错的坐标归一化环节。import xml.etree.ElementTree as ET import os def xml_to_yolo(xml_path, txt_path, class_names): tree ET.parse(xml_path) root tree.getroot() img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) lines [] for obj in root.iter(object): cls_name obj.find(name).text if cls_name not in class_names: continue # 跳过未在类别列表中的标注避免类别ID越界 cls_id class_names.index(cls_name) bndbox obj.find(bndbox) xmin float(bndbox.find(xmin).text) ymin float(bndbox.find(ymin).text) xmax float(bndbox.find(xmax).text) ymax float(bndbox.find(ymax).text) # 归一化并计算中心点坐标 x_center ((xmin xmax) / 2) / img_w y_center ((ymin ymax) / 2) / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h # 越界保护某些标注框可能超出图像边界裁剪到有效范围 x_center max(0, min(1, x_center)) y_center max(0, min(1, y_center)) w max(0, min(1, w)) h max(0, min(1, 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)) # 使用示例 if __name__ __main__: class_names [fight] # 单类别打架检测 xml_to_yolo(000001.xml, 000001.txt, class_names)这段脚本的关键点在于两个一是读取图片宽高时必须用size/width和size/height不要用os.path.getsize去拿图片字节大小二是坐标归一化之前一定要先确认 XML 里存的是绝对像素值如果 labelimg 开启了某些缩放模式存出来的坐标可能已经不是原图尺寸了。数据集的 3000 张图是已经标注好的你直接用即可但后续补充数据时这个脚本就是你的后悔药。2.4 拿到资源后第一步跑一遍格式自检数据集从网盘下载下来之后不要急着解压完就丢进训练脚本里跑。先做一个简单的格式自检确认三件事图片数量与标注文件数量一致、所有标注文件能被正确解析、类别 ID 没有越界。常见的问题是下载过程中文件损坏或者某些 XML 文件被压缩软件解压时破坏了编码。我一般会写一个十几行的校验脚本遍历所有 XML 或 TXT 文件统计标注框数量、检查是否有空文件、是否有坐标值为负或大于 1 的异常数据。这个习惯让我避免过多次“训练到一半 loss 异常”的翻车事故。数据集附带的 3000 张图如果校验下来完全没有问题那后面的训练才会顺畅。3. 数据质量先过关单类别打架数据的标注细节与校验手段3.1 单类别打架数据的标注要点打架检测只有 fight 一个类别看起来比多类别检测简单实际上对标注质量的要求更高。因为类别少模型能从数据里学到的区分信息完全依赖框的位置和质量。如果标注框过大把背景面积也包进去模型就学不到“人形轮廓”这个关键特征如果框太小只框住了上半身而漏掉了腿部的踢踹动作模型又可能把打架动作的局部误判为普通挥手。# 统计标注框宽度和高度的分布快速发现框尺寸异常 import os from collections import Counter def check_bbox_distribution(label_dir): size_counter Counter() total_boxes 0 for filename in os.listdir(label_dir): if not filename.endswith(.txt): continue with open(os.path.join(label_dir, filename)) as f: for line in f: parts line.strip().split() if len(parts) ! 5: print(f异常行: {filename} - {line.strip()}) continue cls_id, x_center, y_center, w, h parts w, h float(w), float(h) # 按面积粗略分桶 area w * h if area 0.01: size_counter[小目标(1%面积)] 1 elif area 0.09: size_counter[中目标(1%~9%)] 1 else: size_counter[大目标(9%)] 1 total_boxes 1 print(f总标注框数: {total_boxes}) for category, count in size_counter.items(): print(f{category}: {count} ({count/total_boxes*100:.1f}%)) # 使用示例 check_bbox_distribution(labels/train)为什么关注框的尺寸分布因为监控场景下打架检测的特殊性在于摄像头距离不同人在画面中的尺度差异很大。酒吧摄像头可能离人只有两三米而监狱走廊的摄像头可能在五六米高的位置俯拍。如果训练集中小目标的占比过低模型在远处的打架行为检测上就会明显偏弱。3.2 标注文件常见异常与自动检查脚本从网盘下载的数据集经常遇到的一个问题是文件不完整。3000 张图加三种格式的标签解压后如果某个文件夹的文件少了十几个训练时 YOLO 会默默地跳过没有标注的图片而不是报错。结果就是你训练了半天发现训练集实际只有 2800 张图这对单类别检测来说可能影响不大但如果你基于这个模型的评估指标去做决策就会得出错误的结论。这里给一个检查图片与标注匹配情况的脚本适用于 YOLO 格式的标签目录。import os def check_img_label_match(img_dir, label_dir): img_files [f for f in os.listdir(img_dir) if f.endswith((.jpg, .jpeg, .png))] label_files [f for f in os.listdir(label_dir) if f.endswith(.txt)] img_names {os.path.splitext(f)[0] for f in img_files} label_names {os.path.splitext(f)[0] for f in label_files} # 有图无标签 img_without_label img_names - label_names # 有标签无图 label_without_img label_names - img_names if img_without_label: print(f有图无标签 ({len(img_without_label)} 张): {list(img_without_label)[:5]}...) if label_without_img: print(f有标签无图 ({len(label_without_img)} 张): {list(label_without_img)[:5]}...) # 检查空标签文件 empty_labels [] for name in label_names: path os.path.join(label_dir, name .txt) if os.path.getsize(path) 0: empty_labels.append(name) if empty_labels: print(f空标签文件 ({len(empty_labels)} 个): {empty_labels[:5]}...) else: print(未发现空标签文件) if not img_without_label and not label_without_img: print(f图片与标签完全匹配共 {len(img_names)} 张) # 使用示例 check_img_label_match(images/train, labels/train)这个脚本的核心价值不在于多复杂而在于把“数据完整性”变成一个可验证的硬指标。数据集里 3000 张图三种格式理论上图片和标签是严格对应的但下载解压过程谁也无法保证不出问题。跑这一遍至少能确认数据源是可用的。3.3 类别编号错了才是最大灾难单类别场景下最容易出现的一个隐蔽错误YOLO 格式的标注文件里类别 ID 写成了 1但data.yaml里只定义了一个类别。YOLO 训练时遇到这种情况通常会报错但如果是两个类别你定义了 fight 和 normal而标注文件里把 fight 写成 0、把 normal 写成 1恰好顺序反了那模型就会把“打架”识别成“正常”训练过程不会报任何错误推理结果全反了。这份数据集只有 fight 一个类别理论上不会出现类别混淆但你在使用其他公开数据集时一定要检查classes.txt的顺序。我自己的习惯是每次拿到新数据集第一件事就是打印所有标注文件的类别 ID 分布确认没有超出类别总数的 ID 出现。# 统计所有 YOLO 标注文件中的类别 ID 分布 # 在数据集标签目录下执行 awk {print $1} labels/train/*.txt | sort | uniq -c上面这条命令如果输出只有3000 0说明所有标注都是类别 0和单类别 fight 的定义一致。如果出现了其他数字就得回头排查 labelimg 的类别配置文件了。4. YOLO11 一键训练脚本GPU/CPU/Mac 三平台怎么跑4.1 数据集目录结构先对齐YOLO 系列框架对数据集目录有硬性要求训练前必须把图片和标签按train/val分好。这份打架检测数据集附带一键训练脚本但脚本不会替你整理目录所以拿到资源后第一步是把图片和标签按照下面的结构放好。fight_dataset/ ├── data.yaml ├── images/ │ ├── train/ │ │ ├── fight_0001.jpg │ │ └── ... │ └── val/ │ ├── fight_0001.jpg │ └── ... └── labels/ ├── train/ │ ├── fight_0001.txt │ └── ... └── val/ ├── fight_0001.txt └── ...data.yaml的内容也很简单核心就是指定类别名和路径。# data.yaml path: /path/to/fight_dataset # 数据集根目录建议写绝对路径 train: images/train val: images/val nc: 1 names: [fight]这里有一个容易忽略的细节path字段建议写成绝对路径避免相对路径在不同平台下解析不一致的问题。Windows 下路径分隔符是反斜杠Mac 和 Linux 是正斜杠写成绝对路径能少踩一个坑。4.2 一键训练脚本的分支逻辑这份资源附带的 YOLO11 训练脚本核心价值在于根据当前机器的硬件条件自动选择训练设备。脚本的逻辑通常是先检测有无 NVIDIA GPU再检测是否为 Apple Silicon最后才落到 CPU 训练。下面是一个典型的实现import torch import platform import sys def detect_device(): # 检测 Apple SiliconMac M系列芯片 if platform.system() Darwin: try: if torch.backends.mps.is_available(): return mps except AttributeError: pass # 旧版 PyTorch 没有 mps 模块忽略即可 # 检测 NVIDIA GPU if torch.cuda.is_available(): gpu_count torch.cuda.device_count() print(f检测到 {gpu_count} 张 NVIDIA GPU) return 0 # 使用第一张 GPU多卡可改为 0,1,2,3 # 回退到 CPU print(未检测到可用 GPU使用 CPU 训练) return cpu if __name__ __main__: device detect_device() # 调用 YOLO11 训练接口 # 这里是伪代码示意实际调用方式取决于你使用的 YOLO11 源码包 from ultralytics import YOLO model YOLO(yolo11n.pt) # 使用 YOLO11 nano 版本作为预训练权重 args { data: path/to/fight_dataset/data.yaml, epochs: 100, imgsz: 640, device: device, batch: 16 if device ! cpu else 8, workers: 4 if device ! cpu else 2, } model.train(**args)这个脚本的每个分支都有明确的目的。GPU 分支判断cuda.is_available()这是 PyTorch 的标准做法Mac 的mps分支依赖 PyTorch 1.12 及以上版本才有的 Metal 加速后端CPU 分支作为最后的保底方案。参数上也做了区分CPU 训练的 batch size 明显比 GPU 小因为 CPU 内存带宽有限过大的 batch 会导致训练速度进一步下降。4.3 参数说明batch、epochs、imgsz、device这 4 个参数是影响训练效果最直接的旋钮逐一说明。batch表示一次迭代送入网络的图片数量。GPU 上显存越大batch 可以越大但 16 到 32 之间是打架检测这类单类别任务比较合适的区间CPU 训练建议 4 到 8再大不仅速度上不去内存还可能爆。epochs表示训练轮数打架检测这类单类别任务100 轮在 GPU 上大概需要几个小时在 CPU 上可能要跑两三天脚本里把两个平台的默认值区分开是有道理的。imgsz是输入图片的分辨率640 是 YOLO 系列的标准值监控画面通常是 1080p 甚至更高按 640 输入意味着模型看到的是缩小后的画面小目标检测会吃亏如果你特别关注远处或者画面中较小的人的打架行为可以把imgsz提到 960但训练时间和显存占用都会明显上升。device就是刚才脚本自动检测到的值手动指定时 GPU 写0多卡写0,1Mac M 芯片写mpsCPU 写cpu。# 命令行方式直接训练如果你更习惯用命令行而不是脚本 # GPU 机器 yolo detect train datapath/to/fight_dataset/data.yaml modelyolo11n.pt epochs100 imgsz640 batch16 device0 # Mac M 芯片机器 yolo detect train datapath/to/fight_dataset/data.yaml modelyolo11n.pt epochs100 imgsz640 batch16 devicemps # CPU 机器 yolo detect train datapath/to/fight_dataset/data.yaml modelyolo11n.pt epochs100 imgsz640 batch8 devicecpu4.4 三平台分别要注意的环境差异先说 Windows GPU 平台最常见的问题是 CUDA 和 PyTorch 版本不匹配。博主给的脚本通常会要求你安装特定版本的 PyTorch安装命令一般是pip install torch torchvision torchaudio但如果你是自己装的环境建议先跑python -c import torch; print(torch.cuda.is_available())确认 CUDA 可用再开始训练。否则脚本检测到没有 GPU会默默降级到 CPU 训练速度慢得让你怀疑机器坏了。CPU 平台最大的限制是速度。3000 张图的打架检测数据集在普通桌面级 CPU 上用 640 分辨率训练 100 轮时间可能在几十个小时量级。如果你只能 CPU 训练有几个务实的手段用 nano 尺寸的模型而不是 medium 或 large把imgsz降到 416 或 480减少训练轮数到 50 到 60 轮先用起来等有 GPU 再二次训练。这些参数不是玄学是资源受限时最合理的取舍。Mac M 芯片平台要特别注意 PyTorch 的 MPS 后端还不够成熟。虽然torch.backends.mps.is_available()返回 True但某些算子仍然没有完整的 MPS 实现训练过程中可能报出一些看起来莫名其妙的错误比如NotImplementedError或者AssertionError。遇到这种情况先试试把 batch 调小如果还不行可以在脚本里加一行torch.backends.mps.sync_debug True定位具体卡在哪个操作上实在不行就退回 CPU 训练M 芯片的 CPU 算力其实也不差。5. 训练避坑数据划分、单类别标注与显存不足的常见问题5.1 问题一训练集和验证集划分不随机现象训练完发现验证集里的场景和某个训练子集的场景高度重合mAP 指标虚高部署到新监控点位后效果明显变差。原因打架检测数据集的图片是按场景组织的比如酒吧场景的图片可能连号排列。如果你用了默认的前 80% 做训练、后 20% 做验证那验证集可能是某一类场景的集中样本甚至更糟——同一段视频的连续帧被同时分到了训练集和验证集。解决划分前先按场景分组把同一场景的图片整体划到训练集或验证集不要随机逐张切分。写个简单的脚本把图片文件名前缀作为分组依据或者用sklearn的GroupShuffleSplit做分组划分。5.2 问题二loss 正常下降但 mAP 几乎不变现象训练的 loss 曲线看起来在正常收敛但每隔 10 轮输出的验证集 mAP50 始终在低位徘徊。原因单类别检测数据集最常见的病根是误检和漏检的比例失衡。打架检测的标注只有 fight 一类但监控画面里大量存在的普通行走、交谈、拥抱如果被模型当成了正样本mAP 就会被压住。还有一个隐蔽的原因很多打架图片中目标框内还包含了另一个人身体的一部分这种高度重叠的框让模型学不出清晰的边界推理时输出的框位置偏移严重。解决如果你的数据集里有大量高重叠目标但标注框是两个分离的矩形说明标注时没有把互相遮挡的目标处理到位。我的实践是给这种样本增加额外的裁剪增强或者对重叠度过高的样本做一次人工复核。另外可以试试把置信度阈值调高到 0.35 以上做推理过滤掉低分误检。5.3 问题三CPU 训练时显存没占满但内存先爆了现象batch16在 CPU 上跑训练刚开始十来个 step 就报Killed或者内存溢出错误。原因很多人以为 CPU 训练不需要多少内存其实 PyTorch 的 DataLoader 在加载图片时要先把 JPEG 解码成原始像素数组一张 1080p 的图解码后大约占用 8MB 内存workers8的情况下一下子就是 8 批数据同时在内存里等待内存就爆了。解决batch直接降到 4 或 2workers降到 2。如果降了还不行可以试试把图片先全部缩放到 640 分辨率单独存成一个缓存目录再训练这算一种土办法但确实有效。另外关掉torch.backends.cudnn.benchmark也能省一点内存。5.4 问题四M 芯片 Mac 上torch.backends.mps直接报错现象脚本检测到 Mac 后调用model.train(devicemps)抛出了类似RuntimeError: Placeholder storage has not been allocated的错误。原因MPS 后端对某些算子的实现不完整特别是用到upsample或者某些自定义的注意力机制时容易触发。这不是你的代码问题是 PyTorch 在 Apple Silicon 上的成熟度问题。解决我的经验是先升级到最新版 PyTorch (pip install --upgrade torch torchvision)新版对 MPS 的支持每代都有明显进步。如果升级后仍然报错就把device设为cpu跑M 系列芯片的 CPU 性能做推理完全够用训练也就是慢一些而已但至少能稳定出结果不会白跑几小时然后报错。6. 从博主训练日志里读懂收敛三个必看指标6.1 训练日志里哪些字段值得盯这份资源附带博主训练结果日志很多人拿到日志后不知道看什么只知道“看起来训练完了”。其实 YOLO 训练日志里值得盯的字段就三个box_loss、cls_loss、mAP50。box_loss反映的是预测框和真实框的位置偏差打架检测场景下目标互相遮挡严重这个值通常不会收敛得像行人检测那么低一般降到 1.0 以下就说明框的位置学得差不多了。cls_loss是分类损失单类别任务下这个值本身就不会太高但如果它反复震荡不下降说明有相当一部分样本的语义特征没学好。mAP50是核心指标打架检测这个数据量级下博主日志里如果能到 0.85 以上说明数据质量是过关的如果只在 0.7 左右徘徊先排查是不是数据划分出了问题。6.2 用日志判断要不要加轮数和调学习率最后 10 轮的日志特别值得对比。如果mAP50还在明显上升说明欠拟合你把epochs从 100 加码到 150 或 200 大概率还能涨点指标。如果mAP50已经连续 10 轮不涨但box_loss还在降这就是典型的过拟合前兆——继续训练只会让模型对训练集更敏感对新场景的泛化反而更差。另一个判断点是日志里输出的学习率曲线。YOLO11 默认使用余弦退火调度学习率会在后半段逐步降低到接近 0。如果你的训练在第 80 轮时学习率已经压得很低而 mAP 还没起来说明模型已经学不动了加再多轮数也没意义这时候更应该回头检查数据质量和标注正确性而不是调参。说到调参有一条自己的经验是从那次用 CPU 训练三天终于跑完 3000 张图之后养成的从那以后我每次训练前都强制走一遍格式自检脚本确认图片和标签一一对应、类别 ID 正常、没有空标签文件再确认数据划分没按文件名顺序切最后才把epochs和batch设好交给训练脚本。这套流程排队下来训练翻车的概率至少降了一半。希望这篇笔记能帮你在自己的打架检测项目上少走几段弯路。资源获取方式数据集放在百度网盘内附的 PDF 里有完整的数据集介绍和下载说明在公众号「极智视界」回复凭证jIGUWmZjW4ho即可拿到祝你训练顺利。本文还有配套的精品资源点击获取