返回列表

3rd Place - Attention + XGBoost Ensembler

545. IceCube - Neutrinos in Deep Ice | icecube-neutrinos-in-deep-ice

开始: 2023-01-19 结束: 2023-04-19 物理与天文 数据算法赛

第三名解决方案:Attention + XGBoost 集成

作者:Dipam Chakraborty | 发布日期:2023年4月20日 | 比赛排名:第三名

这是我三年来首次参加Kaggle比赛,也是我第一次获得金牌,度过了一段非常激动人心的时光。😃

查看文末的 TL;Dr 获取简短总结 - 本文包含模型信息和个人经验教训。

惨痛教训 - Attention 就是全部所需

这个数据集并非自然图结构,我对所有基于图模型的方法感到困惑。图中的边是根据时间或距离等标准人为选择的,我认为这不是正确的方法。因此我专注于LSTM,后来转向了自注意力机制。

基础模型

基础模型是一个简单的自注意力模型。大部分模型代码来自 Andrej Karpathy 的 NanoGPT https://github.com/karpathy/nanoGPT.git

输入数据采用了 Datasaurus 的 Dynedge 基准中的归一化脉冲数据和透明度信息 https://www.kaggle.com/code/anjum48/early-sharing-prize-dynedge-1-046 - 序列采样最大长度为256,优先采样 auxiliary=False 的脉冲

输出为每个角度设置两个分类头,各有128个区间。- 我使用自定义损失函数训练,该函数对独热目标向量进行局部平滑,下文将详细说明。

模型的小版本可在 RTX 3060 上训练,1小时内达到约1.02的分数。(嵌入维度128 - 6层)

惨痛教训 - 规模胜过一切

这场比赛无疑需要大量计算资源,但我只能使用配备 RTX 3060 的家庭电脑。自然地,我花了大量时间手工设计特征、优化代码,尝试各种看似有帮助的想法。结果不得不放弃大多数实验,这些方法在小模型上有效,但在使用更多数据训练的大模型上效果被淹没。

曾经有效但后来被放弃的显著想法:

  1. 简单的数据增强,如轻微移动点、改变电荷等
  2. 使用监督对比损失训练预测两个事件之间的角度
  3. 最终使用多种池化代替平均池化(实际上这导致了过拟合)
  4. 在球面坐标上使用角度分类器而非两个分类器

以上所有方法在训练2-3小时的小模型时都提升了性能,但大模型通过规模达到了相同效果。即使使用20批训练数据,模型也没有过拟合。

分数的真正提升来自于模型规模的扩大。使用更多数据进行训练也至关重要,我最终使用的模型在650批数据上训练。它们在完成整个数据的4轮循环后开始过拟合。

所有浪费在实验上的日子让我想起了 Richard Sutton 的博客《The Bitter Lesson》 http://www.incompleteideas.net/IncIdeas/BitterLesson.html - 哈哈,GPU 也很惨痛啊。

扩展和工程技巧

四月初,我决定购买新GPU来看看需要多少计算资源。升级到 RTX 4080,速度提升了3倍。(不,这不是我做的唯一扩展 😅,但更多计算资源至关重要)

最终提交模型有18个自注意力层,嵌入维度512。这达到了0.982的LB分数,我相信训练更大的模型会更好。我用15层训练了相同模型,但由于混合精度不稳定导致性能更差。

工程技巧

  1. 混合精度训练快得多,我使用FP16配合FlashAttention训练。但在达到0.990分数后变得不稳定,所以我之后切换到FP32。只有更大的模型出现了这种不稳定性,我没能研究如何在FP16下继续,可能是输入或损失的规模需要调整。
  2. 数据集中的序列长度差异很大。采样到256个脉冲后,每个事件的平均长度接近100。这意味着很多注意力计算(当不使用FlashAttention时)浪费在填充上。为加速训练,我按事件长度分组批次。这比FlashAttention快1.5倍,比普通注意力实现快3.5倍。打包也以相同比例加速推理。
  3. 每"轮"加载部分训练数据。这很简单,在每轮后更换数据加载器中的批次,或作为后台进程预取。

扩展效果惊人:

  1. 128嵌入 - 9层 - 200批 - 分数约1.001
  2. 256嵌入 - 12层 - 400批 - 分数约0.990
  3. 512嵌入 - 18层 - 650批 - 分数约0.982(最后有些过拟合)

此外,我针对最大3072的序列长度微调了模型,这带来了0.002的微小改进。

平滑交叉熵损失

Von Mises-Fisher 损失与注意力模型结合时不稳定,端到端训练从未超过1.04。

