687. CSIRO - Image2Biomass Prediction | csiro-biomass
这是我参加过的最令人沮丧的比赛之一。一切感觉都非常随机。我尝试了无数种方法,但只有少数几种真正有效(或者也许并没有?😂)。CV(交叉验证)和 LB(排行榜)波动不可预测。最后,我别无选择,只能将所有尝试过的模型进行集成——甚至不知道它们是否真的有帮助——并希望榜单变动最小化。
现在私有排行榜已经揭晓,我重新审视了所有的实验。这终于让我更有信心区分哪些方法真正有效,哪些无效。我将忽略许多证明无效的方法,仅尽可能简洁地总结有用的发现:
我的集成模型中主要包含两种变体:


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)的方法:


在增强数据处理中,训练期间随机生成一个滑动起点,从中仅切出 6 个图块(加上 2 个掩码图块)作为输入。这种方法允许模型看到更多的图像变化,而不是固定的 8 图块分区。当上述方法用于模型结构 B 型(图块级头部)时,目标值需要乘以 6/8=0.75,因为较少的图块本质上意味着较少的生物量。
数据加载器中使用增强切片的概率设置为 0.5,其损失权重也设置为 0.5。
还有一些标准的训练设置,它们本身并非决定性因素,但根据我的经验证明是最优的:
训练分为两个阶段,早停耐心值为 12:
其他设置:
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,)
我选择了两个提交方案:
最终它们都达到了 0.64 的私有 LB。然而我最好的提交是使用上述技术的单个模型,在私有 LB 上获得了 0.65。唉~