ARTICLE DETAIL

资讯详情

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

行人检测实战:VOC/COCO/YOLO格式转换与YOLO11三平台训练指南

行人检测实战:VOC/COCO/YOLO格式转换与YOLO11三平台训练指南 简介行人检测是监控场景中高频落地需求。这份资源面向需要训练YOLO等目标检测模型的算法工程师与学习者提供1000张真实场景行人图像数据集覆盖校园、街景、道路及不同程度遮挡行人适合公共场所监控项目扩展训练数据。包体为单个PDF约4.17MB内附数据集基本情况、标注样例缩略图与百度网盘获取方式标签提供VOC(xml)、COCO(json)、YOLO(txt)三种格式可直接接入主流检测框架。资源已有1045人学习。随附YOLO11一键训练脚本支持GPU、CPU、Mac三平台并给出博主训练结果日志作参考可帮助使用者快速完成从数据处理到模型训练验证的闭环。1. 从“1000张行人图三格式标签”说起这套组合到底解决了什么问题拿到一个“行人目标检测数据集1000张图 VOC/COCO/YOLO三种格式标签 支持GPU/CPU/Mac三平台YOLO11一键训练脚本”这样的组合包第一反应不是急着解压而是先想清楚两件事它能让我的检测模型少走多少弯路以及我要为它补多少功课。如果你正打算做行人检测的基线实验或者想在一两天内跑通一个端到端流程这类方案能替你省掉标注整理和训练启动的体力活但前提是你知道它的边界。1000张图对行人检测来说不算大却能撑起一个完整的从数据检查、格式转换、平台适配到训练验证的闭环。真正值钱的不是图本身而是“三种格式都给你配齐”和“一条命令在不同硬件上都能跑”这两件事——这两件事恰恰是绝大多数人在项目里自己动手时最容易翻车的地方。2. VOC/COCO/YOLO三种标签格式的差异与转换脚本2.1 三种格式各自的存储约定从文件结构到坐标含义很多人在数据集上栽跟头不是因为不会训练而是因为标签格式没搞明白。VOC格式是Pascal VOC项目留下的习惯每张图对应一个同名XML文件里面用BndBox记录左上角和右下角的像素坐标COCO格式把全部信息集中到一个JSON里用images、annotations、categories三张表关联图片和目标框坐标是[x, y, w, h]的像素绝对坐标YOLO格式则最朴素——每张图一个同名TXT文件每行写class_id x_center y_center width height这四个值全部是归一化后的相对坐标除以图片的宽和高。这三套体系没有谁好谁坏它们服务于不同的工具链。VOC系标注工具LabelImg和老一代检测框架SSD、Faster R-CNN的VOC数据加载器惯用它COCO是学术榜单和评估体系的事实标准COCO API的pycocotools生态成熟YOLO系训练脚本只认TXT归一化格式速度最快、最简洁。你拿到一套“一个数据集给你三种格式”的交付物时实际上相当于省掉了从标注工具到训练框架之间最繁琐的搬运工作。但搬运格式不等于改个后缀坐标换算中存在大量细节坑最典型的是归一化坐标和像素坐标互相转换时的精度丢失以及类别ID从1开始还是从0开始的老问题。2.2 给出一份可复用的VOC/COCO转YOLO脚本下面是我在实际项目中一直保留的转换脚本主体它解决的是绝大多数人最常用的转换方向从VOC或COCO转到YOLO训练格式。脚本只依赖lxml和原生json其他库都不用装。import json from pathlib import Path import xml.etree.ElementTree as ET def voc_to_yolo(xml_path, out_path, class_list): # 读入XML并解析拿到像素坐标 root ET.parse(xml_path).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_list: continue # 跳过不在类别表里的对象避免训练时索引越界 cls_id class_list.index(cls_name) box obj.find(bndbox) x_min float(box.find(xmin).text) y_min float(box.find(ymin).text) x_max float(box.find(xmax).text) y_max float(box.find(ymax).text) # YOLO要求归一化的中心点和宽高注意宽度不能为0 x_center (x_min x_max) / 2 / img_w y_center (y_min y_max) / 2 / img_h w (x_max - x_min) / img_w h (y_max - y_min) / img_h x_center min(max(x_center, 0.0), 1.0) y_center min(max(y_center, 0.0), 1.0) w min(max(w, 0.0), 1.0) h min(max(h, 0.0), 1.0) lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) out_path.write_text(\n.join(lines) \n, encodingutf-8) def coco_to_yolo(anno_path, img_dir, out_dir, class_list): # COCO的JSON里有一个categories字段必须重新映射到连续ID data json.loads(Path(anno_path).read_text(encodingutf-8)) cat_map {c[id]: c[name] for c in data[categories]} id_map {name: i for i, name in enumerate(class_list)} img_info {im[id]: im for im in data[images]} anns {} for ann in data[annotations]: img_id ann[image_id] anns.setdefault(img_id, []).append(ann) for img_id, ann_list in anns.items(): im img_info[img_id] img_w im[width] img_h im[height] lines [] for ann in ann_list: cls_name cat_map[ann[category_id]] if cls_name not in class_list: continue cls_id id_map[cls_name] x, y, w, h ann[bbox] # COCO的bbox是[x, y, width, height]转成中心点表示 x_center (x w / 2) / img_w y_center (y h / 2) / img_h w_norm w / img_w h_norm h / img_h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w_norm:.6f} {h_norm:.6f}) out_file out_dir / f{im[file_name].rsplit(., 1)[0]}.txt out_file.write_text(\n.join(lines) \n, encodingutf-8)这段代码里有两个参数值得特意说明。第一个是class_list它必须是固定顺序的类别名列表比如[person]转换脚本靠这个列表把类别名映射成从0开始的ID第二次转换时如果顺序变了训练结果就全乱了。第二个是坐标值的clip操作代码里用min(max(...))把归一化坐标限制在0.0到1.0之间这一步看似多余实际能挡住标注工具偶尔产生的越界框。另外转换完成后强烈建议做一次反向校验从生成的TXT里随机抽几张图写个小脚本把归一化坐标还原成像素框画到图上肉眼看一遍有没有错位。格式转换的代码本身不难难的是你永远不知道上游标注工具会留下什么脏数据反向校验是成本最低的后悔药。2.3 文件名对应关系三格式共存的隐藏协议三套格式能共存于同一数据集是靠一个不成文的约定串起来的图片文件名不含后缀是主键。Annotations/0001.xml对应JPEGImages/0001.jpg也对应labels/0001.txt。COCO的JSON里则用images表的file_name字段间接指向图片文件。这个约定一旦被破坏整套数据就断了链。常见的破坏方式有三种一是重命名图片时没有同步改XML和TXT导致三个目录各有各的名字二是XML或TXT文件被放在子目录里遍历脚本只找了顶层目录三是文件名里带了空格或中文Linux训练环境下读取路径时报错非常隐蔽。因此无论是你自己做数据集还是拿到别人交付的数据集第一步永远是写一个文件名比对脚本把三个目录的stem集合做差集缺一个都别急着进训练。1000张图的数据集不大这种文件级检查几秒就能扫完但它能让你避开后面几个小时的无头排查。3. YOLO11三平台一键训练脚本结构选型与关键参数怎么落3.1 为什么是YOLO11网络演进和训练性价比的选择YOLO11是YOLO系列里对“训练友好度”做得很激进的一代。它在主干部分引入了跨阶段局部模块的改进变体在检测头里简化了结构训练时显存占用比上一代更稳定同时保留了对小目标的检测能力。对行人检测这个具体任务来说行人往往是小目标、密集场景多YOLO11的特征融合设计相比老版本在处理密集行人时漏检更少。更重要的是YOLO11的训练脚本生态成熟它直接支持device参数接收cpu、cuda:0、mps三种取值这天然就是三平台训练的基础。训练脚本里另一个值得关注的细节是预训练权重的选择。单人做1000张图的行人检测项目我不建议从零训练而是加载YOLO11在COCO上训练出来的通用权重再冻结主干做迁移学习。这样即便你的数据集只有1000张训练100个epoch也能收敛得像模像样。YOLO11在结构上的改进给这个迁移流程提供了便利它的权重文件按detect、pose、seg等任务区分行人检测只需要detect权重。一个新版本的训练收益总是集中在“更稳的学习率”“更友好的显存占用”这类工程体验上YOLO11就是典型的这种改进型版本值得为它把脚本写顺。3.2 平台检测逻辑如何用一条命令兼容GPU/CPU/Mac所谓“一键训练”核心是一个启动脚本来替你做三件事识别当前操作系统和硬件、按平台能力设定合理的训练参数、用统一的入口调用训练程序。下面是一个兼容三平台的最小启动脚本框架基于Bash实现只依赖nvidia-smi和uname在任何一台设备上都能跑。#!/usr/bin/env bash set -e # 允许环境变量覆盖默认值方便不动脚本本体做实验 DATA${DATA:-datasets/person.yaml} EPOCHS${EPOCHS:-100} IMGSZ${IMGSZ:-640} if [[ $(uname) Darwin ]]; then # Mac平台优先MPS后端Intel老机型自动回退CPU if [[ $(uname -m) arm64 ]]; then DEVICEmps BATCH${BATCH:-16} else DEVICEcpu BATCH${BATCH:-8} fi elif command -v nvidia-smi /dev/null; then # Linux NVIDIA显卡读取显存总量动态决定batch MEM_TOTAL$(nvidia-smi --query-gpumemory.total --formatcsv,noheader,nounits | head -1) DEVICE0 if [[ $MEM_TOTAL -lt 12000 ]]; then BATCH${BATCH:-16} elif [[ $MEM_TOTAL -lt 24000 ]]; then BATCH${BATCH:-32} else BATCH${BATCH:-64} fi else # 纯CPU环境含无NVIDIA GPU的Linux DEVICEcpu BATCH${BATCH:-8} fi python train.py \ --data $DATA \ --epochs $EPOCHS \ --imgsz $IMGSZ \ --device $DEVICE \ --batch $BATCH脚本里的参数值得逐条说清楚。DEVICEmps是苹果M系列芯片的PyTorch加速后端它比CPU快得多但对某些算子支持不完整YOLO11训练主流程用得少碰上了就换成cpu再跑DEVICE0是单张NVIDIA显卡的写法多卡可以写成0,1训练框架会自动并行BATCH按显存而不是按显卡型号来定这块的判断逻辑来自实际踩坑经验——同是笔记本8G显存的卡跑batch 64直接OOM16G显存却轻松跑batch 64只看型号不看显存就是瞎猜。CPU环境下强行调大batch毫无意义反而让每个step的等待时间变得不可接受不如把IMGSZ调小到448或512换来更短的训练周期。3.3 训练参数的选择逻辑给新手一套可行的默认值训练参数的默认值不能拍脑袋。针对“1000张行人图从头迁移”这个组合我常用的配置是epochs100、imgsz640、batch按前面脚本按平台自适应、optimizerSGD、初始学习率lr00.01、学习率周期调度cos_lrTrue。为什么不用Adam行人检测任务里SGD加余弦退火的收敛轨迹更稳定最终mAP通常比Adam高0.5到1个点而且SGD对学习率的敏感度低新手不必反复调。imgsz640是速度和精度的平衡点1000张图输到640分辨率一张A100跑100个epoch也就一两个小时普通人用消费级显卡跑半天也能接受。如果图里行人普遍很小imgsz960能提升召回率但训练时间会翻倍这个选择要看线上场景的视觉分辨率来定。我一般还会在脚本里显式加上--patience 30意思是30个epoch验证集指标没提升就提前结束。加这个参数的目的是防过拟合不是省时间。1000张图的数据量在训练后期很容易出现过拟合验证集mAP先涨后跌没有早停机制的话跑完100个epoch反而会得到一个比60个epoch时更差的模型。所有参数都应该是“让训练自动适应数据”而不是“让数据适应人的拍脑袋”。4. 拿到数据集后怎么把训练真正跑起来4.1 第一步先做数据完整性校验别急着训练很多人在这一步栽了跟头解压完数据集直接填好YAML就开始训练结果跑到一半报AssertionError: train labels not found回头查才发现labels目录下的文件名和JPEGImages对不上。血泪经验是训练前必须先跑一次校验脚本扫一遍三个目录的名称对应关系、图片能否正常解码、标注框是否越界。用下面的脚本可以在30秒内对1000张图完成体检。from pathlib import Path import cv2 img_dir Path(JPEGImages) voc_dir Path(Annotations) yolo_dir Path(labels) img_stems {p.stem for p in img_dir.glob(*.jpg)} voc_stems {p.stem for p in voc_dir.glob(*.xml)} yolo_stems {p.stem for p in yolo_dir.glob(*.txt)} # 差集越小越好非空集合就是要手动处理的断链文件 print(有图无VOC:, sorted(img_stems - voc_stems)[:10]) print(有图无YOLO:, sorted(img_stems - yolo_stems)[:10]) print(有VOC无图:, sorted(voc_stems - img_stems)[:10]) for img_path in img_dir.glob(*.jpg): img cv2.imread(str(img_path)) if img is None: print(无法解码:, img_path)这段代码的输出是“差集列表”每一项都对应一个要人工修复的文件。常见的处理办法是如果缺XML但TXT存在就用TXT的信息反写一份XML如果缺TXT但XML存在就调用前面写的voc_to_yolo函数补一份。如果某个文件既没有XML也没有TXT直接把它从图片目录里移走避免训练时统计到无效样本导致mAP偏低。校验这一步的重点是“尽早暴露问题”不要拖到训练开始后才被框架报错牵着走。4.2 第二步生成YOLO训练所需的数据集配置文件YOLO系训练框架读的不是普通文件夹而是一个.yaml文件里面要写明图片目录、验证集目录和类别表。对于手头这份“三格式数据集”如果目录结构是标准风格可以直接写一个person.yaml。# 数据集配置路径相对于项目根目录 path: ./datasets/person train: images/train val: images/val # 类别名必须和转换脚本里的class_list保持一致 names: 0: person这个YAML有三个容易写错的点。path字段是整个数据集的根目录后面train和val都是相对它的子路径有人把path指向了images/train训练直接报找不到图片names下面的ID顺序必须和TXT标签文件里的class_id一一对应类别顺序错了模型训练得再好预测出来也是错位的train和val指向的目录要保证里面图片数量和标签数量完全一致否则YOLO会在训练前打印一行警告然后跳过无法匹配的文件而很多新人忽略了那个警告。如果你是拿这份数据去做“验证自己的想法”强烈建议用train_val_split脚本自行划分训练集、验证集和测试集比例按7:2:1。注意划分时不能只分图片必须让图片、XML、TXT三个文件同步移动一个文件移动了其他两个没动你的数据完整性校验就白做了。这一步做得漂亮后续所有实验都在同一份干净数据上做每一版指标才具备可比性。4.3 第三步启动训练并看懂前几行日志一切就绪后运行bash train.sh训练脚本立刻会在终端打印模型结构、参数量、每张图的标注框统计分布。如果显示的类别数和你的person.yaml一致且每张图的标注平均约1到3个框这说明数据加载是健康的。如果标注框统计显示某些图片有几十个框去找出那些图看一遍它们大概率是密集人群图需要确认是否有漏标或错标。前10个epoch训练会有一段时间的冷启动期loss下降不明显甚至小幅上升这是正常的。到第20个epoch左右如果box_loss和cls_loss两条曲线的下降趋势已经稳定说明模型学进去了。此时验证集的mAP大概率还在低位耐心让它继续跑。真正需要你干预的只有两种情况loss曲线在前10个epoch内就断崖式跌到接近0这往往是标注文件和图片错位模型在学“背答案”或者是loss值越跑越高那通常是学习率太大需要把lr0从0.01降到0.001。5. 训练与数据配合里最容易踩的5个坑及排查清单5.1 边界框等于图片边界导致归一化坐标越界现象训练过程中AssertionError: bbox is out of bounds或者But this box is not it这类报错直接中断训练。原因标注工具在图片边缘画框时给出x_max 图片宽度、y_max 图片高度转成归一化坐标时w或h恰好等于1.0YOLO对边界值极其敏感判定越界。解决在转换脚本里对所有坐标执行min(max(value, 0.0), 1.0)强制截断并在启动训练前用脚本扫描所有TXT文件找出包含1.000000或0.000000的异常行数。我曾经处理过一个8000张的数据集这类越界标注有上百处全量截断后训练再也没报过错。5.2 VOC类别表和YOLO类别ID对不上模型学会了错误映射现象训练正常完成验证集mAP看起来还行但从权重推理出来的框乱标把自行车框当成行人。原因VOC标签里出现了一个class_list之外的类别名转换脚本默认跳过它但COCO的JSON里这个类别被映射到了别的ID两份标签的语义不一致。解决转换完成后分别统计VOC、YOLO、COCO三份标签中各自出现过的类别名做一次集合对比。不要假设数据集的类别表是固定的凡是转换脚本里出现if cls_name not in class_list: continue的地方都必须单独打印一条警告日志让异常类别浮出水面。5.3 Mac上训练速度慢或者直接崩溃现象在macOS的M系列芯片上跑训练tqdm进度条每步等待时间长达几十秒有时第几个epoch直接报RuntimeError并提示某个算子不支持MPS后端。原因MPS支持度还不完整YOLO11训练流程里的部分算子在MPS上走的是CPU回退路径导致性能骤降某些层甚至触发未知BUG。解决确定要长跑训练时在Mac上不要硬用MPS直接设DEVICEcpu配合在脚本里设置OMP_NUM_THREADS8、MKL_NUM_THREADS8让CPU吃满多核也能保持可接受的训练速度。MPS更适合做快速验证——跑通代码、确认数据没问题然后切到真正的训练平台去跑完整实验。5.4 GPU显存充足但训练中断开连接现象在远程Linux服务器上训练跑到第20个epoch左右终端显示Killed进程死了没有Python报错堆栈。原因训练时num_workers线程数量过大导致内存溢出Linux的OOM Killer杀掉了训练进程。常见于num_workers16和数据加载到RAM同时开启的情况。解决把num_workers降到min(8, CPU核心数)并且不要把缓存策略设成cacheram1000张图的数据量根本不需要缓存内存用cacheFalse就够了。如果还遇到Killed跑一下free -h看内存余量确认是不是还有其他程序占用了大量内存。5.5 验证集mAP高但实际检测效果差现象训练结束验证集上mAP超过80%但把权重放到真实街景图片上测试漏检一大堆误检也严重。原因数据集本身只有1000张且场景单一训练时没有按真实场景划分验证集。所有图片可能都来自同一批采集训练集和验证集存在数据泄露模型过拟合了背景而不是行人的共性特征。解决划分数据集前按场景聚类确保同一时间段、同一地点采集的图片全部进训练集或全部进验证集不要随机切分。另一个必要手段是把验证集音改为“困难样本集”在训练结束后用这个模型去跑它从未见过的街头抓拍图用模型输出的置信度分数来判断有没有泛化能力——如果大部分框的置信度都在0.8以上而且框得很准说明它真正学到了如果框抖得厉害、置信度忽高忽低说明接下来的工作重心是扩充数据而不是继续调参。6. 从能跑通到跑得好迁移学习、增量扩充与验证技巧先把这段的经验浓缩成一句话1000张图快速跑通是流程验证不是生产级模型的终点真正能提升结果的是迁移学习策略、数据积累节奏和验证方法。迁移学习的方向很明确——加载YOLO11在COCO上预训练过的detect权重冻结前10层主干只训练检测头和颈部前50个epoch用lr00.005后50个epoch解冻全部层将学习率降到0.001。这个动作能让模型在1000张图上依然保持COCO学到的底层特征而不必从零重新拟合纹理和边缘。不要信“训练越久越好”在这个数据量级上100个epoch已经进入了收益递减区间多看验证集曲线找那个mAP的峰值点比傻跑到结束更重要。增量扩充是长期维护这个数据集的主要方式。每周末拍一批新场景的行人照片跑一遍标注工具的自动预标注再由人工修正统一转成三格式后合并进原数据集。合并时必须用脚本重新统计三个目录的文件名差集和类别分布避免新旧两套标注把类别ID搞混。新数据加入后不要推翻旧模型重训而是在旧权重基础上用较低学习率继续训练20到30个epoch这是成本最低的迭代方式。还有一个值得坚持的验证习惯每次训练完不要只记录最终mAP还要保存每一轮的验证集预测图按从置信度0.9到0.3的区间各抽几张放大观察。行人检测的很多问题——小目标漏检、密集场景误检、逆光下人影模糊——是mAP指标反映不出来的看图能更快抓住模型的盲区。我自己在做这类项目时有一个习惯训练结束后故意挑几张模型从未见过的带遮挡、带逆光、带雨雾的图去跑推理如果这些图里还能准确框出行人我才会认为这套训练流程合格了。这份数据加脚本的组合方案价值在于让“数据准备”这个环节不再成为你做行人检测的障碍。把它当做一个流程模板而不是一个一劳永逸的模型你会少走很多弯路。希望帮到你。本文还有配套的精品资源点击获取
返回列表