对于分类器,我将角度数字化为128个区间。交叉熵损失效果良好,但我想引入归纳偏置,让模型知道类别是有序的而非独立。为此,我对目标独热向量应用带高斯核的1D卷积。对于方位角,这种卷积有环绕;对于天顶角,我根据核长度扩展区间。(稍后发布解释损失的笔记本)这带来了略微更稳定的训练,但通过良好的超参数调整,交叉熵也可能同样有效。

甜蜜结局 - Attention 并非全部所需

显然注意力不足以获胜,0.982甚至不在金牌区(在角落哭泣)。然而,基础模型对后续想法的成功至关重要。

堆叠vMF模型

在编码器嵌入上训练带vMF损失的MLP是有效的。我转储100批平均池化的编码器嵌入并用其训练堆叠模型,达到了约0.978的分数。vMF模型与基础模型的简单平均集成得到了约0.976的分数。

魔法 - 使用XGBoost集成 🔥

我知道标准的集成技巧,但这是我第一次使用另一个模型进行集成。如果您知道这在实践中被使用,请告诉我是否有相关文献。我在比赛结束前3天才想到这个点子,几乎没来得及充分测试,但它像魔法一样奏效。

我假设它有效的原因 - 分类器和vMF模型远未达到完美分数,且存在高度分歧。以下是天顶角的分直方图。

两个模型都有自己的偏差,如何组合它们并不明显。

首先我尝试了一个简单的投票规则 - 如果它们相差太大就不平均:
这已经将分数从0.976提升到0.973,已是前5的解决方案。

avg_zenith = (zn_stack + zn_base) / 2
vote_zenith = avg_zenith.clone()
voter = torch.abs(zn_stack - zn_base) > 0.4
vote_zenith[voter] = zn_stack[voter].clone()

两个模型都有一些指示预测"置信度"的方法。分类器的区间概率和vMF的kappa值。我尝试了这些阈值以及一些事件长度和z坐标,将分数提升到0.9715,所以我决定尝试XGBoost。第一次尝试分数直接达到了0.969。 🚀

此外,我尝试了另一个提升分类器来"预测哪些预测是坏的",并用它来翻转角度,这也带来了轻微的分数提升。

添加更多简单特征后,18层模型的最终分数为0.965(我提交了一个15层模型的方案,得分为0.967)。

我最终使用了sklearn的HistGradientBoostingClassifier,但XGBoost有类似的结果。

推理时间统计

基础模型的推理时间约为每批4分钟,长序列模型又需4分钟,这只能带来很小的提升。因此有可能在25分钟内获得0.967的分数

最终提交

最终提交包含3个模型:18层、15层,以及来自同一18层运行的另一个检查点。

另一个提升分类器用于合并18层和15层模型,然后使用简单投票合并3个模型。

以下是最终提交和单注意力模型的推理笔记本:

  1. 最终方案 - https://www.kaggle.com/code/dipamc77/3rd-place-attention-xgboost-ensembler
  2. 仅注意力模型 - https://www.kaggle.com/code/dipamc77/single-attention-model-inference

学习总结

  1. 在尝试特征工程/其他想法前,先测试模型的扩展能力,尤其是欠拟合时
  2. 初期投入工程精力优化流程,以便快速测试想法
  3. 如果模型间分歧大且未达到最高分,尝试训练分类器来集成模型
  4. 尽管最终提升来自集成技巧,扩展才是最重要的。更大模型很可能带来更好结果
  5. 不要放弃,我在最后阶段尝试了各种方法提升分数!
  6. 购买更多GPU。 💸

事后看来,我应该尝试一些图模型,因为它们可能与分类器有分歧,集成它们可能带来良好改进。

值得注意的失败想法

没想到这些会失败,也没有过早放弃,但无法让它们生效:

  1. 寻找数据集中的噪声以在训练中忽略
  2. 注意力模型的vMF损失训练
    • 编辑 - 阅读第二名解决方案后,这可能是因为我没有正确编码输入。可能是我理解不足。我以为用普通全连接层编码连续变量就足够了。显然分类损失确实有效。
  3. 通过类别权重和过采样减少天顶角预测的偏差

TL;Dr - 最终解决方案摘要

脉冲作为标记,序列长度最大256,优先采样"真实"脉冲。

  • (自注意力(512嵌入,18层,平均池化,2048前馈)+ 2角度分类头)-> 约0.982
  • (为最大3072序列长度微调的注意力模型)-> 0.980
  • (在注意力模型嵌入上使用vMF损失的MLP)-> 约0.976
  • (使用提升分类器组合分类器和vMF角度)-> 约0.965 🔥
  • (15层 + 18层 + 同一18层运行的检查点 - 用另一个提升分类器组合)-> 约0.962

P.S - 无论您的排名如何,请分享您的经验教训和失败想法。这有助于您整理所学并获得更好见解,我一定会阅读所有分享。

同比赛其他方案