人行道场景目标检测数据集:YOLO格式智慧城市长尾问题实战
人行道场景检测听起来不像自动驾驶那种大热门赛道但它恰恰是智慧城市落地最频繁、最琐碎、也最容易被低估的一环。我平时做项目就深有体会算法效果不好十有八九不是模型的问题而是数据集跟实际场景压根对不上。最近我整理了一套专门面向人行道场景的数据集10300张图全部跑通YOLO格式说句实在话这套东西的定位从一开始就不是“通用目标检测数据集”而是冲着智慧城市里的长尾场景去的——盲道占用、共享单车乱停、小商贩超范围经营、路面破损这些真实痛点。不管你是刚入门YOLO、想训练自己的第一个检测模型还是已经跑过不少公开数据集、正在为落地场景的数据发愁这篇文章都值得你看完。我会把数据集的设计思路、标注规范、目录结构、训练参数这些核心细节全部拆开讲也会把我踩过的坑、排查问题的方法一并交代清楚。1. 内容整体设计与思路拆解1.1 人行道场景为什么难做——长尾分布是最大敌人智能交通、智慧城管这些项目表面上是“把所有东西都检测出来”实际上核心难点在长尾分布。什么叫长尾分布简单说人行道场景里常见的、高频出现的目标就那么几类——行人、非机动车、红绿灯关于这些类别的公开数据集一抓一大把。但真正让项目无法交付的往往是那些出现频率低、但一旦出问题就会引发投诉的目标占用盲道停放的共享单车、摆到路面上的经营摊位、破损翘起的地砖、违规驶入人行道的电瓶车。我用一个生活化的例子来解释这个问题的严重性。假设你训练一个模型用来识别“人行道上乱停的共享单车”你用通用数据集训练模型可能表现还不错。但如果这个通用数据集的来源是欧洲城市、美国郊区那里的共享单车长什么样跟国内城市随处可见的黄色、蓝色、绿色单车完全不是一回事。通用数据集里这种类别的样本可能只有几十张而你落地场景里每天要处理成千上万张实时抓拍。你拿一个“见过世面但没见过你家门口单车”的模型去部署结果可想而知——误报率、漏报率都高到没法用。这套数据的出发点就是解决这个核心矛盾不追求类别多到吓人而是追求每一个类别在你的真实场景里“够用”“扛得住”。10300张图数量不算夸张但每张图都经过筛选保证目标密度适中、场景覆盖到位并且每张图都带YOLO格式的标注文件拿来就能直接进训练流程。1.2 数据集方案选型背后的考量关于目标检测数据集业内比较大的公开资源像COCO、BDD100K确实覆盖面广类别也丰富。我在很多项目里也用过它们做预训练。但落到人行道这种高度垂直的场景有四个绕不开的问题第一地域差异大。BDD100K是欧美路况为主街景风格、车辆型号、行人衣着、道路设施跟国内城市差异明显直接拿来做智慧城市项目domain shift带来的性能下降很直接。第二类别粒度过粗。COCO里有“person”和“bicycle”但没有“盲道”“井盖缺失”“占道经营”这种城管和交管关心的对象。换句话说就算你拿COCO预训练最后还是要自己标注大量私有类别迁移成本并不低。第三场景单一。很多公开数据集都是在天气良好、光线充足的条件下采集的而人行道检测项目在实际部署时雨天、夜晚、逆光、树荫遮挡是常态。没有覆盖这些条件的训练数据模型一到恶劣天气就“罢工”。第四标注颗粒度不统一。公开数据集的标注框往往比较松有的连被遮挡很严重的行人都框进去这对训练质量是有负面影响的。所以我在设计这套数据集时权衡下来放弃了“大而全”的路子转向“小而精”场景聚焦人行道类别聚焦智慧城市真实项目需求质量优先每张图的标注都做过审核。对做落地项目的团队来说这种数据比“拿一堆通用数据来凑”要实用得多。2. 核心细节解析与实操要点2.1 10300张图意味着什么——数据规模与质量的关系很多人一听到“10300张”第一反应是“够不够”我的回答是取决于你的任务复杂度。如果做的是COCO那80类通用检测一万张确实偏少但人行道场景如果聚焦到8到12个核心类别一万多张训练图完全够用了。关键在于类别分布和场景多样性而不是盲目堆数量。这套数据的构成大致是训练集7000张左右验证集1500张左右测试集1800张左右。我特意没有把验证集和测试集合在一起因为很多初学者习惯只分train和val然后反复用val调参调着调着就过拟合到验证集上了。留一个独立的测试集相当于给模型做一次“期末考试”心里更有底。从场景覆盖来看这些图像涉及主干道两侧人行道、背街小巷、商业街区、学校门口、地铁站出入口、公园步道等多种子场景时间跨上午、下午、傍晚、夜间天气覆盖晴天、阴天、雨天。之所以强调这些是因为行人检测和车辆检测在本质上吃的是“场景先验”——一个只在晴天白天训练过的模型放到夜雨里基本是废的。2.2 标注规范与类别体系——“检测什么”比“怎么检测”更重要在开始标注之前最需要花时间想清楚的是类别体系。我见过太多项目标注到一半才发现类别定义含糊有的框把共享单车和私人单车混在一起有的把盲道区域的静物也算作“障碍物”最后模型输出一团糟。这些问题的根源就是没有建立清晰的标注规范。这套数据的类别体系大概分为几个层级交通参与者类包括行人、非机动车电瓶车、自行车、机动车违规驶入人行道的车辆。这几个是基础中的基础几乎没有哪个行人场景项目能绕开。道路设施类包括盲道、红绿灯、路灯杆、井盖。盲道是智慧城市项目的重点关注对象——是否被占用、是否破损中断都是城管部门的考核指标。违规行为对象类包括占道经营摊位、违规停车机动车占用人行道。这类是典型的城管执法需求通用数据集里基本找不到对应类别。路面状态类包括路面破损、积水。这类检测对图像分辨率要求高但对模型结构要求反而常规属于“数据到位就能出效果”的类别。标注规范里还规定了几条硬性规则目标被遮挡超过70%不标目标像素宽度小于20像素不标类别含糊的宁可放弃也不硬标。我在标注时严格执行这几点因为YOLO系列的标注质量直接影响训练收敛速度和最终mAP——一张标注错误的图十个epoch都未必能纠正回来。用YOLO格式的朋友注意了每张图对应的txt文件每行格式是这样的class_id x_center y_center width height其中x_center、y_center、width、height都是归一化到[0,1]区间的相对坐标。比如一张1920x1080的图一个框的左上角是(480, 540)右下角是(720, 810)换算下来就是3 0.3125 0.625 0.125 0.25这里的3如果是类别编号就代表这个框属于第四个类别。我之前遇到不少人直接拿像素坐标写进txt结果训练时loss直接飞掉查了半天才发现是归一化的问题。2.3 YOLO格式适配与目录结构拿到数据集以后第一件事就是确认目录结构是否符合YOLO的训练习惯。这套数据的组织方式如下dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ ├── data.yaml └── classes.txtimages和labels一一对应同名文件分别放在train、val、test各自的子目录里。data.yaml里面配置了类别名、训练/验证/测试路径直接就能喂给YOLOv5、YOLOv8或者YOLOv11使用。关于data.yaml有一个小细节经常有人栽跟头——路径配置。如果你用的是相对路径务必确认训练时的工作目录在哪如果你用的是绝对路径那换机器之后一定要同步修改。我通常建议直接改成绝对路径省事尤其团队协作时每个人克隆仓库后只需要改一下data.yaml里的路径就能跑。3. 实操过程与核心环节实现3.1 基于YOLOv8的训练配置与参数调整我之前用YOLOv8s在这套数据上做了完整训练实验整个流程算是检验数据集质量最直接的方式。硬件环境就是普通的单卡RTX 3090或者4090没有上多卡分布式。训练启动命令参考yolo train data/path/to/data.yaml modelyolov8s.pt epochs100 imgsz640 batch16 device0这里的几个参数我在实际调的时候有自己的偏好。imgsz640是速度和精度的折中如果你主要检测路面破损这种小目标可以考虑提到768甚至896但代价是显存占用和训练时长都翻倍。batch16在24G显存下比较稳如果你的卡只有12G显存建议降到8不然很容易OOM。epochs100对一万张图的数据集来说比较充裕我实测大约在60到70个epoch时mAP就趋于平台期了。关于加载预训练权重这件事我的态度是除非你有充分理由否则强烈建议用yolov8s.pt而不是从零训练。预训练模型在COCO上学到的通用特征——边缘、纹理、形状——对这些垂直场景的迁移非常有帮助。从零训练不是不行但通常需要多花一倍以上的epoch才能追上。3.2 关键训练指标解读与调优方向训练过程中我习惯重点盯三个指标box_loss、cls_loss和mAP50-95。box_loss反映的是框的回归质量。如果你发现box_loss降得很慢或者反复震荡先检查标注框是不是有大量不对称或者压根没紧贴目标的情况。cls_loss则体现分类置信度如果训练后期cls_loss还明显高于初期多半是某些困难样本在“拖后腿”。mAP50-95这个指标被人吐槽很多因为它太严格了——IoU从0.5一路算到0.95取平均。但我依然建议保留这个指标作为验收依据因为人行道场景里小目标多如果只盯着mAP50很可能你看到的结果是“看起来不错”部署之后才发现小目标的定位精度完全不够用。我在实验里有一组对照用YOLOv8s在这套数据上训练100个epochmAP50大约在0.62到0.65区间mAP50-95大约0.38到0.42。说实话这个成绩单看不惊艳但放到真实场景里行人、非机动车、盲道占用这几类核心目标的检测效果是够用的。如果你追求更高的精度建议把模型换成yolov8m或者yolov8l显存够的话直接上rticle是yolov8lmAP能再涨四五个点。3.3 从训练到部署ONNX导出与推理验证训练完的模型不能一直躺在PyTorch权重文件里落地部署时通常要转成ONNX或者TensorRT格式。导出ONNX的命令很简单yolo export model/path/to/best.pt formatonnx imgsz640 opset12但这里有几个容易踩的坑我逐个说。第一opset版本不能太低否则一些算子不支持导出会报错。第二如果你在推理端要用GPU加速建议导出时加上dynamicTrue让输入尺寸不固定方便后续接不同分辨率的视频流。第三导出后务必用onnxruntime或者TensorRT做一次完整推理验证不要认为“能导出就一定能正确运行”。我遇到过一种情况模型在PyTorch里推理一切正常导出ONNX后某些小类别完全检测不到。排查下来发现是导出时imgsz设置跟训练时不一致导致输入张量的缩放逻辑变化小目标特征丢失了。这种问题很隐蔽如果不是逐类别对比推理结果真的很难发现。4. 常见问题与排查技巧实录4.1 小目标漏检严重怎么办人行道场景里有一个天然难点——小目标特别多。一个站在50米外的行人在1080P画面里可能只有40x80像素对于yolov8s这种下采样倍数较大的模型小目标的特征图经过多次缩放后几乎剩不下多少信息。我处理这个问题的优先级顺序是先加大输入分辨率从640升到768或896这一步通常能带来3到5个点的mAP提升再加一个浅层检测头YOLOv8本身是P3、P4、P5三层输出如果你用小目标专用的yolov8-p2版本会多一个P2层专门负责小目标检测最后才是调anchor实际上YOLOv8已经用anchor-free了这一步不需要操作。如果做完这三步依然漏检那问题大概率出在数据本身——你训练集中小目标样本的数量是不是太少了我建议统计一下所有标注框中宽度小于32像素的占比如果低于10%数据增强层面可以适当加一些随机裁剪放大人为“制造”更多小目标样本。4.2 雨天和夜间效果差怎么破很多同学拿到数据集训练完晴天白天的效果很满意一到雨夜就翻车。这个问题简单来说就是训练数据的“域覆盖度”不够。我这里专门留了大约15%的阴雨夜间图像就是让大家不用一上来就慌。如果效果依然不理想优先检查图像增强策略。YOLOv8自带的增强参数里hsv_h、hsv_s、hsv_v控制色调饱和度明度偏移适当调高可以让模型对光照变化更鲁棒。我一般习惯把hsv_h调到0.02左右hsv_s调到0.7左右hsv_v调到0.5左右。另外随机擦除随机遮挡一部分区域对夜间遮挡场景也有帮助。如果实在不想动训练参数另一个思路是直接在图像预处理阶段做自适应直方图均衡化CLAHE把夜间图像的对比度拉起来再送进模型推理。这个方法不增加任何训练成本实测在很多夜间场景下能明显减少漏检。4.3 类别混淆与误检的定位方法误检——把人行道上的垃圾桶认成“占道经营”把路灯杆的影子认成“路面破损”——这类问题在智慧城市项目里特别常见。排查的时候先不要急着加数据或者调参数第一步应该分析混淆矩阵。YOLO训练结束后会生成confusion_matrix.png这张图能直观展示哪些类别互相“打架”。我举一个真实例子有一次模型频繁把“盲道占用”的共享单车框错到“占道经营”上看混淆矩阵果然这两类交叉严重。仔细琢磨了一下发现标注阶段这两类在某些角度下的视觉特征确实接近——一辆停在盲道上没放稳的单车看起来跟一个占地很小的水果摊在人行道的纹理背景下从俯视角度确实容易混。我的处理办法是在标注规范里加了更明确的判定边界——共享单车只算“盲道占用”摊位必须有明显经营物特征货架、遮阳伞、秤盘才算“占道经营”。重新清洗了一轮数据后这个问题基本消失。这个案例想说的是数据集标注的一致性在某种程度上比模型结构的选择还重要。很多看似“模型不行”的问题根源都在数据标签的模糊地带。4.4 训练loss不收敛的常见原因排查训练loss不收敛也是高频问题。我把常见原因和排查顺序列一个表方便你对照操作现象可能原因排查与解决loss一开始就飙到很大标注坐标未归一化检查txt文件坐标是否在0-1之间loss震荡剧烈不下降学习率过高把lr0从0.01降到0.001训练集loss下降但验证集loss不降过拟合增强数据、加大dropout、提前早停mAP50很高但mAP50-95上不去标注框边界不紧重新校准目标边缘验证集上某些类别AP为0该类别样本过少检查每个类别的样本数量分布还有一个特别容易被忽视的坑类别编号不连续。比如classes.txt里面你定义了0、1、2、3、4但标注文件里出现了类别5训练时这部分的loss会一直偏高模型被“带偏”。我的建议是训练前先跑一个脚本统计所有标注文件的类别范围确认没有越界python -c import os labels_dir labels/train ids set() for f in os.listdir(labels_dir): with open(os.path.join(labels_dir, f)) as fh: for line in fh: ids.add(int(line.split()[0])) print(sorted(ids)) 如果输出结果跟classes.txt对不上那数据准备阶段一定有疏漏。5. 训练与业务结合的扩展玩法5.1 结合智慧城市的实际项目场景我打算把数据集持续迭代扩充。目前这批图像以城市常规道路为主后续打算加入更多特殊场景比如学校门口上下学高峰时段、景区周边、大型活动集会之后的人行道状态这些场景对城市管理来说才是真正的硬骨头。另外也可以把无人机俯拍视角纳进来跟常规平视视角形成互补这样对城管巡逻、社区安防这类需求会更有价值。数据集本身也可以做成一个小工具链比如配合简单的统计脚本能按类别、按时长、按区域输出人流量热力图、非机动车违停高发路段分布图这类可视化数据对城管部门来说比单纯的检测框更有说服力。5.2 数据增强策略的进阶思路对这套数据集来说常规的马赛克增强、随机翻转、随机缩放已经够用。但如果想让模型泛化性更强有两个方向可以试试第一个是MixUp增强把两张图按一定比例混合让模型学会在“混乱”背景里识别目标第二个是随机光照模拟在训练时随机叠加亮度曲线模拟清晨、正午、黄昏的不同光照——这个对夜间检测的提升特别明显。我实测下来的经验是增强策略不要一次性全开一次加一个策略每加一次都跑短epoch的对照实验。这样可以明确知道是哪个策略在起作用、哪个策略反而让效果变差。盲目把所有增强都拉满很多时候是负优化因为太强的增强会让模型学到“假纹理”。最后再分享一个小技巧当你拿到一套新数据集不管来源是哪第一件事别急着开训练先花半小时随机抽几张图打开标注框过一遍。看看框是不是紧贴目标、有没有漏标错标、类别分布是不是合理。这套人工抽检动作比之后追查三天loss异常值要省时间得多。数据集的质量是模型性能的上限模型结构只是在逼近这个上限。把这行的功夫做到位后面的事都会顺很多。