返回列表

20th (public 25th) Soluion - simple model + data augmentation

687. CSIRO - Image2Biomass Prediction | csiro-biomass

开始: 2025-10-28 结束: 2026-01-28 作物智能识别 数据算法赛
第 20 名(公开榜第 25 名)解决方案 - 简单模型 + 数据增强

第 20 名(公开榜第 25 名)解决方案 - 简单模型 + 数据增强

作者: Yannan Chen
发布时间: 2026-01-29
竞赛: CSIRO Biomass

这是我参加过的最令人沮丧的比赛之一。一切感觉都非常随机。我尝试了无数种方法,但只有少数几种真正有效(或者也许并没有?😂)。CV(交叉验证)和 LB(排行榜)波动不可预测。最后,我别无选择,只能将所有尝试过的模型进行集成——甚至不知道它们是否真的有帮助——并希望榜单变动最小化。

现在私有排行榜已经揭晓,我重新审视了所有的实验。这终于让我更有信心区分哪些方法真正有效,哪些无效。我将忽略许多证明无效的方法,仅尽可能简洁地总结有用的发现:


模型结构

我的集成模型中主要包含两种变体:

A 型(头部网络接收平均池化特征作为输入):

Model Type A

B 型(头部网络接收图块级特征作为输入):

Model Type B

A 型 基本上采用自公开笔记本,其中 FiLM 层是最有效的组件:

class FiLM(nn.Module):
    def __init__(self, feat_dim):
        super().__init__()
        hidden = max(32, feat_dim // 2)
        self.mlp = nn.Sequential(
            nn.Linear(feat_dim, hidden),
            nn.ReLU(inplace=True),
            nn.Linear(hidden, feat_dim * 2)  # output gamma + beta
        )
    def forward(self, context):
        gamma_beta = self.mlp(context)
        return torch.chunk(gamma_beta, 2, dim=1)  # (gamma, beta)

我简化了原始方法,丢弃了左右特征拼接等技术,我认为这是不必要的,因为图像中没有固有的左右结构。

B 型,根据我的实验,在 CV 上始终表现更优,在 LB 上也略好。它利用了一种直觉,即生物量重量可以在图块级别进行估计,然后聚合以获得最终预测。与仅依赖输入到预测头部的平均池化特征的 A 型相比,B 型允许头部捕获更丰富的局部信息,从而获得更准确的最终结果。

关于头部的变体,我尝试了 3 个头(green, gdm, total)、4 个头(green, clover, gdm, total)和 5 个头。5 个头的结果最差。3 个头和 4 个头在 CV 和 LB 上几乎相同,所以我也将它们都包含在我的集成模型中。

总的来说,我的心得是骨干网络才是真正驱动排行榜性能的关键——ViT Huge > ViT Large > ViT Small。除此之外,鉴于图像尺寸和数据集较小,添加更复杂的结构(注意力机制、gating、平均 - 最大池化等)毫无帮助。保持简单和优雅的效果最好。


数据增强

这是最棘手的部分,我尝试了无数种方法。这是我发现的唯一能稍微改善 CV 和公开 LB(以及私有 LB)的方法:

常规切片:

Typical Tiling

增强切片:

Augmented Tiling

在增强数据处理中,训练期间随机生成一个滑动起点,从中仅切出 6 个图块(加上 2 个掩码图块)作为输入。这种方法允许模型看到更多的图像变化,而不是固定的 8 图块分区。当上述方法用于模型结构 B 型(图块级头部)时,目标值需要乘以 6/8=0.75,因为较少的图块本质上意味着较少的生物量。

数据加载器中使用增强切片的概率设置为 0.5,其损失权重也设置为 0.5。


训练设置

还有一些标准的训练设置,它们本身并非决定性因素,但根据我的经验证明是最优的:

训练分为两个阶段,早停耐心值为 12:

  • 在第一阶段,仅训练 FiLM 层和头部,学习率从 1e-3 降至 2e-5,共 8 个 epoch
  • 在第二阶段,骨干网络解冻,学习率从 2e-5 降至 1e-6,共 50 个 epoch

其他设置:

weight_decay = 0.05
dropout = 0.3
loss_w = [0.1, 0.1, 0.1, 0.1, 0.1] # 顺序为 [green, dead, clover, gdm, total],我发现这比 [0.1, 0.1, 0.1, 0.2, 0.5] 稍好

损失使用 huber_loss 计算:

resid = (preds - targets) / scale  # (B,5)
w = torch.tensor(loss_w, device=preds.device, dtype=preds.dtype)
loss_per_sample = (F.huber_loss(
    resid, torch.zeros_like(resid),
    delta=1.0,
    reduction="none"
    ) * w).sum(dim=1)  # (B,)

提交方案

我选择了两个提交方案:

  • 精心挑选的 12 个模型集成,具有最高的 LB 分数 (0.75~0.76)
  • 26 个模型的集成,具有不错的 LB 分数 (0.74~0.76)。

最终它们都达到了 0.64 的私有 LB。然而我最好的提交是使用上述技术的单个模型,在私有 LB 上获得了 0.65。唉~

同比赛其他方案