ARTICLE DETAIL

资讯详情

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

YOLO26目标检测实战:357张警察识别小数据集的训练与避坑指南

YOLO26目标检测实战:357张警察识别小数据集的训练与避坑指南 简介一套面向目标检测学习与安防场景应用的警察/民警识别数据集包含357张带标注图片支持以YOLO26框架训练出区分警察与非警察两类目标的检测模型。数据包共715个文件以357个jpg原图、357个txt标注文件和1个yaml配置为主压缩包体积约13.76MB结构简洁便于直接接入常用检测训练流程。每张图片均配有对应txt标注标注内容与图片文件名一一对应yaml文件定义了类别与路径参数可减少数据预处理工作量。数据场景覆盖多种环境下的民警样本适合用于智慧安防、人员识别等方向的算法验证与课程实践。目前已有25人学习下载作者提供免费技术咨询适合刚接触YOLO目标检测的初学者或需要扩充训练数据的研究者。1. 带标注的警察识别数据集只有357张图先弄清它能干什么带标注的警察民警识别数据集是个能直接喂给目标检测训练流程的数据包357张图片上已经画好边界框标签只有两类警察和非警察。没在小数据上摔过的人容易低估357这个数字——它恰好卡在“预训练权重不够用、从零训练又过拟合”的临界区间而两类样本分布稍微偏一点训练出的模型就只会认警察不认别的。这篇笔记从数据集格式讲起到训练命令、参数取舍、5个常见翻车现场最后落在“怎么验证一个357张小数据集训练出的模型能不能扛真实场景”。适合手里有识别类检测任务、想用少量标注数据快速出模型又不希望被小数据集坑一把的工程师照着做。2. 把警察/非警察数据集接进YOLO26目录约定与标签核验拿到数据集第一步不是直接跑训练而是先搞清楚它长什么样、能不能被训练入口正确读进去。357张图配2个标签听上去简单但格式细节错了训练时要么报错要么静默吃掉错误标签最后得到一张看起来正常、实际全乱的损失曲线。先花20分钟把目录和标注内容核一遍比事后重训划算得多。2.1 YOLO26的输入长什么样txt标签文件与归一化坐标YOLO26的数据组织方式和YOLO系列一脉相承一个图片目录、一个标签目录、一个描述类别与路径的yaml文件。图片和标签靠“同名不同后缀”对应images/train/img_0001.jpg对应labels/train/img_0001.txt。一个txt文件里每一行是一个目标格式是五个数字类别编号、归一化中心点x、中心点y、归一化框宽、归一化框高。0 0.451 0.332 0.264 0.581 0 0.688 0.593 0.211 0.437 1 0.832 0.637 0.170 0.322第一行的含义是类别0警察的边界框中心位于图片宽度的45.1%、高度的33.2%框宽占整张图宽度的26.4%框高占整张图高度的58.1%。这里必须强调“归一化”YOLO系格式不存像素坐标全部换算成0到1之间的小数图片分辨率变了也不用重标。第三行开头的1代表类别1非警察。所谓“非警察”不是背景而是标注者认为“像警察但不是”的负样本目标比如保安、身形相似的便衣路人。有了这层格式理解就能写出对应的data.yaml。放在数据集根目录下内容如下path: /work/datasets/police_dataset train: images/train val: images/val nc: 2 names: 0: police 1: non_police关键参数有三个。path是数据集绝对路径你的数据集解压在哪就写哪train和val是相对path的图片目录YOLO26读取这两个目录后自动去同级labels目录找同名txtnc写2对应两个类别。names的顺序必须和txt里类别编号一致0对应警察1对应非警察。如果你把names里0和1的顺序写反训练依然能跑通但验证集上的AP曲线会错位警察的分数算到非警察头上。有条件的话用cvat或labelimg这类标注工具打开数据集原档看一眼确认框是“目标检测框”而不是“分类标签”。有些从分类数据集转过来的数据会给整张图打一个大框这种框在目标检测里没有意义训练出来的模型永远只能给出全图级别的判断定位能力为零。2.2 训练前必做的标注体检数量、类别、漏标与格式错误数据标注质量在小数据集上的影响被放大得很明显。357张图本来就少如果有20张漏标、30张坐标越界模型的可学信息直接缩水十分之一。我习惯在第一次训练前跑一个脚本把三个东西一次性查完每类目标数量、空标签文件、坐标越界与格式错误的行。from collections import Counter from pathlib import Path labels_dir Path(police_dataset/labels/train) cls_counter Counter() empty_txt [] bad_txt [] for txt in sorted(labels_dir.glob(*.txt)): lines [line.strip() for line in txt.read_text().splitlines() if line.strip()] if not lines: empty_txt.append(txt.name) continue for line in lines: parts line.split() if len(parts) ! 5: bad_txt.append((txt.name, 字段数不为5)) continue try: cls int(parts[0]) x, y, w, h map(float, parts[1:]) except ValueError: bad_txt.append((txt.name, 数字解析失败)) continue if not (0 cls 2): bad_txt.append((txt.name, f类别编号越界: {cls})) if not (0.0 x 1.0 and 0.0 y 1.0): bad_txt.append((txt.name, 中心点坐标越界)) if w 0 or h 0 or w 1.0 or h 1.0: bad_txt.append((txt.name, 宽高异常)) cls_counter[cls] 1 print(类别数量统计:, sorted(cls_counter.items())) print(空标签文件:, len(empty_txt), empty_txt[:5]) print(异常标签行所在文件:, len(bad_txt), bad_txt[:10])这段脚本做的事情很直接读入每个txt的每一行先检查字段个数和数字解析再检查类别编号是否落在0和1之间坐标与宽高是否在合理范围内。统计结果里最需要警惕的是空标签文件——它通常意味着某张图被漏标了而训练时YOLO26会把这张图当作纯背景处理。漏标多了模型会把警察目标在训练时强行当作负样本压下去最后表现为验证集上警察类的召回率极低。数量统计拿到之后还要看两类目标的比例。如果警察类有600个框非警察类只有80个这个比例决定了后续要不要做类别均衡处理。先记下这个数第3章调参时会用到。坐标体检只能发现“格式错误”发现不了“框错位置”。我一般还会挑出5到10张图把标签画回原图用肉眼核对框是否落在人身上、有没有只框半个身体的情况import cv2 from pathlib import Path img_dir Path(police_dataset/images/train) label_dir Path(police_dataset/labels/train) for txt_path in sorted(label_dir.glob(*.txt))[:5]: img cv2.imread(str(img_dir / (txt_path.stem .jpg))) if img is None: print(缺图:, txt_path.name) continue h, w img.shape[:2] for line in txt_path.read_text().splitlines(): cls, cx, cy, bw, bh line.split() cx, cy, bw, bh map(float, (cx, cy, bw, bh)) x1 int((cx - bw / 2) * w) y1 int((cy - bh / 2) * h) x2 int((cx bw / 2) * w) y2 int((cy bh / 2) * h) color (0, 0, 255) if cls 0 else (255, 0, 0) cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) cv2.imwrite(check_ txt_path.stem .jpg, img)输出的check_*.jpg上红色框是警察蓝色框是非警察。重点看三类情况框是不是把整个人包进去了、有没有把路牌或树干框进去、同一张图上该标的人是否全部标了。这种可视化检查在357张的小数据集上特别值钱因为一张错框图对损失函数的污染远大于大样本场景。3. 用YOLO26训出能用的警察检测模型4个命令与3个关键参数格式和标注都体检过了才轮到训练。小数据集的训练节奏和大数据集不一样大数据集可以靠堆epochs慢慢收敛小数据集必须在欠拟合和过拟合之间找到平衡点。参数设得不对要么百来轮还在欠拟合要么中途mAP跳水你还没察觉。3.1 最小可用训练命令与训练日志解读假设环境里已经装好了PyTorch和对应CUDA版本训练入口命令是这种形式pip install -U ultralytics yolo train datapolice_dataset/data.yaml modelyolo26n.pt epochs200 imgsz640 batch16 patience40 seed42我建议先跑这样一份“最朴素”配置之后再按显存和数据分布去调。datapolice_dataset/data.yaml指向第2章的配置文件modelyolo26n.pt使用预训练权重作为初始值而不是从随机初始化开始。357张图对从零训练来说太少必须迁移预训练特征。epochs200是训练轮数上限patience40表示连续40轮验证指标没有提升就提前停。这两个参数是配合使用的200轮是天花板实际多数小数据集在80到150轮之间就触发早停了。训练起来之后别只盯着终端刷屏要看两类信号。一类是box_loss和cls_loss它们应该在开始几十轮快速下降之后趋于平缓另一类是每个epoch末尾打印的mAP50与mAP50-95它们比loss更直观。经常遇到的情况是loss还在稳步下降但mAP已经不动了这说明模型在拟合数据中的噪声而不是学习通用特征早停机制会在这里兜底。训练结束后runs/detect/trainN/weights/目录下会生成两个权重文件。best.pt是验证集上分数最高的权重last.pt是最后一步的权重。小数据集上我永远只用best.pt原因稍后在第4章展开。3.2 三个必调参数epochs、batch、imgsz的取舍针对357张的规模参数不要按默认值硬扛。默认套路是给大数据集设计的直接用会出问题。我整理了一份针对这个小数据集的起点参数表第一次训练就照着这个范围来参数建议起点说明epochs200到300配合patience使用跑不满自动早停patience40连续不提升40轮就停防止后段过拟合imgsz640输入分辨率目标太小可试832但显存开销增加batch16显存不足就降到8低于4则训练极不稳定seed42固定随机种子换机器复现的关键小数据集最容易踩的参数坑是epochs设太大。有人习惯把职业习惯带过来看到357张图直接填epochs500心想反正meta没限制。如果不开早停模型到250轮后几乎必然开始记训练集的噪声验证mAP掉头向下。而把patience设到40大部分情况下200轮上限都跑不满模型会在150轮前后自己停下来。seed参数常被忽略但它直接决定了数据增强和初始权重扰动。不固定seed同一个数据集、同一个命令两次训练的mAP能差出3到5个百分点。357张图数据量小这个波动会被放大所以必须在命令里固定种子。batch的选择则要看显存。16是个常见起点因为常用的消费级显卡跑640分辨率、16batch勉强能放得下。如果显存只有8G启动时报OOM就按下面这个降配组合跑nvidia-smi --query-gpumemory.free --formatcsv yolo train datapolice_dataset/data.yaml modelyolo26n.pt epochs300 imgsz640 batch8 halfTrue seed42 workers4halfTrue让模型用半精度计算显存占用几乎减半代价是训练结果可能有微小波动在检测任务上通常可以忽略。workers4控制数据加载线程数Windows上设成0能避开一些进程抢占问题Linux下保持4到8即可。交错的坑在workers设太大时如果磁盘本身是机械硬盘数据加载会成为瓶颈反而拖慢训练。3.3 验证与导出用val跑出两类指标再选择导出格式训练完不能直接拿去部署先跑一次验证看每个类别单独的APyolo val datapolice_dataset/data.yaml modelruns/detect/train/weights/best.pt splitval这条命令会在best.pt上做推理并和标注对比输出每个类别的precision、recall和AP。对小数据集我更关注单类别的recall而不是总mAP。比如警察类recall要的是90%以上非警察类如果只有60%说明负样本被大量误判成警察按下文第4章的思路去调。验证目录下会生成混淆矩阵图和PR曲线建议都打开看一眼。混淆矩阵可以看出警察类被误判成了哪些类别。确认指标能接受后再决定导出格式。常见做法是导成ONNX方便后续在推理框架里跑yolo export modelruns/detect/train/weights/best.pt formatonnx opset12 imgsz640导出后的best.onnx不携带类别名推理端拿到的输出只有两个类别的置信度分数。部署时要把0和1映射回警察和非警察这个映射表要单独保存在推理代码里。有些项目图省事直接在导出后的模型里猜类别名结果上线后输出全反了这属于部署环节最不值当的翻车。ONNX导出后可以顺手用onnxruntime跑一次单张图确认输出张量尺寸是1x2xN的格式N是建议框数量再进入后续部署流程。4. 警察识别数据集训练避坑357张小数据集的5种翻车现场这部分是血泪经验汇总。小数据集训练出的问题症状高度趋同但原因各不相同。我把实际工作中最常见的5个现象、原因和解决办法列出来每条都对得上357张这个量级的警察识别任务。4.1 现象Loss正常下降但验证mAP一直上不去训练过程中box_loss和cls_loss都很正常地下降看着就要收敛了但验证集上的mAP50始终在0.3上下徘徊。排除了学习率问题后最可疑的是train/val划分出了事。如果这357张图里有从视频里连续抽帧的片段前几帧和第几帧内容高度相似。随机划分时往往同一段画面一半进了训练集、一半进了验证集。模型把训练集里那几帧的纹理记住了验证集里相似画面本该得分很高但如果相似画面分配不均匀验证分数就忽高忽低。解决方法是按场景划分而不是按文件随机划分。先根据文件名前缀或拍摄时间把数据分组保证同一组画面只出现在训练集或只出现在验证集。357张图本来就少划分前先看一眼有没有连续帧的痕迹这是我拿到任何小数据集的第一反应。4.2 现象警察类AP很高非警察类AP低得没法看验证结果显示警察类AP达到85%非警察类只有40%。打开标签统计一看警察类有600个框非警察类只有不到100个。这就是类别严重不平衡。YOLO26默认的损失函数里多数类对梯度更新的贡献更大少类学不够。非警察类本身还要覆盖保安、骑行路人、深色衣服行人等多种形态类内差异大样本又少AP自然上不去。解决思路有两个分支。第一种是给少类做扩充水平翻转、随机亮度抖动、随机裁剪后把新增的增强样本同时复制到对应的txt标签文件里。第二种更省事把类别分布差异过大的情况反馈给数据生产环节补充一批非警察样本让400个框以上。如果短期内没有数据补充可以将两类权重调成0.6, 0.4让少类样本对损失的贡献权重提高但这种方法治标不治本数据均衡才是正路。4.3 现象模型把穿深色制服的背影误检成警察这是警察识别场景特有的误报。357张图里如果警察样本大多是正面或侧面角度模型容易把深蓝色保安制服、深色棉衣的背影学成“警察特征”。表现在推理阶段就是正面识别得不错一到背影或侧脸就会误报。原因不是模型坏了而是标注样本的视角多样性不足以覆盖真实场景的拍摄角度。解决方法是两条腿走路。训练阶段强化数据增强水平翻转、旋转10度以内、亮度对比度扰动能让模型对视角变化更钝感数据层面建议把容易混淆的深色制服非警察单独多标几个框让模型见过足够的“非正面警察”负样本。357张图规模下对混淆样本的记忆还没被大量数据稀释多标负样本是最有效的办法。4.4 现象训练刚跑几个step就报OOM进程退出训练命令看着没问题数据检查和yaml也正常但第一步还没跑完就OOM。通常是batch和imgsz组合超出了显存容量。默认batch若设成auto一些版本会根据显存自动选择结果在显存小的卡上仍然选得过于乐观。另外一种情况是开了halfTrue但仍然不够或者imgsz从640提到832之后显存翻倍上涨。解决顺序是固定往下调先把batch降到8若还OOM就把imgsz降到416两者都降后显存占用会缩到原来的三分之一以内。若在4G显存的环境里batch4、imgsz416、halfTrue是能跑通的底线组合。357张图虽然训练量小但推理端如果也要用低显存设备模型输入尺寸固定为640时就别勉强开batch16训练训练端的batch不会影响推理端性能。4.5 现象换一台机器重训指标和之前对不上同一个人、同一个数据集、同样的命令换到另一台显卡机器上重新训练验证mAP差了5个百分点。如果没固定随机种子数据增强的随机性每次都不一样结果对不上是正常的。另一个隐蔽原因是预训练权重版本不一致前一次用的是yolo26n某个版本权重后一次跑的目录里可能混了更新过的权重文件。还有一个更隐蔽的data.yaml里path写的是绝对路径换机器后路径变了但文件内容没随之更新yaml自动改为相对引用可能导致训练验证数据划分变化。解决方法是把训练参数固化下来。命令行里固定seed权重文件复制到项目目录后用相对路径引用训练结束把args.yaml备份一份。做到这三步换机器重训即使不至于完全一致指标差距也能控制在可接受范围内。5. 357张图的模型怎么才叫“能上线”K则交叉验证与压力测试小数据集的一次验证分不说明问题可能只是运气好。我现在的习惯是任何少于500张图的数据集在宣布模型可用之前先过三关交叉验证测稳定性、真实场景截图做压力测试、训练配置做快照留底。5.1 用KFold交叉验证测数据集的真实水平把357张图切成5份每次拿4份训练、1份验证轮着来一遍取平均mAP这就是5折交叉验证。它能看出模型到底是真的学到了通用特征还是某个fold运气好from sklearn.model_selection import KFold from pathlib import Path images sorted(Path(police_dataset/images).glob(*.jpg)) kf KFold(n_splits5, shuffleTrue, random_state42) for fold, (train_idx, val_idx) in enumerate(kf.split(images)): fold_dir Path(ffold_{fold}) fold_dir.mkdir(exist_okTrue) (fold_dir / train.txt).write_text(\n.join( f/abs/path/police_dataset/images/{images[i].name} for i in train_idx)) (fold_dir / val.txt).write_text(\n.join( f/abs/path/police_dataset/images/{images[i].name} for i in val_idx))YOLO26的data.yaml里train和val除了写目录也支持写一个txt路径列表文件。脚本生成的train.txt和val.txt就是这个用途。每折生成一个data_yamltrain指向对应fold的train.txtval指向val.txt然后训练5次。如果5折结果mAP50在0.5到0.7之间波动说明模型对数据分布敏感需要回看标注质量如果数值接近说明模型总体稳定。357张图做5折时每折验证只有七八十张分数波动大也正常但方向性异常一眼就能看出来。5.2 对着夜视截图和远距离截图做压力测试交叉验证通过之后不要直接拿模型上线。从真实场景里挑一些训练集没覆盖的画面夜间灯光下的执勤画面、远处的人群、背影、雨雾天。对这些画面跑推理看两类问题漏检警察、误检路人。357张训练数据不可能覆盖所有真实光照因此这部分测试的结果是决定补拍数据还是调低置信度阈值的依据。如果夜间误报太多一个务实做法是把置信度阈值从0.25提到0.35误报会明显下降代价是少量低分警察目标被滤掉。这个阈值取舍要针对实际场景压测后定不是我拍脑袋帮你定。5.3 训练配置快照备份训练结束把配套文件留底这是我吃过亏之后养成的习惯。放到项目目录下的固定文件夹里命名带上日期或版本mkdir -p model_archive_v001 cp -r runs/detect/train/weights model_archive_v001/ cp police_dataset/data.yaml model_archive_v001/ cp runs/detect/train/args.yaml model_archive_v001/args.yaml是训练入口自动记录的完整参数包含batch、imgsz、epochs、seed以及数据增强开关。日后模型在线上表现异常要回溯时这份快照能告诉你当时到底怎么训的换人接手也看得懂。没有这份记录后悔药都找不到。我这套流程走完357张图训练出的警察识别模型通常能稳定在mAP50约0.7上下够扛住场景相对固定的内部演示或受控测试。如果把模型直接撒到开放街道场景必须补充数据重新评估。希望你按这套步骤前先补两条自己的验证数据一条是训练集里警察类和非警察类的真实框数比值另一条是你实际部署场景的视频截图。有了这两条后面所有参数调整都有依据希望帮到你。本文还有配套的精品资源点击获取
返回列表