本篇文章为比赛第一名方案解析,该比赛涉及到CV-目标检测-农业方向。
介绍:参赛者将从小麦植物的户外图像中检测小麦穗,包括来自全球各地的小麦数据集,利用世界范围内的数据,以估计小麦穗的数量和大小。
网址:www.kaggle.com/c/global-wh…
重点
- Custom mosaic data augmentation
自定义马赛克数据增强 - MixUp 混合
- Heavy augmentation 重度增强
- Data cleaning 数据清洗
- EfficientDet 高效检测
- Faster RCNN FPN 更快的 RCNN FPN
- Ensemble multi-scale model 集成多尺度模型
- Test time augmentation(HorizontalFlip, VerticalFlip, Rotate90)
测试时间增强(水平翻转,垂直翻转,旋转 90 度) - Pseudo labeling 伪标签
数据处理
- 自定义马赛克增强
马赛克是一种数据增强方法,将 4 张训练图像合并成一张进行训练(而不是 CutMix 中的 2 张)。不是随机裁剪图像的一部分,我创建了以下自定义马赛克增强,以保留边界信息:
- 混合操作
- 重度增强:
随机裁剪,水平翻转,垂直翻转,转为灰度,添加高斯噪声,高斯噪声,运动模糊,中值模糊,模糊,对比度受限自适应直方图均衡化,锐化,浮雕,随机亮度对比度,色调饱和度值
- 外部数据
我使用了 2 个外部数据:
小麦穗(www.kaggle.com/c/global-wh…
小麦 2017(plantimages.nottingham.ac.uk/)在帖子中(https…
我为所有图像创建标注(边界框)并裁剪为 1024x1024。我联系了作者以确保没有版权问题。 - 数据清洗
删除了宽度或高度小于 10 像素的小边界框
修复了过大的边界框
模型
- 5 折,分层-k 折,按来源分割(usask_1,arvalis_1,arvalis_2…)
- 优化器:EfficientDet 使用初始学习率 5e-4 的 Adam,Faster RCNN FPN 使用初始学习率 5e-3 的 SGD
- LR 调度器:余弦退火
- 混合精度训练与 nvidia-apex
性能
有效 AP/公共 LB AP
- EfficientDet-d7 图像大小 768:Fold0 0.709/0.746,Fold1 0.716/0.750,Fold 2 0.707/0.749,Fold3 0.716/0.748,Fold4 0.713/0.740
- EfficientDet-d7 图像尺寸 1024:Fold1,3
- EfficientDet-d5 图像尺寸 512:Fold4
- Faster RCNN FPN-resnet152 图像大小 1024: 第 1 折
- 集成使用 wbf 的 9 个模型可以达到 0.7629 公共 LB/0.7096 私有 LB(旧测试集)
伪标签
- 基础:EfficientDet-d6 图像尺寸 640 Fold1 0.716 有效 AP
- 第一轮:使用 trainset + 隐藏测试集(集成输出)高效 Det-d6 训练 10 个 epoch,从基础检查点加载权重
结果:[旧测试集] 0.7719 公共 LB/0.7175 私有 LB 和 [新测试集] 0.7633 公共 LB/0.6787 私有 LB - Round2: 继续使用 trainset + 隐藏测试集(伪标签轮次 1 的输出)训练 EfficientDet-d6 6 个 epoch,从伪标签轮次 1 的检查点加载权重
结果:[旧测试集]0.7754 公共 LB/0.7205 私有 LB 和 [新测试集]0.7656 公共 LB/0.6897 私有 LB
源代码
以上为方案思路,便于大家有一个整体的概念,现在我来讲讲源代码中一些比较重要的内容。
重点代码讲解
一、数据处理
该方案能拿到这么高的分数,其中最重要的原因之一就是在训练时对数据进行了非常细致的清洗,这也是这个方案的精妙之处。
1. 过滤无效标注(isbox过滤)
- 验证集处理:
valid_df = valid_df.loc[valid_df['isbox']==True]
这一操作过滤了验证集中isbox
为False
的样本,确保验证集仅包含有效标注的边界框数据。
valid_df = valid_df.loc[valid_df['isbox']==True].reset_index(drop=True)
warm_df = pd.concat([train_df, wheat2017_df, spike_df], ignore_index=True).sample(frac=1).reset_index(drop=True)
train_df = pd.concat([train_df, wheat2017_df], ignore_index=True).sample(frac=1).reset_index(drop=True)
2. 数据合并与采样
- 组合外部数据:将
wheat2017_df
和spike_df
合并到训练数据中,例如:
warm_df = pd.concat([train_df, wheat2017_df, spike_df], ignore_index=True)
train_df = pd.concat([train_df, wheat2017_df], ignore_index=True)
- 随机打乱:通过
sample(frac=1).reset_index(drop=True)
对合并后的数据进行随机打乱,避免顺序偏差。
3. 潜在的隐含清洗
-
数据集类(WheatDataset):
- 跳过无效图像路径或损坏的图像文件。
- 过滤空标签(无边界框的样本),尤其是在训练模式(
mode='train'
)下。
通过以上这些步骤可以增加数据集的可用性,去除大量的伪标签。
二、模型融合
这个方案是20年的,所使用的模型也比较老了。原方案所使用的模型是faster_rcnn融合后的模型,对于25年的读者来说,将方案多次融合的模型换成简单的viT就可以得到相近的效果。
三、提分思路
对于这么一个高分的方案,我们想要再进行提分肯定是不容易的。读者可以将模型加入近期大火的MOE模型。我简单的跑了一下代码,在不做过多调参的情况下可以达到接近原方案的分数,相信根据新模型调整后能得到更好的表现,读者根据此思路也可自行进行尝试。