简介:面向监控场景打架检测的数据集资源,包含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 的图为例,比较三种格式对同一个框的表示 # 假设标注了一个打架目标,像素坐标为 xmin=400, ymin=300, xmax=950, ymax=800 # VOC(XML 内核心内容) # <bndbox> # <xmin>400</xmin> # <ymin>300</ymin> # <xmax>950</xmax> # <ymax>800</ymax> # </bndbox> # COCO(JSON 内 annotations 数组中一项) box = [400, 300, 550, 500] # [x, y, width, height],其中 width=950-400, height=800-300 # YOLO(TXT 一行) # 0 0.3516 0.5093 0.2865 0.4630 # 计算过程:x_center=(400+950)/2/1920, y_center=(300+800)/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 Silicon(Mac 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,1,Mac M 芯片写mps,CPU 写cpu。
# 命令行方式直接训练,如果你更习惯用命令行而不是脚本 # GPU 机器 yolo detect train data=path/to/fight_dataset/data.yaml model=yolo11n.pt epochs=100 imgsz=640 batch=16 device=0 # Mac M 芯片机器 yolo detect train data=path/to/fight_dataset/data.yaml model=yolo11n.pt epochs=100 imgsz=640 batch=16 device=mps # CPU 机器 yolo detect train data=path/to/fight_dataset/data.yaml model=yolo11n.pt epochs=100 imgsz=640 batch=8 device=cpu4.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 训练时显存没占满但内存先爆了
现象:batch=16在 CPU 上跑,训练刚开始十来个 step 就报Killed或者内存溢出错误。
原因:很多人以为 CPU 训练不需要多少内存,其实 PyTorch 的 DataLoader 在加载图片时要先把 JPEG 解码成原始像素数组,一张 1080p 的图解码后大约占用 8MB 内存,workers=8的情况下一下子就是 8 批数据同时在内存里等待,内存就爆了。
解决:batch直接降到 4 或 2,workers降到 2。如果降了还不行,可以试试把图片先全部缩放到 640 分辨率单独存成一个缓存目录再训练,这算一种土办法但确实有效。另外关掉torch.backends.cudnn.benchmark也能省一点内存。
5.4 问题四:M 芯片 Mac 上torch.backends.mps直接报错
现象:脚本检测到 Mac 后调用model.train(device='mps'),抛出了类似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即可拿到,祝你训练顺利。
本文还有配套的精品资源,点击获取