过拟合、小物体难检?深入复盘一个真实垃圾检测项目的调参踩坑记录
过拟合与小物体检测困境:一个垃圾识别项目的深度调参实战
站在深圳某科技园的落地窗前,望着楼下分类细致的垃圾桶,我突然意识到:AI垃圾分类系统要真正落地,必须跨过小物体检测和模型过拟合这两座大山。去年带队参加某数据创新大赛时,我们的YOLOv3模型在测试集上mAP始终卡在0.74,特别是瓶盖、烟头等小物体检测效果惨不忍睹。更令人沮丧的是,那些论文里吹得神乎其神的DropBlock、Focal Loss等技巧,在我们的数据集上统统失效。本文将还原我们如何通过数据解剖、损失函数手术和模型架构调整,最终将小物体检测mAP提升40%的全过程。
1. 问题诊断:为什么常规技巧会失效
1.1 数据分布的致命陷阱
当我们第一次用seaborn画出标注框的宽高分布时,立刻明白了问题所在:数据集中约35%的标注框面积小于32×32像素,但同时存在大量占据图像1/4面积的大型物体。这种极端的尺度差异导致:
import matplotlib.pyplot as plt plt.scatter(annotations['width'], annotations['height']) plt.xlabel('Bounding Box Width') plt.ylabel('Bounding Box Height')更糟糕的是,小物体主要集中在"烟蒂"、"瓶盖"等少数类别。统计显示:
- 烟蒂:平均面积28×25像素
- 塑料袋:平均面积120×90像素
这种类别-尺度耦合现象,使得单纯的类别重采样或损失加权都难以奏效。
1.2 过拟合背后的真实原因
验证集损失曲线在20个epoch后就开始震荡上升,但添加L2正则化(weight_decay=0.01)后效果微乎其微。通过Grad-CAM可视化发现,模型对大物体的关注点集中在边缘特征,而小物体则依赖背景噪声:
| 现象 | 可能原因 | 常规方案失效分析 |
|---|---|---|
| 大物体检测稳定 | 特征提取充分 | - |
| 小物体检测波动大 | 低分辨率特征丢失 | Focal Loss加重样本不平衡 |
| 验证集损失早熟上升 | 背景噪声过拟合 | DropBlock破坏关键特征 |
| 特定类别持续低mAP | 标注质量不一致 | 数据增强无法创造新信息 |
2. 小物体检测的破局之道
2.1 特征金字塔重构方案
放弃原生的YOLOv3特征金字塔结构,我们借鉴BiFPN思想重构了多尺度特征融合路径:
# 自定义的双向特征金字塔 class EnhancedFPN(nn.Module): def __init__(self, in_channels): super().__init__() self.top_down = nn.ModuleList([ ConvBlock(in_channels[i], 256) for i in range(3) ]) self.bottom_up = nn.ModuleList([ nn.Sequential( nn.Upsample(scale_factor=2), ConvBlock(256, 256) ) for _ in range(2) ])关键改进点:
- 在P3(1/8下采样)层添加额外的跳层连接
- 使用可学习的特征权重替代简单相加
- 在浅层引入轻量级注意力机制
2.2 动态样本加权策略
抛弃固定的(2-w*h)加权公式,我们设计了一套动态调整策略:
重要发现:小物体检测效果与当前batch中的尺度分布强相关。当batch内大物体占优时,小物体梯度会被压制。
解决方案:
- 实时统计batch内物体尺度分布
- 根据百分位动态调整损失权重
- 对极端小物体(<16px)启用特殊增强
def dynamic_weight(loss, targets): areas = (targets[:,3]-targets[:,1]) * (targets[:,4]-targets[:,2]) quartiles = torch.quantile(areas, torch.tensor([0.25,0.75])) mask = areas < quartiles[0] loss[mask] *= (1 + (quartiles[1]/areas[mask]).clamp_max(3.0)) return loss3. 过拟合问题的系统解法
3.1 对抗性数据增强组合
经过大量实验,我们发现以下组合对垃圾检测最有效:
局部粘贴增强(Partial Stitching)
- 随机选择小物体标注框
- 复制到其他图像的合理位置
- 添加光照一致性调整
物理仿真遮挡
def add_occlusion(img, bboxes): for box in bboxes: if random() < 0.3: x1,y1,x2,y2 = box img[y1:y2,x1:x2] = simulate_dirt(img[y1:y2,x1:x2]) return img频域混合增强(Frequency Domain Mixup)
- 对两幅图像分别做FFT变换
- 交换高频成分
- 逆变换生成新样本
3.2 网络结构针对性调整
在YOLOv3-SPP基础上,我们做了三处关键修改:
早层特征保留
在Darknet-53的stage2后添加旁路分支,将高分辨率特征直接传递到检测头。解耦检测头设计
将分类和回归任务分离,避免特征互相干扰:Original Head: [Conv] -> [Class+Box预测] New Design: [Conv] -> [Class分支] [Conv] -> [Box分支]梯度过滤机制
在反向传播时,对疑似背景噪声的梯度进行抑制:def backward_hook(module, grad_in, grad_out): grad_out = grad_out * foreground_mask return grad_out
4. 效果验证与生产部署
4.1 量化评估指标对比
在测试集上的提升效果:
| 指标 | Baseline | 改进后 | 提升幅度 |
|---|---|---|---|
| mAP@0.5 | 0.74 | 0.83 | +12.2% |
| 小物体mAP | 0.51 | 0.72 | +41.2% |
| 推理速度(FPS) | 38 | 32 | -15.8% |
特别值得注意的是,"烟蒂"类别的检测精度从0.48跃升至0.67,证明我们的尺度针对性优化确实有效。
4.2 实际部署中的工程优化
为了平衡精度和推理速度,我们最终采用了两阶段部署方案:
前端轻量级筛选
使用裁剪后的YOLOv3-tiny快速定位可能包含垃圾的区域后端精细检测
只对候选区域运行完整模型,关键配置参数:deployment: roi_threshold: 0.3 max_candidates: 10 min_object_size: 8
这套系统目前在深圳某区的智能垃圾房运行,平均识别准确率达到89.7%,最让我们自豪的是,那些曾被忽视的烟头、瓶盖等小物件现在也能被准确识别。记得第一次看到系统成功标记出嵌在落叶堆里的烟蒂时,整个团队都忍不住欢呼——这或许就是算法工程师最纯粹的快乐。
