545. IceCube - Neutrinos in Deep Ice | icecube-neutrinos-in-deep-ice
这是我三年来首次参加Kaggle比赛,也是我第一次获得金牌,度过了一段非常激动人心的时光。😃
查看文末的 TL;Dr 获取简短总结 - 本文包含模型信息和个人经验教训。
这个数据集并非自然图结构,我对所有基于图模型的方法感到困惑。图中的边是根据时间或距离等标准人为选择的,我认为这不是正确的方法。因此我专注于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 的家庭电脑。自然地,我花了大量时间手工设计特征、优化代码,尝试各种看似有帮助的想法。结果不得不放弃大多数实验,这些方法在小模型上有效,但在使用更多数据训练的大模型上效果被淹没。
曾经有效但后来被放弃的显著想法:
以上所有方法在训练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层训练了相同模型,但由于混合精度不稳定导致性能更差。
工程技巧
扩展效果惊人:
此外,我针对最大3072的序列长度微调了模型,这带来了0.002的微小改进。
Von Mises-Fisher 损失与注意力模型结合时不稳定,端到端训练从未超过1.04。
对于分类器,我将角度数字化为128个区间。交叉熵损失效果良好,但我想引入归纳偏置,让模型知道类别是有序的而非独立。为此,我对目标独热向量应用带高斯核的1D卷积。对于方位角,这种卷积有环绕;对于天顶角,我根据核长度扩展区间。(稍后发布解释损失的笔记本)这带来了略微更稳定的训练,但通过良好的超参数调整,交叉熵也可能同样有效。
显然注意力不足以获胜,0.982甚至不在金牌区(在角落哭泣)。然而,基础模型对后续想法的成功至关重要。
在编码器嵌入上训练带vMF损失的MLP是有效的。我转储100批平均池化的编码器嵌入并用其训练堆叠模型,达到了约0.978的分数。vMF模型与基础模型的简单平均集成得到了约0.976的分数。
我知道标准的集成技巧,但这是我第一次使用另一个模型进行集成。如果您知道这在实践中被使用,请告诉我是否有相关文献。我在比赛结束前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个模型。
以下是最终提交和单注意力模型的推理笔记本:
事后看来,我应该尝试一些图模型,因为它们可能与分类器有分歧,集成它们可能带来良好改进。
没想到这些会失败,也没有过早放弃,但无法让它们生效:
脉冲作为标记,序列长度最大256,优先采样"真实"脉冲。
P.S - 无论您的排名如何,请分享您的经验教训和失败想法。这有助于您整理所学并获得更好见解,我一定会阅读所有分享。