ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

基于9100张YOLO格式数据集的安防异常行为检测实战指南

基于9100张YOLO格式数据集的安防异常行为检测实战指南 1. 安防监控场景下的异常行为检测这个数据集到底能做什么安防监控这个领域做算法的人都有一个共识模型结构可以复现训练技巧可以学但数据这件事往往才是真正卡住项目进度的瓶颈。我见过太多团队在YOLO系列模型上折腾了半天从YOLOv5换到YOLOv8又去试YOLOv10、YOLO11结果mAP涨了两个点就沾沾自喜回头一看业务场景里的漏报率还是高得离谱。问题出在哪大概率不是模型不行而是数据集的场景覆盖度和标注质量没跟上。这次要聊的是一个9100张规模的YOLO格式安防监控异常行为检测数据集。先把这个标题拆开来看核心领域是安防监控核心任务是异常行为检测数据格式是YOLO标注格式数据量是9100张图像。这四个信息点基本决定了一个目标检测项目从数据侧能走多远。异常行为检测在安防场景里具体指什么不是泛泛的“有人出现就算异常”而是有明确行为类别的判定比如攀爬翻越、人员聚集、倒地、奔跑、徘徊、物品遗留、打架斗殴这类需要触发告警的行为。这些类别和普通的“人、车、物”检测有本质区别——它们的类间差异小、类内差异大同一个“倒地”动作在不同监控视角、不同光照、不同遮挡条件下视觉特征可能完全不同。所以这类数据集的价值不在于图片数量堆到多少万而在于场景多样性、视角覆盖度、标注一致性这三件事有没有做到位。9100张这个量级说实话不算大。现在动辄几十万张的公开数据集比比皆是但那些数据集大多是通用目标检测COCO、Objects365这类类别是“人、车、狗、椅子”跟安防异常行为根本不搭边。真正能直接拿来训练异常行为检测的、标注规范的、YOLO格式开箱即用的数据集市面上并不多。9100张如果场景切分合理、类别定义清晰、标注框贴合度高完全够训练出一个在特定场景下可用的基线模型尤其是配合预训练权重做迁移学习的时候。这篇文章适合谁看如果你是安防算法工程师正在找可直接训练的数据集或者想了解异常行为检测的数据组织方式如果你是CV方向的学生想拿一个真实场景的数据集练手YOLO训练全流程如果你是产品/项目经理想评估异常行为检测落地的数据门槛和周期——那这篇内容应该能给你一些实际参考。我会从数据集的结构设计、标注规范、训练配置、常见坑这几个维度展开尽量把“为什么这么做”讲透而不是只丢一堆参数。2. 数据集整体设计与标注思路拆解2.1 为什么异常行为检测不能直接套用通用检测数据集通用目标检测数据集和安防异常行为数据集表面上看都是“画框打标签”但底层逻辑差别很大。通用数据集追求的是类别覆盖广度一张图里可能有十几个类别每个类别的实例数分布相对均匀。而异常行为数据集追求的是行为判定的准确性一张监控画面里可能只有一个人但这个人的姿态决定了它属于“正常行走”还是“异常徘徊”。这就带来一个关键问题标注的边界在哪里。举个例子“徘徊”这个行为一个人在同一区域来回走动超过一定时间算徘徊那走了两趟算不算停留了30秒算不算这些判定标准如果不在标注规范里写清楚不同标注员给出的结果会天差地别。我见过一个项目同样的视频片段三个标注员标出来的“徘徊”样本重合率只有60%多这种数据训出来的模型混淆矩阵里“徘徊”和“正常行走”的误判率必然高。所以这个9100张数据集在设计时大概率需要遵循一个原则行为类别定义必须可操作化。不能只写“徘徊”而要写成“同一目标在固定区域内往返移动超过2次且单次停留时间大于3秒”。这种定义虽然看起来啰嗦但它是保证标注一致性的前提。2.2 YOLO格式标注的利与弊YOLO格式的标注文件是.txt每行一个目标格式是class_id x_center y_center width height坐标全部归一化到0-1之间。这个格式最大的好处是轻量、解析快、和YOLO系列训练代码无缝对接。你拿到数据集直接配好data.yaml就能开训不需要像COCO格式那样先转JSON再转中间格式。但YOLO格式也有它的局限。它只支持水平矩形框不支持旋转框、多边形分割、关键点。对于异常行为检测来说有些行为用矩形框标注其实是不够精确的。比如“倒地”这个行为人的身体是横向延展的矩形框会包含大量背景区域再比如“攀爬”人的肢体是斜向伸展的矩形框的冗余更大。如果数据集里这类样本占比高模型学到的特征里会混入很多背景噪声。不过话说回来YOLO格式的检测框虽然粗糙但对于触发告警这个业务目标来说很多时候够用了。安防场景要的是“发现异常并框出大致位置”不是精确到像素级的姿态估计。所以这个数据集选择YOLO格式从工程落地角度是合理的——训练成本低、推理速度快、部署链路成熟。2.3 9100张的类别分布与场景切分逻辑9100张图如果按异常行为类别来分我推测大致会覆盖以下几类人员异常行为倒地、攀爬、奔跑、打架、区域异常状态人员聚集、物品遗留、区域入侵、特定目标异常如监控画面中的异常物体出现。具体类别数可能在5到15类之间类别太少会导致模型泛化能力不足类别太多则每类的样本量会被稀释。这里有一个经验值可以参考每个类别至少要有500到800个实例模型才能学到比较稳定的特征。如果某个类别只有一两百个实例训练时很容易被其他类别的梯度淹没最终表现为该类别的召回率极低。9100张图如果分10个类别平均每类910张扣除背景图和难样本实际每类可用实例可能在600到800之间这个量级做迁移学习是够的。场景切分方面安防监控数据集必须考虑视角多样性。固定枪机、球机巡航、半球机、不同安装高度2.5米到5米、不同俯仰角这些都会影响目标的视觉尺度。如果数据集里全是同一个摄像头拍的模型换一个点位就废了。所以9100张里我建议至少覆盖8到12个不同场景每个场景贡献700到1000张这样训练出来的模型才有跨场景迁移的底子。3. 核心细节解析与实操要点3.1 标注规范异常行为类别的可操作化定义标注规范是数据集的灵魂。我见过太多数据集图片质量不错但标注文件打开一看同一个行为在不同图片里的标注逻辑不一致这种数据训出来的模型评估指标看着还行一上业务就露馅。对于异常行为检测标注规范至少要明确三件事类别定义、标注触发条件、边界框绘制规则。类别定义要写到“可判定”的程度。比如“人员聚集”不能只写“多人聚集”而要写“同一画面内人员间距小于1.5米且人数大于等于3人持续存在”。再比如“物品遗留”要写“静止物体在公共区域停留超过设定时间阈值且周围无人员看管”。这些阈值虽然在实际部署时可以调整但在标注阶段必须固定下来否则标注员无法执行。标注触发条件指的是“什么情况下必须标什么情况下可以不标”。比如画面边缘只露出半个身子的人算不算一个实例夜间红外模式下目标模糊标不标这些边界情况如果不提前约定标注结果会非常混乱。我的经验是宁可漏标不要错标。漏标可以通过后续难例挖掘补回来错标会直接污染训练集。边界框绘制规则主要针对遮挡和截断。两个目标重叠时框是各画各的还是只画可见部分我的建议是按可见区域画框同时保留一个occlusion标记位如果格式支持的话。YOLO格式本身不支持额外标记那就需要在文件命名或目录结构上做区分比如把遮挡样本单独放一个子目录。3.2 数据清洗9100张里有多少是“无效样本”拿到一个数据集第一件事不是直接开训而是做数据体检。9100张图里通常会有10%到20%的样本存在各种问题重复帧、模糊帧、标注框越界、类别标签错误、空标注文件。重复帧在视频抽帧数据集里特别常见。如果抽帧间隔设得太小相邻两帧几乎一模一样这种样本对训练几乎没有增益反而会让模型过拟合到特定场景。我的做法是用感知哈希pHash做去重汉明距离小于5的视为重复只保留一帧。9100张去重后可能剩7500到8000张这是正常的。模糊帧的判断可以用拉普拉斯方差方差低于阈值的直接剔除。安防监控里夜间低照度画面、雨雪天气画面模糊帧比例会更高这部分数据如果保留模型会学到很多噪声特征。标注框越界是指x_center ± width/2超出0-1范围这种框在训练时会导致坐标回归异常。用脚本批量检查一遍越界的直接修正或剔除。类别标签错误则需要人工抽检按类别分层抽样每类抽50到100张看标注框和类别是否匹配。这一步很枯燥但省不得。3.3 训练集/验证集/测试集切分不能随机切很多教程教人用train_test_split随机切分这在异常行为检测里是大忌。因为监控视频抽帧得到的图片相邻帧高度相似随机切分会导致训练集和验证集里出现几乎相同的图片验证指标虚高实际泛化能力很差。正确的切分方式是按场景切或者按时间段切。比如你有10个摄像头点位那就用8个点位的做训练1个做验证1个做测试。或者按时间切前70%时间段的做训练中间15%做验证最后15%做测试。这样切出来的验证集才能真正反映模型在新场景下的表现。如果数据集本身没有场景或时间标记那就用聚类方法先把图片按视觉特征分组再按组切分。简单一点可以用颜色直方图或者预训练模型提取的特征做KMeans把9100张聚成20到30个簇然后按簇分配。这样能最大程度避免数据泄漏。4. 实操过程与核心环节实现4.1 环境搭建与YOLO训练配置假设我们用的是YOLOv8或者YOLO11环境搭建这块其实已经很成熟了。Python 3.9以上PyTorch 2.0以上CUDA版本根据显卡驱动来定。我一般用conda建一个独立环境避免和系统里的其他包冲突。conda create -n yolo_anomaly python3.10 conda activate yolo_anomaly pip install ultralytics pip install opencv-python pandas matplotlib数据集目录结构按YOLO的标准来组织dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yamldata.yaml的内容大概是这样path: /path/to/dataset train: images/train val: images/val test: images/test names: 0: fall_down 1: climb 2: fight 3: gather 4: run 5: loiter 6: abandoned_object 7: intrusion类别名称根据实际数据集来定这里只是示例。注意names的索引必须和标注文件里的class_id一一对应错一个位整个训练就废了。4.2 预训练权重选择与迁移学习策略9100张这个量级必须用预训练权重。从零开始训模型根本收敛不到可用的程度。YOLO系列官方提供了在COCO上预训练的权重直接加载就行。from ultralytics import YOLO model YOLO(yolov8m.pt) # 加载预训练权重 results model.train( datadata.yaml, epochs100, imgsz640, batch16, device0, workers8, patience20, lr00.01, lrf0.01, momentum0.937, weight_decay0.0005, warmup_epochs3, augmentTrue )模型尺寸选择上yolov8n太小特征提取能力不够异常行为的细粒度特征学不到yolov8x太大9100张数据容易过拟合而且推理速度慢。yolov8m或yolov8l是比较平衡的选择。如果部署端算力有限可以先用m训一版看效果再考虑蒸馏到n。学习率方面迁移学习通常用比从头训练更小的初始学习率。lr00.01是一个比较稳妥的起点如果训练过程中loss震荡厉害可以降到0.005。lrf是最终学习率因子0.01意味着学习率从0.01线性衰减到0.0001。4.3 数据增强哪些增强适合安防场景YOLO默认开启的增强包括Mosaic、HSV色彩空间扰动、随机翻转、随机缩放。这些增强对通用目标检测有效但安防场景需要有选择地使用。Mosaic增强把四张图拼成一张能提升小目标检测能力但异常行为检测里很多目标是中等尺寸Mosaic反而可能引入不合理的上下文关系。我的做法是前期用Mosaic后期关闭。YOLO训练参数里有close_mosaic设成10到15意思是最后10到15个epoch关闭Mosaic让模型在真实分布上微调。HSV增强里色调H扰动要谨慎。安防监控有白天彩色和夜间红外两种模式如果训练集里红外样本少色调扰动可能让模型把颜色当成判别特征。饱和度和亮度扰动可以保留但幅度不要太大。随机翻转要注意行为的方向性。“攀爬”这个行为左右翻转后可能变成另一种行为如果类别定义里没有区分方向翻转没问题如果有方向区分翻转就会制造错误标签。这个要根据具体类别定义来决定。4.4 训练过程监控与关键指标解读训练启动后重点盯几个指标box_loss、cls_loss、mAP50、mAP50-95、precision、recall。box_loss下降说明边界框回归在收敛cls_loss下降说明分类在收敛。如果box_loss降但cls_loss不降可能是类别定义太模糊模型分不清如果cls_loss降但box_loss不降可能是标注框质量有问题。mAP50是IoU阈值0.5时的平均精度这个指标在安防场景里参考价值有限因为0.5的IoU要求太宽松了。重点看mAP50-95它综合了多个IoU阈值更能反映框的贴合度。异常行为检测里框稍微偏一点可能就把背景里的干扰物包进来了所以mAP50-95高才是真的好。precision和recall要结合起来看。安防场景通常更看重recall漏报一个异常行为可能意味着一次安全事故误报只是多派一次人去核实。所以如果precision和recall需要权衡优先保recall。可以通过调整推理时的置信度阈值来实现训练阶段则可以通过cls损失里的正负样本权重来微调。4.5 模型推理与部署前的验证训练完成后用model.val()在测试集上跑一遍得到最终指标。然后拿一些训练集里没出现过的场景图片做人工验证看模型的实际表现。model YOLO(runs/detect/train/weights/best.pt) results model(test_image.jpg, conf0.25, iou0.45) results[0].show()conf是置信度阈值iou是NMS的IoU阈值。安防场景里conf可以设低一点0.2到0.3先把召回拉上来误报可以通过后续的时序滤波比如连续N帧都检测到才触发告警来压制。部署前还要做速度测试。在目标硬件上跑一下FPS如果达不到实时要求通常安防监控要求25FPS以上就要考虑模型量化、TensorRT加速或者换更小的模型。5. 常见问题与排查技巧实录5.1 训练不收敛或loss震荡这是最常见的问题。原因通常有三个学习率太大、batch size太小、数据标注噪声太大。学习率太大表现为loss一开始就飙高然后剧烈震荡。解决办法是把lr0降到0.001甚至更低同时加长warmup_epochs。batch size太小比如小于8会导致梯度估计不稳定loss曲线毛刺很多。如果显存不够可以用梯度累积来模拟大batch。数据标注噪声是最隐蔽的原因。如果loss降到一定程度就下不去了而且验证集指标远低于训练集大概率是标注里有错标。这时候需要把模型预测置信度高但和标注不一致的样本挑出来人工复核。5.2 某些类别召回率极低如果某个类别的recall明显低于其他类别先看该类别的实例数。如果实例数少于300基本可以判定是样本不足。解决办法是针对性补充数据或者用过采样把该类别在训练集里的比例提上来。如果实例数够但recall还是低看类别定义是否和其他类别重叠。比如“奔跑”和“正常行走”在静态图片里可能很难区分模型学不到判别性特征。这时候要么合并类别要么引入时序信息用视频片段而不是单帧。还有一个可能是锚框尺寸不匹配。YOLO的默认锚框是基于COCO数据集统计的如果异常行为目标的尺寸分布和COCO差异大锚框需要重新聚类。YOLOv8已经用了anchor-free这个问题影响小一些但如果是YOLOv5就需要用kmeans重新生成锚框。5.3 误报率高背景类干扰安防场景里误报大多来自背景类干扰。比如树影晃动被误判为人员活动灯光变化被误判为异常物体。这类问题的根源是训练集里负样本背景图不够。解决办法是往训练集里加入纯背景图片数量大概占总数据的5%到10%。这些图片没有任何标注模型会学会把它们预测为背景。YOLO训练时没有对应.txt文件的图片会被自动当作背景图处理。另一个技巧是难例挖掘。用训练好的模型在验证集上跑推理把误报的样本挑出来人工确认后加入训练集重新训练。这个过程迭代两三轮误报率通常能降一半以上。5.4 常见问题速查表问题现象可能原因排查方法解决措施loss不下降学习率太小或标注错误检查lr和标注文件调大lr复核标注loss震荡学习率太大或batch太小观察loss曲线降lr加batch或梯度累积某类recall低样本不足或类别重叠统计类别分布补数据或合并类别误报率高负样本不足分析误报样本加背景图难例挖掘验证指标虚高数据泄漏检查切分方式按场景/时间切分推理速度慢模型太大或未加速测FPS换小模型或TensorRT5.5 几个我踩过的坑第一个坑是标注文件编码问题。有些标注工具导出的.txt文件带BOM头YOLO训练时读取会报错。用utf-8-sig或者批量转成utf-8就行。第二个坑是图片和标注文件名不对应。图片叫img_001.jpg标注叫img_001.txt但中间可能有多余空格或者大小写不一致。训练前用脚本批量检查一遍文件名不匹配的直接报错。第三个坑是类别索引从1开始。YOLO的class_id是从0开始的如果标注工具默认从1开始训练时会出现class_id越界。这个错误很隐蔽因为训练不会直接报错但模型学到的类别会整体偏移。第四个坑是验证集里出现了训练集的重复图片。前面说过按场景切分但有时候不同场景的图片恰好相似还是会有泄漏。可以用pHash再查一遍确保验证集和训练集没有高度相似的图片。6. 数据集扩展与模型迭代的后续思路9100张是一个可用的起点但不是终点。实际项目里模型上线后一定会遇到新的场景、新的异常行为。这时候需要建立一个数据闭环模型推理产生告警人工复核告警把确认的误报和漏报样本加入训练集定期重新训练。这个闭环里主动学习能大幅降低标注成本。具体做法是用当前模型对未标注数据做推理挑出置信度处于中间区间比如0.3到0.7的样本这些是模型最“犹豫”的样本标注价值最高。只标这些比随机标能更快提升模型性能。另外如果业务场景对时序行为有要求比如徘徊、聚集单帧检测是不够的。可以考虑在YOLO检测的基础上加一个轻量时序模块比如用LSTM或者Transformer对连续帧的检测结果做序列建模。YOLO负责“看到什么”时序模块负责“判断是不是异常”。这种两阶段方案在安防场景里落地效果通常比端到端模型更可控。最后说一个实际部署时的经验模型版本管理很重要。每次重新训练都要记录用了哪些数据、什么参数、指标如何。不然过几个月回头看根本不知道线上跑的是哪一版模型。我一般用wandb或者mlflow做实验跟踪简单一点用Excel也行关键是有记录。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进