3、车载数据集构建:BDD100K与SODA10M数据集解析、针对车载场景的标注规范、数据清洗与不平衡处理
做车载感知这几年,我最大的体会是:算法模型只占30%,剩下70%的功夫全在数据上。你想想看,一个在Cityscapes上跑得飞快的YOLO,放到车载场景里可能直接翻车——为什么?因为车载场景太特殊了,光照、遮挡、小目标、极端天气,这些在公开数据集里往往被弱化了。
今天我们就来聊聊车载数据集怎么构建。我会结合BDD100K和SODA10M这两个主流数据集,讲讲标注规范、数据清洗,以及最让人头疼的不平衡处理。
3.1 BDD100K与SODA10M:两个主流车载数据集
先说说BDD100K。这个数据集是伯克利发布的,10万张图片,涵盖了白天、夜晚、雨天、雪天、雾天等各种场景。我个人习惯把它当作车载感知的入门级基准。为什么?因为它的标注相对规整,类别也全——10大类,包括car、bus、truck、bicycle、motor、person、rider、traffic light、traffic sign、train。
但BDD100K有个问题:小目标太少。比如远处的行人、被遮挡的交通标志,这些在BDD100K里占比不高。而车载场景恰恰最需要关注这些小目标——你想想,一个站在50米外的行人,如果模型漏检了,后果是什么?
再说SODA10M。这个数据集是中科院自动化所发布的,1000万张图片,规模是BDD100K的100倍。它的特点是场景多样性极强,尤其是中国路况——电动车、三轮车、逆行、加塞,这些在BDD100K里几乎看不到。我在项目中遇到过,用BDD100K训练的模型,到了中国城市道路上,对电动车的召回率直接掉了15个点。后来加了SODA10M的数据,才把这个问题解决。
两个数据集的核心差异,我整理了一张表:
| 维度 | BDD100K | SODA10M |
|---|---|---|
| 图片数量 | 10万 | 1000万 |
| 场景覆盖 | 欧美为主,天气多样 | 中国路况,极端场景丰富 |
| 小目标占比 | 较低 | 较高 |
| 标注质量 | 高,人工校验 | 中等,部分自动标注 |
| 适用场景 | 通用车载感知 | 复杂交通环境 |
3.2 针对车载场景的标注规范
标注规范这事,说白了就是定义清楚“什么是你要检测的”。车载场景和通用目标检测不一样,有几个特殊点需要注意。
3.2.1 类别定义要细
通用检测里,一个“car”就完了。但在车载场景里,car要拆成sedan、SUV、van、truck、bus。为什么?因为不同车型的尺寸、形状、运动特性都不一样。我记得有一次,模型把一辆大货车当成了car,结果在跟车距离判断上出了大问题。所以,类别粒度越细,下游任务越安全。
3.2.2 遮挡与截断的处理
车载场景里,遮挡是常态。一辆车被另一辆车挡住一半,或者行人被路灯杆挡住,这些都要标注。我见过一些团队,为了省事,只标注完整的目标。结果模型在遮挡场景下直接摆烂——漏检率飙升。
标注规范里要明确:遮挡超过50%的目标,标注为“遮挡”子类。这样模型可以学到“即使只看到一半,这也是一个目标”。
3.2.3 小目标的标注策略
小目标(比如远处的交通标志、行人)在车载场景里特别重要。但小目标在图像里可能只有几十个像素,标注员很容易漏掉。我的做法是:强制标注所有像素面积大于20x20的目标。低于这个阈值的,可以忽略,因为YOLO系列对小目标的检测能力有限,强行标注反而会引入噪声。
3.3 数据清洗:别让脏数据毁了你的模型
数据清洗,说白了就是把垃圾数据扔掉。车载数据集里常见的脏数据有这几类:
- 模糊图片:运动模糊、对焦不准,这些图片模型学不到有效特征。
- 重复图片:连续帧里高度相似的图片,会造成数据冗余。
- 标注错误:框不对、类别标错、漏标,这些是最大的坑。
- 极端光照:过曝或过暗的图片,模型很难处理。
我一般用三步走策略:
- 自动清洗:用Laplacian算子检测模糊度,低于阈值的直接删除。用感知哈希算法检测重复图片,保留一张即可。
- 人工抽检:随机抽5%的图片,人工检查标注质量。如果错误率超过2%,整批数据打回重标。
- 模型辅助:用预训练模型跑一遍,把置信度低于0.3的检测结果标出来,人工确认是否是漏标。
3.4 不平衡处理:让模型学会“雨露均沾”
不平衡问题,是车载数据集里最头疼的。你想想看,一辆车在路上跑,90%的时间都是正常行驶,只有10%的时间会遇到行人、自行车、交通标志。如果直接拿原始数据训练,模型会变成“car检测器”——对car的精度很高,但对其他类别的召回率惨不忍睹。
不平衡处理,我常用的方法有这几种:
3.4.1 重采样
简单粗暴,但有效。对少数类(比如行人、自行车)进行过采样,对多数类(比如car)进行欠采样。但要注意,过采样不能简单复制,否则模型会过拟合。我一般用数据增强的方式过采样——对少数类图片做随机裁剪、旋转、颜色抖动,生成新的样本。
3.4.2 类别权重
在损失函数里给不同类别加权重。比如,car的权重设为1,行人的权重设为5。这样模型在训练时会更关注行人。YOLOv5的损失函数里就支持类别权重,直接设置就行。
# YOLOv5 类别权重设置示例
class_weights = [1.0, 5.0, 3.0, 2.0, 4.0] # 对应 car, person, bicycle, traffic_light, traffic_sign
model.class_weights = torch.tensor(class_weights).to(device)
3.4.3 困难样本挖掘
这是我最喜欢的方法。先用模型跑一遍训练集,找出那些预测错误的样本——比如把行人误检为car,或者漏检了远处的交通标志。把这些困难样本挑出来,单独训练一个epoch。这样模型就能“补课”,学会处理那些它不擅长的场景。
3.5 实战建议:从数据到模型的闭环
最后,我想分享一个实战中的闭环思路。数据集的构建不是一次性的,而是迭代的。你训练一个模型,上路测试,发现某些场景表现不好。这时候,你要回到数据集,补充这些场景的数据,重新标注、清洗、训练。如此循环,模型才会越来越强。
我现在的团队,每个月都会做一次数据复盘。把模型在实车测试中漏检的案例收集起来,分析是数据问题还是模型问题。如果是数据问题,就补充数据;如果是模型问题,就调整网络结构。这样持续优化,半年下来,模型的mAP从0.72提升到了0.85。
嗯,数据集的构建就聊到这里。下一章,我们会讲数据增强——怎么用有限的数据,生成无限的变化。到时候再细聊。