ARTICLE DETAIL

资讯详情

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

YOLO11打架检测实战:标签格式转换与三平台训练全攻略

YOLO11打架检测实战:标签格式转换与三平台训练全攻略 简介目标检测方向的打架行为识别数据集聚焦监控场景下的安防项目与计算机视觉研究。包含1000张真实监控截图覆盖街道、酒吧、商店、公交车、监狱、空旷地等多种环境以及两人冲突、多人群体事件等不同形态统一标注为fight单一类别。标注采用labelimg完成质量较高并同步提供VOC(xml)、COCO(json)、YOLO(txt)三种典型目标检测格式可直接用于YOLO等主流算法训练节省手动整理数据的时间。随包附赠YOLO11一键训练脚本支持GPU(GPUs)、CPU以及Mac(M芯片)多平台运行另提供博主训练结果日志作为参考。资源包共1个PDF文件大小5.6MB因原图数据集体积较大PDF内含数据集详细介绍与实际获取方式。目前已有695人学习适合需要快速落地监控异常行为检测的开发者与算法工程师。1. 打架检测数据集1000张图能做什么不能做什么拿到1000张打架检测图第一反应通常不是“够不够”而是“赶紧跑”。但真正做过一次智能安防或园区监控项目的工程都会告诉你1000张图不是让你直接上生产而是让你把训练、验证、部署这条链路完整跑通。这个数据集的价值在于它同时给了VOC/COCO/YOLO三种格式标签配合YOLO11的一键训练脚本等于把“从标注到权重文件”的最短路径摆在你面前——你只需要关心参数怎么调、坑在哪里。适合谁三类人第一类是做毕业设计或竞赛演示的学生需要快速出一个能演示的检测效果第二类是做安防算法选型的工程师想用最小成本验证YOLO11在打架/肢体冲突场景上的表现第三类是刚接触目标检测的开发者想通过一套完整数据脚本理解三种标签格式之间的关系。不适合谁想拿它直接做生产级模型的团队——1000张图的分布、场景覆盖和标注一致性都远远不够它只够证明方向可行。2. 三种标签格式到底差在哪VOC/COCO/YOLO的转换与校验2.1 先把三种格式的坐标系讲透在碰脚本之前必须搞明白这三类标签的本质区别否则转换出来的数据一定出错。VOC格式是XML文件每个目标用bndbox里的xmin/ymin/xmax/ymax四个绝对像素值表示矩形框坐标原点是图像左上角。COCO格式是单JSON文件用bbox字段存[x, y, width, height]也是类似VOC的绝对像素坐标。YOLO格式最特殊——每个图像对应一个txt文件每行是class_id x_center y_center width height这四个数全部归一化到0~1之间除以图像宽高得到。常见的翻车点就在这里有人直接把VOC的xmin/ymin/xmax/ymax除以宽高当成YOLO的x_center/y_center结果框全偏到左上角。YOLO需要的是中心点坐标不是左上角坐标也不是右下角坐标。转换公式应该是x_center (xmin xmax) / 2 / widthy_center (ymin ymax) / 2 / heightw (xmax - xmin) / widthh (ymax - ymin) / height。差一步就全错这属于那种“脚本不报错但结果一定错”的问题。2.2 VOC转YOLO的Python脚本一行都不能错下面这段是VOC XML转YOLO txt的完整脚本我一般会直接在数据集根目录跑输出到labels文件夹。注意几个关键点类别映射表必须手动维护不能从XML里自动读图像宽高要从对应的图片文件读取不能依赖XML里的size节点有些标注工具导出的size字段不准。代码使用Python编写import os import xml.etree.ElementTree as ET from PIL import Image # 类别映射与数据集的类别顺序保持严格一致 CLASS_MAPPING {fight: 0, normal: 1} # 根据实际类别调整 # 定义输入输出路径 xml_dir Annotations # VOC XML所在目录 img_dir JPEGImages # 对应图像目录 out_dir labels # YOLO txt输出目录 os.makedirs(out_dir, exist_okTrue) for xml_file in os.listdir(xml_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(xml_dir, xml_file)) root tree.getroot() # 图像文件名取自XML宽高取自真实图片文件 img_name root.find(filename).text img_path os.path.join(img_dir, img_name) with Image.open(img_path) as img: img_w, img_h img.size txt_path os.path.join(out_dir, xml_file.replace(.xml, .txt)) with open(txt_path, w) as f: for obj in root.iter(object): cls obj.find(name).text if cls not in CLASS_MAPPING: continue box obj.find(bndbox) xmin int(float(box.find(xmin).text)) ymin int(float(box.find(ymin).text)) xmax int(float(box.find(xmax).text)) ymax int(float(box.find(ymax).text)) # 边界裁剪防止标注框超出图像边缘 xmin max(0, min(xmin, img_w - 1)) ymin max(0, min(ymin, img_h - 1)) xmax max(0, min(xmax, img_w - 1)) ymax max(0, min(ymax, img_h - 1)) # 转换为YOLO格式中心点 宽高全部归一化 x_center (xmin xmax) / 2 / img_w y_center (ymin ymax) / 2 / img_h width (xmax - xmin) / img_w height (ymax - ymin) / img_h f.write(f{CLASS_MAPPING[cls]} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}\n) print(f转换完成共处理 {len(os.listdir(xml_dir))} 个XML文件)参数说明CLASS_MAPPING必须和数据集的类别顺序一致建议先打开一个XML确认类别名图像宽高从JPEGImages里读是为了避免XML里的size被标注工具改过边界裁剪那三行是血泪经验——不少标注框会画出边界几个像素不裁剪的话训练时YOLO会警告甚至把loss拉高。2.3 COCO和YOLO互转JSON解析的坑如果数据集给的是COCO格式转YOLO的核心是解析annotations里的id到image_id的映射。这里最常见的坑是COCO的images数组和annotations数组不是一一对应的——一张图可能没有标注框也可能有多个框。用Python写过一次就会发现必须先把images的id和file_name映射成一个字典再遍历annotations逐个写入否则文件对应关系一定会错import json # 打开COCO标注文件 with open(instances_train.json, r) as f: coco json.load(f) img_id_to_name {img[id]: img[file_name] for img in coco[images]} out_dir labels_coco os.makedirs(out_dir, exist_okTrue) # 先建立每个图片id对应的标注列表 anns_by_img {} for ann in coco[annotations]: img_id ann[image_id] anns_by_img.setdefault(img_id, []).append(ann) for img_id, anns in anns_by_img.items(): img_name img_id_to_name[img_id] txt_path os.path.join(out_dir, img_name.replace(.jpg, .txt)) with open(txt_path, w) as f: for ann in anns: cat_id ann[category_id] x, y, w, h ann[bbox] # COCO的bbox是[x, y, width, height]转YOLO需要中心点 x_center x w / 2 y_center y h / 2 # 归一化前需要知道图像宽高 img_info coco[images][img_id] img_w, img_h img_info[width], img_info[height] f.write(f{cat_id} {x_center/img_w:.6f} {y_center/img_h:.6f} {w/img_w:.6f} {h/img_h:.6f}\n)这段脚本里有个容易忽略的细节coco[images]的索引和img_id不是同一个值直接用img_id去索引coco[images]会拿到错图。必须先通过img_id_to_name找到文件名再从文件名推导宽高或者为图片id单独建立宽高映射。COCO格式的这种间接索引设计就是它转换时最容易出bug的地方。2.4 校验三件套画框、比对、数行数转换完成后别急着训练三件事必须做。第一件是可视化校验随机抽20张图用OpenCV把标签框画回原图肉眼确认框的位置和类别是否匹配。第二件是数量比对统计XML/JSON/COCO里总框数和转换后txt里的行数是否一致不一致就说明有目标在转换中被丢弃了。第三件是坐标合法性检查写个快速脚本扫描所有txt确认每一行的五个数字都在0到1之间同时确认width和height大于零。import os # 快速合法性检查扫描所有YOLO标签 label_dir labels bad_files [] for f in os.listdir(label_dir): with open(os.path.join(label_dir, f)) as fp: for line in fp: parts line.strip().split() if len(parts) ! 5: bad_files.append(f{f} 字段数不对) break vals [float(v) for v in parts[1:]] if not all(0 v 1 for v in vals): bad_files.append(f{f} 坐标超出范围) break if vals[2] 0 or vals[3] 0: bad_files.append(f{f} 宽高异常) break if bad_files: for msg in bad_files[:10]: print(msg) else: print(全部标签格式合法)这步不做直接开训练大概率会出现训练了十几个epoch发现mAP一直为0的情况——不是网络问题是数据喂错了。3. YOLO11三平台训练GPU/CPU/Mac的脚本适配与参数差异3.1 一键脚本的骨架平台检测加统一入口标题里的“一键训练脚本”听起来很玄学本质就是一个Python脚本先检测当前设备可用的计算平台再根据平台设置不同的device参数和batch大小。YOLO11基于ultralytics框架训练入口是model.train()天然支持device0GPU、devicecpuCPU、devicempsMac三种模式。写脚本时我一般先做一个设备检测函数把平台差异封装在同一个入口后面这样用户不需要懂底层细节。import torch from ultralytics import YOLO # 需要pip install ultralytics def detect_device(): # 返回当前最优可用平台 if torch.cuda.is_available(): return 0 # GPU用显卡编号 elif torch.backends.mps.is_available(): return mps # Mac的Metal性能着色器 else: return cpu device detect_device() print(f当前训练平台: {device})这段代码放在训练脚本最前面后面的所有参数都依赖这个变量。GPU的0不是随便写的它对应nvidia-smi里显卡的索引多卡机器上0,1可以指定多卡并行Mac的mps是PyTorch在Apple Silicon上调用Metal的入口CPU不需要写编号直接传字符串cpu。这个检测逻辑每台机器只需要跑一次但无数人在这个环节翻车尤其是Windows上装了CPU版PyTorchtorch.cuda.is_available()永远返回False结果一直用CPU在硬跑。3.2 训练参数按平台分组batch和epoch怎么定不同平台的显存和计算能力天差地别训练参数必须分开设置。GPU上batch大小可以设16或32CPU上只能设4或8Mac的MPS建议8或16。epoch数量其实和平台关系不大但和数据集规模关系很大——1000张图训练100个epoch大约等于10万次迭代对YOLO11来说是一个能收敛的下限如果想看效果最少也要50个epoch起步。# 根据平台设置不同的训练参数 platform_configs { 0: {batch: 16, workers: 4, imgsz: 640}, # GPU可用显存决定batch上限 mps: {batch: 8, workers: 2, imgsz: 640}, # MacMPS内存共享需保守 cpu: {batch: 4, workers: 0, imgsz: 640}, # CPUworkers设0防止数据加载阻塞 } config platform_configs[device] print(f训练配置: batch{config[batch]}, workers{config[workers]})workers这个参数极易被忽略。GPU上有独立的显存带宽CPU负责把数据送到显存4个worker并行加载很顺畅但在纯CPU训练时workers如果设成4内存会瞬间被打满训练反而更慢。我一般在CPU平台直接设0让数据加载在主进程内同步进行慢但稳定。Mac的workers设2就够了因为MPS和CPU共享内存加载线程太多会把内存带宽吃满。3.3 完整的最小训练脚本直接抄作业把上面的逻辑串起来就是一个跨三平台的完整训练脚本总行数不超过40行包含数据加载、模型初始化、训练、验证、导出五个环节。这里注意data.yaml的路径必须用绝对路径或相对于脚本的位置ultralytics框架对相对路径的处理比较坑经常出现“文件明明在你的数据集里但它找不到”的玄学问题。import torch from ultralytics import YOLO def detect_device(): if torch.cuda.is_available(): return 0 elif torch.backends.mps.is_available(): return mps return cpu device detect_device() if __name__ __main__: # 加载预训练权重yolo11n.pt是最轻量的适合小数据集 model YOLO(yolo11n.pt) # 数据配置文件三条路径分别是训练图、验证图、类别名列表 data_yaml dataset/fight.yaml # 训练参数 model.train( datadata_yaml, epochs100, batch16 if device ! cpu else 4, imgsz640, devicedevice, workers4 if device ! cpu else 0, patience20, # 早停20个epoch没提升就停止 projectfight_run, nameexp1, pretrainedTrue, # 使用预训练权重做迁移学习 )参数说明epochs对1000张图的数据集100是标准值低于50基本学不到打架的语义特征imgsz640是YOLO11的默认输入分辨率如果你的图片本身很大比如2048像素的监控截图可以先不降分辨率训练效果会更好但显存消耗翻倍patience20是早停机制防止你睡一觉起来发现loss已经震荡了30个epoch还在跑。pretrainedTrue使用的是COCO预训练权重虽然COCO没有打架类别但底层特征边缘、纹理、人体轮廓是迁移的训练速度比从零开始快得多。3.4 Mac平台特有的坑MPS内存泄露和回退Mac上用MPS训练YOLO11有两个高频坑。第一个是内存持续增长——PyTorch的MPS后端偶尔会积累中间计算的缓冲不释放跑着跑着内存从4GB涨到10GB然后系统卡死。我的经验做法是显式开启PYTORCH_ENABLE_MPS_FALLBACK1环境变量让不支持MPS的算子自动回退到CPU虽然慢一点但不会崩。第二个是torch.mps.empty_cache()在每一轮epoch结束时手动调用一次强制清理缓存。import os # 在脚本最顶部设置MPS回退避免算子不支持导致的崩溃 os.environ[PYTORCH_ENABLE_MPS_FALLBACK] 1另外ultralytics在Mac上默认用mps训练时有些版本存在loss为NaN的问题。遇到这种情况直接把batch降到4或者用devicecpu试一下如果CPU能正常收敛而MPS不收敛那就是MPS后端的浮点精度问题换CPU训练保平安。4. 从数据集到YOLO11配置YAML文件与目录结构4.1 数据集的目录组织一张图必须对应一个标签YOLO11要求的数据目录结构非常固定常见的组织方式是在数据集根目录下分三个文件夹images/train、images/val、labels/train、labels/val。很多数据集自带标签但目录结构是扁平的需要自己划分训练集和验证集。划分比例一般按8:2或9:1但打架检测这种类别不均衡的数据验证集不能太小否则mAP的波动会很大。# 创建YOLO11需要的目录结构 mkdir -p dataset/images/train dataset/images/val mkdir -p dataset/labels/train dataset/labels/val # 将前800张图放入训练集假设当前目录有images和labels两个原始文件夹 ls images | head -n 800 | xargs -I {} cp images/{} dataset/images/train/ ls images | tail -n 200 | xargs -I {} cp images/{} dataset/images/val/ # 对应的标签文件同步移动 ls labels | head -n 800 | xargs -I {} cp labels/{} dataset/labels/train/ ls labels | tail -n 200 | xargs -I {} cp labels/{} dataset/labels/val/这里必须强调图片和标签的文件名必须一一对应img_001.jpg必须配img_001.txt扩展名不同没关系前缀必须完全一致。YOLO训练时按文件名匹配图片和标签后缀对不上会直接跳过该图。上面这个shell命令用head和tail切分数据如果之前ls的顺序和标签顺序不一致就会导致图片进了训练集而标签进了验证集——训练时每张图都没有标签loss能正常计算但模型什么都学不到。4.2 fight.yaml的编写类名和路径是唯二关键YOLO11的配置文件是一个YAML文件最少只需要四行内容path是数据集根目录train和val是相对或绝对路径names是一个类别名列表。打架检测数据集的类别通常就两类fight和normal或者fighting和peaceful具体看数据集里的标注。这里有个隐藏规则names的索引必须和标签文件里class_id的顺序一致——第一个名字对应0第二个对应1顺序错了模型会把“打架”学成“正常”。# dataset/fight.yaml path: /absolute/path/to/dataset # 建议用绝对路径相对路径容易出问题 train: images/train val: images/val names: 0: fight 1: normal这个YAML文件的解析逻辑很简单ultralytics读取path后自动拼接train和val路径然后扫描对应目录下的图片和标签。如果路径写错报错信息是“No labels found in train set”很多新手以为数据没标签或者转换失败实际上就是YAML里路径不对。我一般会先cd到数据集上一级目录用pwd拿到绝对路径再填进去。4.3 第一次训练的完整命令与预期输出对纯新手来说不要直接改脚本参数先用最保守的设置跑一次完整流程验证环境和数据都没有问题。上述脚本保存为train.py后在数据集目录下执行cd dataset python train.py如果一切正常第一轮epoch结束时会打印train loss和val loss的值大概在2到5之间交叉熵加边框损失看具体实现。训练过程中每过一段时间会输出一张验证集的可视化预测图你马上能看出模型是否学到东西——如果框画在人的上半身附近说明已经开始收敛如果框满天飞说明标签转换有问题或者anchor设置不对。100个epoch在GPU上大约需要30到60分钟取决于显卡型号CPU上可能要10小时以上Mac的MPS大约在2到4小时之间。这个时间预期很重要能避免你半夜爬起来看进度时怀疑人生。5. 训练与标签的5个典型坑现象、原因、解决5.1 训练时loss直接为NaN学习率撞上小数据集现象训练了十几个steploss从5直接跳成nan之后一直是nan验证集mAP为0。原因小数据集上模型收敛速度快学习率0.01对1000张图可能过大梯度爆炸后权重全部变成NaN。解决把lr0从默认的0.01降到0.001lrf最终学习率因子从0.01降到0.001通常就能恢复正常。5.2 验证时mAP一直为0类别索引错位现象训练正常loss在降但每轮验证mAP都是0输出的预测框类别永远是一个。原因标签里写的类别ID是0但fight.yaml里names第一个是normal模型把“打架”学成了“正常”验证时计算mAP用的类别索引对不上。解决打开一个标签txt确认第一行第一个数字再对照fight.yaml里的names顺序把索引纠正过来重训。5.3 数据加载慢且CPU占用居高不下workers设错了现象训练循环每次都要等很久才开始系统监视器显示CPU满载但GPU利用率只有10%。原因数据加载线程数workers和机器核数不匹配或者在小数据集上workers太多导致调度开销大于加载收益。解决把workers设为0或2观察GPU利用率是否上升。完全用CPU训练时workers0反而是最优的——数据已经在内存里多线程读取反而引入额外竞争。5.4 Mac训练内存持续增长最终卡死MPS缓存泄漏现象Mac mini或MacBook ProMPS训练到第30个epoch系统内存被吃满鼠标开始转圈。原因PyTorch的MPS后端存在累积缓冲不释放的问题尤其在batch16时更明显。解决在训练循环外显式加上torch.mps.empty_cache()并在脚本顶部设置PYTORCH_ENABLE_MPS_FALLBACK1让不支持的算子自动回退CPU。如果还是涨直接在脚本里把batch降到8牺牲一点速度换取稳定。5.5 验证集有图但报“No labels found”路径拼接错误现象训练时报No labels found in /dataset/images/val但打开文件夹明明有图有标签。原因ultralytics在解析YAML时train和val前面如果写的是./images/val会从当前工作目录找而不是从path拼接。解决YAML里直接写相对路径images/val不要带./同时确保工作目录在path的上一级。这个坑很小但出现频率极高属于“看报错以为数据坏了实际是路径少了一层”。6. 训练完怎么验证模型效果可视化、指标与阈值调整100个epoch训完后模型文件保存在fight_run/exp1/weights/best.pt验证集loss最低的权重不是最后一个epoch。先用最简单的代码验证它在你自己的测试图上能不能出框from ultralytics import YOLO # 加载训练好的权重 model YOLO(fight_run/exp1/weights/best.pt) # 在测试图片上推理 results model.predict( sourcetest_images/, # 放几张没参与训练的图 conf0.25, # 置信度阈值低于此值不画框 saveTrue, # 保存结果图 device0 if torch.cuda.is_available() else cpu, )conf0.25是默认阈值但打架检测有个特殊性肢体重叠严重时正确框的置信度可能只有0.1到0.3。所以我会先看results[0].boxes.conf的输出如果打架场景的置信度普遍低于0.25再降阈值到0.1左右重新跑一次。置信度阈值不是越高越好它本质上是“精确率/召回率”的旋钮——阈值高出来的框少但准阈值低框多但误检也多。安防场景建议先保召回用conf0.1多画一些框再靠后续的跟踪模块过滤。验证集指标里mAP50和mAP50-95是两个核心数字。mAP50是IoU阈值0.5时的平均精度打架检测这种动作密集场景IoU本身波动大mAP50在0.6以上就算有实际意义的模型了mAP50-95更严格它综合了0.5到0.95的多个IoU阈值小数据集上0.3到0.5都很正常。如果你的mAP50到了0.7以上说明1000张图的数据质量不错这个模型完全可以拿去接监控视频流做实时告警的PoC如果只有0.3左右先别急着调参回去看标签——大概率是画框的时候把人的上半身框得太紧打架动作中肢体大幅摆动框和真实范围对不上。最后一招是数据增强。1000张图的分布有限给model.train()加几个参数就能扩大有效数据量hsv_h0.015色调扰动、fliplr0.5水平翻转、scale0.5缩放扰动、mosaic1.0马赛克拼接。这些参数在ultralytics里是默认开启的但很多人不知道它们可以用命令行调大。我自己的习惯是训练时把mosaic1.0保持默认推理阶段关掉——因为训练时马赛克能让模型学到跨场景的上下文但推理时不需要这个逻辑。如果模型在验证集表现不错但拿去真监控画面里框不准还需再走一步收集几十张目标场景的真实截图做二次标注加进数据集的train和val里做增量训练。1000张图的数据集注定做不到开箱即用它真正帮你验证的是“这套工程链路能不能跑通、哪里会卡住、流程顺不顺”剩下的就是数据本身的积累了。希望这些踩坑记录能帮你少走几步弯路。本文还有配套的精品资源点击获取
返回列表