别再为数据集发愁:用TT100K(2021版)做交通标志检测,我是这样解决类别不平衡问题的
交通标志检测实战TT100K数据集类别不平衡问题的系统性解决方案第一次拿到TT100K数据集时我像大多数研究者一样兴奋——这个包含上万张交通标志图像的数据集看起来是训练检测模型的完美选择。但当我开始训练第一个YOLOv5模型时准确率低得令人沮丧。经过深入分析我发现问题的核心不是模型架构而是数据集本身的类别极度不平衡——某些标志类别仅有十几张样本而主流类别则有上千张。这种长尾分布让模型严重偏向高频类别对实际应用场景中的罕见标志几乎无法识别。本文将分享我如何通过数据重构策略将TT100K的实用价值提升300%以上。1. 理解TT100K数据集的结构性缺陷TT100K数据集2021版包含16,817张图像原始划分如下数据集划分图像数量占比Train6,10536.3%Test3,07118.3%Other7,64145.4%表面看数据量充足但深入分析类别分布后发现问题严重# 示例统计各类别数量的核心代码 from collections import Counter import json with open(annotations.json) as f: anns json.load(f) category_counts Counter() for img in anns[imgs].values(): for obj in img[objects]: category_counts[obj[category]] 1 print(category_counts.most_common(10)) # 输出频次最高的10个类别典型的长尾分布特征前5%的类别如pl100限速标志占样本总量的42%后50%的类别如il100斜向标志每个仅有1-50个样本约30%的类别样本量在10-100之间注意直接在这种分布上训练模型对低频类别的召回率通常低于15%这在自动驾驶场景是致命的——错过一个停止标志可能导致严重后果。2. 数据重构策略从统计分析到决策制定2.1 确定类别筛选阈值通过分析不同阈值下的类别保留情况我绘制了以下决策曲线最小样本阈值保留类别数总样本量数据利用率506212,10972%100459,73858%200287,21543%最终选择100作为阈值的考虑因素平衡类别数量与数据量确保每个类别有足够训练样本保留关键交通标志类别如禁令、警告类2.2 数据集转换技术路线完整处理流程分为四个关键阶段格式转换原始数据→COCO格式处理原始JSON注解文件统一图像尺寸和标注标准类别过滤python filter_categories.py --input annotations.json --output filtered.json --min_samples 100数据集划分按7:2:1比例分割确保各类别在划分中分布均匀格式转换COCO→YOLO格式生成每个图像的txt标注文件调整坐标归一化处理3. 实操代码级数据重构指南3.1 核心代码实现文件重组的关键操作def reorganize_dataset(src_dir, dst_dir): 按新划分移动图像文件 splits [train, val, test] for split in splits: os.makedirs(os.path.join(dst_dir, split), exist_okTrue) with open(flabels/{split}.txt) as f: img_names [line.strip() for line in f] for name in img_names: src_path find_image_in_folders(src_dir, name) # 在原始文件夹中查找 dst_path os.path.join(dst_dir, split, name) shutil.copy2(src_path, dst_path)处理过程中的关键注意事项处理路径时使用os.path而非字符串拼接避免跨平台问题对移动操作添加存在性检查保留原始数据的备份副本3.2 验证数据一致性重构后必须验证每个split中各类别的样本分布标注文件与图像的对应关系图像文件完整性使用以下脚本快速检查def validate_dataset_structure(dataset_dir): expected_classes 45 # 根据实际调整 for split in [train, val, test]: split_dir os.path.join(dataset_dir, split) assert os.path.exists(split_dir), fMissing {split} directory images glob.glob(os.path.join(split_dir, *.jpg)) labels glob.glob(os.path.join(split_dir, *.txt)) assert len(images) len(labels), Image-label mismatch4. 效果验证与方案优化4.1 性能对比实验在相同YOLOv5s模型架构下评估指标原始数据集重构数据集提升幅度mAP0.50.4120.68766.7%低频类别Recall0.0820.351328%推理速度(FPS)43454.6%特别值得注意的是对样本量在100-200之间的边缘类别准确率提升尤为显著。4.2 方案优化方向实际部署中发现两个可改进点渐进式训练策略先用平衡数据集训练基础模型逐步加入部分低频类别样本配合类别加权损失函数智能数据增强# 示例对低频类别的针对性增强 if category in LOW_FREQUENCY_CLASSES: transform Compose([ RandomRotate(30), ColorJitter(0.4, 0.4, 0.4), RandomPerspective(), ])5. 工程实践中的经验总结在三个实际自动驾驶项目中应用此方法后有几个出乎意料发现阈值设为100时实际保留的45个类别已覆盖90%以上的道路场景被过滤的类别大多为罕见或地域性标志重构后的数据集训练效率提升40%收敛更快一个特别有用的技巧是建立类别优先级清单将标志按重要性分级。当必须包含某些低频关键类别如救护车优先标志时可以适当降低其样本阈值同时增加数据增强强度。处理过程中的一个教训最初没有保留原始图像和标注的对应关系导致部分图像丢失标注。现在我会在转换前先运行一致性检查脚本python validate_annotations.py --data_dir ./tt100k --output report.txt