简介:本数据集面向安防监控、公共场景智能检测方向的算法工程师与深度学习学习者,提供可直接用于目标检测训练与验证的标注数据,覆盖刀具、手枪、纸币、钱包、智能手机、卡片等六类公共场所常见危险或敏感物品。资源共2000个文件,以1431个Pascal VOC格式xml标注文件和569个YOLO格式txt标注文件为主,另含1431张jpg原图,压缩包约126.77MB,两种标注格式可分别对接不同检测框架,省去格式转换环节。全部标注由labelImg完成,采用矩形框方式,共1497个标注框,其中knife类别框数最多达1035个,其余类别分布相对稀疏,便于开展类别不平衡场景下的实验对比。目前已有330人学习下载。数据集仅提供准确合理的标注,不对训练所得模型或权重精度作任何保证,适合用于课程设计、算法验证与数据增强等实践环节。
1. 公共场所危险物品检测数据集:1431 张 VOC+YOLO 双格式到底能训出什么
安检口传送带上的包,最怕的不是刀,是那种混在充电宝和钥匙串里、只露出一个角的东西。公共场所危险物品检测这个方向,难就难在目标小、遮挡重、类别间长得像,而公开可用的高质量数据又少。这份 1431 张、6 类别的 VOC+YOLO 双格式数据集,解决的就是“从零起步没有标注数据”的问题——它把 XML 和 TXT 两套标注都给你备好了,省掉格式转换那一步。适合谁?适合想快速验证 YOLO 训练链路的学生、需要给安检/园区场景做原型验证的工程师,以及拿它当 baseline 再扩自己数据的人。1431 张不算大,但作为冷启动的第一批燃料,够用。
2. 先搞懂 VOC 与 YOLO 双格式:为什么同一批图要存两套标注
2.1 VOC 的 XML 和 YOLO 的 TXT 到底差在哪
VOC 格式的标注是一个图对应一个 XML 文件,里面用<object>节点记录每个目标的类别名和边界框的左上角、右下角绝对像素坐标。YOLO 格式则是一个图对应一个 TXT,每行一个目标,格式是类别索引 中心x 中心y 宽 高,后四个值全部除以图片宽高归一化到 0~1。两者最本质的区别:VOC 存的是绝对坐标加类别字符串,YOLO 存的是归一化坐标加类别整数索引。这个差异决定了你不能直接把 XML 丢给 YOLO 训练脚本,必须先转。
为什么这份数据集要同时给两套?因为工具链是分裂的。LabelImg、部分可视化脚本、某些传统检测框架吃 XML;而 Ultralytics 系的 YOLOv5/v8/v11 训练只认 TXT 加一个data.yaml。双格式意味着你拿到手就能分别喂给两套流程,不用自己写转换再担心坐标算错。常见做法是:用 VOC 做标注复核和可视化,用 YOLO 格式直接开训。
2.2 目录结构怎么摆才不出错
解压后你大概率会看到Annotations(XML)、JPEGImages(原图)、ImageSets/Main(划分文件)这一套 VOC 骨架,外加一个labels目录放 TXT。别急着改目录名,先按下面这个结构核对一遍,缺哪个补哪个:
dataset/ ├── Annotations/ # VOC 的 XML,一图一文件 ├── JPEGImages/ # 所有原图,jpg/png 混放也行 ├── labels/ # YOLO 的 TXT,文件名必须与图同名 ├── ImageSets/Main/ # train.txt / val.txt,只写文件名不带后缀 └── data.yaml # YOLO 训练配置关键点:labels里的 TXT 文件名必须和JPEGImages里的图完全同名,只是后缀不同。ImageSets/Main/train.txt里每行只写xxx(不带.jpg),这是 VOC 的老规矩,很多转换脚本默认按这个读。如果你发现 TXT 和图片对不上号,九成是文件名大小写或者后缀不一致,先ls两个目录对比一遍再往下走。
2.3 6 个类别与类别索引的映射关系
6 类别意味着data.yaml里nc: 6,names列表顺序必须和 TXT 里用的整数索引严格对应。索引错一位,模型学出来的就是“把 A 认成 B”,而且 loss 还会正常下降,属于最阴的坑。拿到数据集第一件事不是开训,是确认索引映射。常见做法是写个脚本统计所有 TXT 里出现过的类别索引,看最大值是不是 5、有没有跳号:
import os, glob from collections import Counter label_dir = "dataset/labels" counter = Counter() for txt in glob.glob(os.path.join(label_dir, "*.txt")): with open(txt) as f: for line in f: line = line.strip() if line: cls_id = int(line.split()[0]) # 每行第一个字段是类别索引 counter[cls_id] += 1 print("出现的类别索引及数量:", dict(sorted(counter.items()))) # 期望看到 0~5 全部出现,且没有 6 及以上的值这段脚本遍历所有 TXT,把每行第一个字段(类别索引)抽出来计数。如果输出里最大索引是 5 且 0~5 都有样本,说明索引连续、映射正常;如果出现 6 或负数,说明标注里有脏数据或者类别数不是 6,得回去查。参数上没什么可调的,label_dir换成你的实际路径即可。跑完这一步,你才对“6 类别”这件事有了实证,而不是只信文件名。
3. 用这份数据集跑通 YOLO 训练的最小闭环
3.1 从 VOC 的 XML 转出 YOLO 的 TXT
虽然数据集号称双格式,但你自己扩标的数据往往只有 XML,所以转换脚本必须会写。核心是把绝对坐标转成归一化中心点坐标,并且把类别名映射成索引。下面这个脚本处理单张 XML,逻辑清晰、方便你改成批量:
import xml.etree.ElementTree as ET from PIL import Image # 类别名到索引的映射,顺序必须和 data.yaml 的 names 一致 CLASS_MAP = {"knife": 0, "scissors": 1, "lighter": 2, "spray": 3, "battery": 4, "hammer": 5} def voc_to_yolo(xml_path, img_path, out_txt): tree = ET.parse(xml_path) root = tree.getroot() w = int(root.find("size/width").text) # 图片真实宽度 h = int(root.find("size/height").text) # 图片真实高度 lines = [] for obj in root.findall("object"): name = obj.find("name").text.strip() if name not in CLASS_MAP: continue # 跳过未定义类别,避免索引错乱 cls_id = CLASS_MAP[name] bnd = obj.find("bndbox") xmin = float(bnd.find("xmin").text) ymin = float(bnd.find("ymin").text) xmax = float(bnd.find("xmax").text) ymax = float(bnd.find("ymax").text) # 转成归一化的中心点 + 宽高 cx = (xmin + xmax) / 2.0 / w cy = (ymin + ymax) / 2.0 / h bw = (xmax - xmin) / w bh = (ymax - ymin) / h lines.append(f"{cls_id} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}") with open(out_txt, "w") as f: f.write("\n".join(lines))逻辑说明:先读 XML 里的图片宽高,这是归一化的分母,读错整个框就飘了。然后遍历每个object,用CLASS_MAP把类别名换成索引,没在映射表里的直接跳过——这一步是防止你扩标时手滑写了个新类别名,导致索引越界。坐标转换公式就是(xmin+xmax)/2/w这一套,保留 6 位小数足够。参数上,CLASS_MAP必须和data.yaml的names顺序一模一样,这是唯一需要你手动对齐的地方。批量跑的时候套个glob循环,把out_txt的路径指到labels/下同名文件即可。
3.2 data.yaml 的四个必填字段与路径写法
YOLO 训练读的是data.yaml,写错路径是最常见的“训练启动即报错”。四个字段:path(数据集根目录)、train(训练集图片列表或目录)、val(验证集)、names(类别名列表)。路径写法有两种:绝对路径最省心,相对路径则相对于path字段解析。我一般用绝对路径,避免在path和train之间绕晕:
path: /data/danger_dataset # 数据集根目录,绝对路径 train: images/train # 相对 path 的训练图目录 val: images/val # 相对 path 的验证图目录 nc: 6 # 类别数,必须和 names 长度一致 names: # 顺序即类别索引,不能乱 0: knife 1: scissors 2: lighter 3: spray 4: battery 5: hammer注意nc和names长度必须相等,写 6 就得有 6 个名字。train和val如果指向目录,YOLO 会自动扫描目录下所有图片并去找同名 TXT;如果指向 txt 列表文件,则按列表读。两种都行,目录方式更省事。改完 yaml 别急着训,先用python -c "import yaml; print(yaml.safe_load(open('data.yaml')))"确认能正常解析,YAML 对缩进极其敏感,多一个空格就报错。
3.3 启动训练与关键超参怎么设
最小闭环命令就一行,但参数决定你能不能收敛:
yolo detect train \ data=data.yaml \ model=yolov8n.pt \ epochs=100 \ imgsz=640 \ batch=16 \ lr0=0.01 \ patience=20逐项说:model=yolov8n.pt用 nano 版,1431 张图这个量级,大模型直接过拟合,nano 或 s 版足够。imgsz=640是 YOLO 的默认输入尺寸,危险物品目标偏小,如果你显存够,可以试 800 甚至 1024,小目标召回会好一些,但速度掉得明显。batch=16看显存调,8G 显存跑 640 一般能到 16,爆显存就降到 8。lr0=0.01是初始学习率,小数据集别设太大,0.01 是稳妥起点,loss 震荡就降到 0.005。patience=20是早停,20 轮验证指标不涨就停,防止白跑。1431 张图、6 类别,100 轮在单卡上大概几十分钟到一两小时,取决于卡。跑完看runs/detect/train/下的results.png,重点看mAP50和mAP50-95两条曲线是否还在涨。
4. 小数据集训练危险物品检测的避坑与排查
4.1 类别索引错位:loss 正常降但 mAP 极低
现象:训练 loss 一路下降,看起来很正常,但验证集 mAP 常年在 0.1 以下,模型预测的框位置对但类别全乱。原因:TXT 里的类别索引和data.yaml的names顺序没对齐,比如 TXT 里 0 是 knife,yaml 里 0 写成了 scissors。解决:跑 2.3 节那段统计脚本,把 TXT 里实际出现的索引和 yaml 的 names 逐一对一遍,顺序错位就改 yaml,索引跳号就回去查标注。这个坑最阴的地方在于 loss 不会报错,只能靠 mAP 异常发现。
4.2 空标注文件导致训练中断
现象:训练启动后报IndexError或ZeroDivisionError,或者某个 batch 直接崩。原因:有些图片没有目标,对应的 TXT 是空文件,YOLO 在读空 TXT 时某些版本会出问题。解决:先扫一遍labels/下有没有 0 字节的 TXT,有的话要么删掉对应图片,要么在 TXT 里留一行占位(不推荐)。更稳的做法是训练前统一过滤:
find dataset/labels -name "*.txt" -size 0 -print # 列出所有空标注,确认后决定删除还是补标4.3 图片与标注文件名不匹配
现象:训练能启动,但日志里train的图片数量远少于预期,或者验证时找不到标签。原因:labels/里的 TXT 和JPEGImages/里的图不同名,常见于批量重命名时后缀没统一(.jpgvs.JPG)。解决:写个脚本对比两个目录的文件名集合,差集就是问题文件。Linux 下文件名大小写敏感,Windows 拷过来的数据尤其容易中招。
4.4 小目标漏检严重:先查标注框尺寸再调模型
现象:模型对大目标检测正常,但小目标(比如远处的打火机)几乎全漏。原因:1431 张图里小目标本身像素就少,640 输入下经过下采样后特征几乎消失。解决:先统计标注框的宽高分布,确认小目标占比;如果确实小目标多,把imgsz提到 800 或 1024,同时开mosaic增强(YOLO 默认开)。别一上来就换模型结构,先把输入分辨率和增强调到位,性价比最高。
4.5 验证集划分不合理导致指标虚高
现象:mAP 看着不错,但换一批新图测试就崩。原因:train.txt和val.txt划分时把同一场景、同一批次的图分到了两边,验证集和训练集高度相似,指标虚高。解决:按场景或拍摄批次划分,而不是随机按比例切。1431 张如果来自几个不同场景,确保每个场景在训练和验证里都有,这样验证指标才可信。随机切分在小数据集上特别容易骗自己。
5. 把 1431 张用出 3000 张效果:增强、扩标与验证技巧
1431 张、6 类别,平均每类两百多张,直接训能出 baseline,但想上生产远远不够。我的习惯是先把增强拉满再谈扩标。YOLO 自带的mosaic、mixup、hsv增强对小数据集提升明显,mosaic=1.0默认开,mixup可以设 0.1~0.2,别太高否则小目标更糊。degrees旋转增强对危险物品要慎用,刀和锤子旋转后语义还在,但打火机转 180 度可能就不像了,建议degrees=10以内。scale缩放增强可以开到 0.5,模拟远近变化,对小目标召回有帮助。
扩标比调参划算。1431 张里挑出模型漏检和误检的图,用 LabelImg 补标,优先补小目标和遮挡目标,补 300~500 张往往比调一周参管用。补标时严格沿用原来的 6 类索引,别新增类别,否则nc和names全要改。补完重新跑 3.1 的转换脚本,把新 TXT 并进labels/。
验证环节别只看 mAP。危险物品检测的漏检代价远大于误检,所以重点看召回率。训练完在验证集上跑yolo detect val,看metrics/recall这一列,如果召回低于 0.8,优先补小目标标注而不是调阈值。另外做个混淆矩阵,看哪两类最容易混——比如 lighter 和 battery 如果经常互认,说明特征太像,得靠更多样本或更高分辨率区分。
最后说个我踩过的坑:有次为了冲 mAP,把imgsz从 640 提到 1280,结果训练时间翻三倍,mAP 只涨了 0.02,小目标召回倒是涨了 0.05。值不值取决于你的场景——如果漏检一个小目标就是事故,那值;如果只是做个 demo,640 够了。别盲目追高分辨率,先算清楚你的算力预算和业务容忍度。希望帮到你。
本文还有配套的精品资源,点击获取