返回列表

5th place solution

687. CSIRO - Image2Biomass Prediction | csiro-biomass

开始: 2025-10-28 结束: 2026-01-28 作物智能识别 数据算法赛
第五名解决方案 - CSIRO Biomass

第五名解决方案

作者: Kinosuke (chiman3se)
发布时间: 2026-01-31
竞赛: CSIRO Biomass

感谢主办方举办这次比赛。这是一个直接的图像任务,我非常享受参与的过程。

概述

DINOv3 的密集特征(dense features)被证明是非常强大的。我的方法侧重于有效地利用这些特征,同时尽可能保留预训练权重。
使用全局特征(CLS)会丢弃 DINOv3 强大的局部分离能力,因此我将模型设计得尽可能接近分割任务。

  • 使用 DINOv3 的密集特征进行端到端训练
  • 渐进式解冻(Gradual Unfreeze)高达 50% — 不进行全量微调
  • 将 2000x1000 的图像一分为二,并将标签也减半,从而使训练数据翻倍
  • 为了应对榜单波动(Shake robustness):6 个模型 x 2 个种子的集成,涵盖 Large/Huge+ 及多种分辨率
  • 推理优化(T4x2 并行推理,FP16)以适应 9 小时的时间限制
模型 分辨率 CV (OOF R²) 公开榜 私有榜
DinoV3 Huge+ (child-exp012) 672 0.809 0.75 0.65
DinoV3 Huge+ (child-exp017) 768 0.816 0.75 0.65
DinoV3 Huge+ (child-exp026) 672 + PatchDropout 0.811 0.75 0.65
DinoV3 Huge+ (child-exp032) 864 0.814 0.75 0.65
DinoV3 Large (child-exp020) 864 0.818 0.73 0.66
DinoV3 Large (child-exp037) 960 0.819 0.74 0.65

模型

为什么选择密度估计?

当使用 PCA 可视化 DINOv3 特征时,你可以看到即使是零样本(zero-shot),DINOv3 在局部分离方面也表现出色。它可以清晰地区分 Green(绿草)、Clover(三叶草)、Dead(枯草,部分)和 Soil(土壤)。

关注 Clover:
Focus on Clover

关注 Dead:
Focus on Dead

注意:我特意选择了视觉上分离清晰的样本。并非所有样本都能如此清晰地分离。

尽管具有出色的局部分离能力,但在 CLS token 上附加一个头会丢弃空间信息,这感觉很浪费。相反,我采用了密度估计(Density Estimation)方法:从每个 patch token 预测局部生物量密度,并在整个图像上求和(积分)。

架构:仅密度头 (Density-Only Head)

输入图像 (1000x1000, 左右分割的一半)
    | 调整大小
输入 (672x672x3)
    |
DINOv3 ViT-Huge+ 骨干网络 (冻结 -> 渐进式解冻)
    |
Patch tokens [B, 1764, 1280]  (42x42 patches, 每个 1280 维)
    | 重塑
[B, 1280, 42, 42]
    |
1x1 Conv (1280 -> 5)   <- 这里只有 6,405 个可学习参数
    |
Softplus (非负约束:生物量密度在物理上是非负的)
    |
密度图 [B, 5, 42, 42]  (每个 patch 的 5 个目标的局部密度)
    |
Sum (聚合所有 patches)
    |
预测 [B, 5] = [Green, Dead, Clover, GDM, Total]

关键点:

  • 不使用 CLS token。预测完全依赖于 patch tokens(仅密度)
  • 1x1 Conv 将每个 patch 的 1280 维特征线性转换为 5 个生物量密度值(无空间卷积)
  • Softplus 强制非负性,每个 patch 代表“该区域有多少生物量”
  • 最终对所有 patches 求和编码了物理关系:生物量 = 密度 x 面积

让训练收敛具有挑战性。我尝试过 Conv3x3 来学习相邻 patches 之间的关系,但无论怎么调整学习率都无法解决 NaN loss 问题。只有使用更简单的 Conv1x1 训练才稳定下来。我还必须监控前 5-8 个 epoch 的训练,检查验证分数是否稳定,手动停止那些没有收敛的运行。

分辨率

分辨率至关重要。更高的分辨率 consistently 提高了 CV 和 LB 分数。我从 224 开始,最终达到了 960。
更高的分辨率需要更多的训练时间和显存,所以我从 256 开始实验,逐渐增加到 512,然后在比赛的最后 3 周增加到 768。最终模型在 Large 上使用 960px,在 Huge+ 上使用 864px。在 864px 上训练 Huge+ 需要 20 小时(5 折),使用 76GB 显存,batch size 为 8 (Colab A100 80GB)。

训练策略

渐进式解冻(Gradual Unfreeze)是关键。对于像 DINOv3 这样具有强大预训练权重的模型,从一开始就进行全量微调会破坏宝贵的预训练表示。

渐进式解冻

  • Epochs 1-5:仅训练头,使用较高的学习率。骨干网络冻结。
  • Epochs 6-40:从最靠近头的层开始逐渐解冻层。

例如,DINOv3 Large 总共有 24 个 Transformer 块。设置 max_unfreeze_ratio=50%,到第 40 个 epoch 时最多解冻 12 个块。
一个重要的设计选择是不使用学习率调度器。大多数调度器应用 warmup 然后逐渐衰减,但使用这种策略时,后期解冻的层将无法获得足够的学习。相反,我线性增加了骨干网络的学习率:

  • 开始 (epoch 5): 1e-5
  • 结束 (epoch 40): 3e-5

数据分割(左右减半)

许多公共 notebook 将 2000x1000 的图像分为左右两半,然后在输入到头之前连接最终层的输出。
我的方法不同:分为左右两半并将标签也减半,有效地使训练数据翻倍。这种方法在 DINOv3、EVA02-CLIP 和 SigLIP 上始终优于 2-stream 连接方法。

损失函数

基础损失是 SmoothL1Loss,预测所有 5 个目标。
关键补充是ConsistencyLoss(一致性损失),它强制执行物理关系:

  • Total = Green + Dead + Clover
  • GDM = Green + Clover

我最初认为只预测 3 个独立目标(Green, Dead, Clover)并推导其余目标会更高效。然而,由于 Clover 和 Green 之间似乎存在一些标签噪声,预测所有 5 个目标并加上一致性约束可以作为有效的正则化。

CV 策略

采用来自此讨论帖的基于组的方法稳定了 CV-LB 相关性。
由于 Clover 和 Dead 难以预测,我使用分层(stratification)将它们均匀分布到各折中:

df_wide['clover_dead_presence'] = (
    (df_wide['Dry_Clover_g'] > 0).astype(int).astype(str) + '_' +
    (df_wide['Dry_Dead_g'] > 0).astype(int).astype(str)
)

这被用作 StratifiedGroupKFold 的分层变量,确保 Clover 或 Dead 值为零的样本均匀分布在各折中。由于许多样本的这些目标值为零,简单的随机分割可能会导致某些折出现偏差。

Clover Dead 含义
0_0 无 (=0) 无 (=0) Clover 和 Dead 均为零
0_1 无 (=0) 存在 (>0) 仅 Dead
1_0 存在 (>0) 无 (=0) 仅 Clover
1_1 存在 (>0) 存在 (>0) 两者都存在

这使得 CV-LB 相关性非常稳定。

CV-LB Scatter Plot

数据增强

使用的增强方法:

HorizontalFlip (p=0.5)
VerticalFlip (p=0.5)
RandomRotate90 (p=0.5)
Rotate (limit=10, p=0.3)
RandomResizedCrop (scale=0.85-1.0, ratio=0.95-1.05, p=0.5)
ColorJitter (brightness=0.3, contrast=0.3, saturation=0.3, hue=0.15, p=0.7)
RandomGamma (gamma_limit=80-120, p=0.3)
RandomBrightnessContrast (brightness=0.25, contrast=0.25, p=0.5)
GaussianBlur (blur_limit=3-5, p=0.2)
RandomShadow (p=0.1)
RandomToneCurve (p=0.2)

关于 RandomResizedCrop:我最初认为不应该使用它,因为它会改变图像中可见的草量。然而,它实际上提高了准确率。这可能是因为不同摄影师的相机到地面的距离不同,这种增强提高了对此类变化的鲁棒性。

过于激进的增强会降低性能:

  • 主办方的论文提到图像是用各种相机类型拍摄的,所以我尝试添加多样的噪声增强,但做得太过分会损害性能。
  • RandomGridShuffle 也没有帮助。

榜单波动缓解 (Shake Mitigation)

2-Seed 集成

鉴于数据集较小且是回归任务,预计会有显著的榜单波动。受Feedback Prize 竞赛中 Psi 的讨论启发,我使用 2 个种子进行训练和评估,以验证模型是否实现了真正的泛化并提高鲁棒性。

模型比较

我从比赛早期就开始参与,逐步升级模型:SigLIP -> EVA02-CLIP -> DINOv3。
我一开始并不知道 DINOv3 会这么强。我通常更喜欢基于 CLIP 的模型,但在这次比赛中 DINOv3 具有压倒性的优势。
以下所有模型都使用相同的训练管道(增强、学习率、渐进式解冻)。仅调整了 batch size 以适应显存。

模型 CV 公开榜 私有榜
SigLIP 0.75 0.66 0.55
EVA02-CLIP 0.75 0.69 0.584
DINOv3 [全局特征] 0.79 0.73 0.64
DINOv3 [密集特征] 0.81 0.75 0.66

无效的方法

  • TTA — 没有改进
  • 滑动窗口推理 — 不是左右分割,而是滑动 500px 进行 4 次传递。没有帮助。
  • 数据清洗 — "WA/2015-8-21" 日期的标签明显有噪声。手动修正它们提高了 CV 但降低了 LB。测试数据可能包含相同的标签错误。
  • 辅助损失 — 使用元数据(Height, Species, Season)作为辅助目标没有帮助。
  • Depth Anything — 深度图与 train.csv 中的 Height 显示出强相关性。我尝试将深度与密度图相乘以结合每个 patch 的高度信息,但没有改善结果。
  • EMA — 没有起作用,尽管我的实现可能不正确。我没有进一步调查。
同比赛其他方案