587. Stanford Ribonanza RNA Folding | stanford-ribonanza-rna-folding
首先,我要衷心感谢 Kaggle 主办这场比赛,也感谢组织者在论坛中积极投入和及时回应。通过参与本次竞赛,我获得了宝贵的经验。特别感谢我的队友 Anton @ant0nch 和 María @manaves,尤其要感谢 María 作为一名生物技术专家所贡献的广泛领域知识。最后,我要感谢我的雇主 Freepik,在最后的几周里提供了额外的计算资源,这对我们至关重要。
我们的解决方案基于一个单一的 transformer 编码器模块。我们使用了标准的 PyTorch 实现,参数为 d_model: 256、dim_feedforward: 768、num_layers: 16、dropout: 0.1,激活函数为 GELU,并首先进行归一化。
模型在按聚类分组的 5 折分割数据上进行训练,聚类是通过对 A、C、G、U 映射为向量 1、2、3、4 后使用 KMeans 得到的。
我们使用 AdamW 优化器,权重衰减为 1e-2,并采用 OneCycleLR 学习率调度,其中 pct_start: 0.02 和 max_lr: 1e-3。
一个 252 维的向量,通过核苷酸嵌入的和来编码序列:
self.embs = nn.Embedding(5, d_model - extra_features)
我们还使用嵌入袋(embedding bag)对来自所提供的 47 种算法的二级结构进行编码,其中仅实际存在的数据计入平均值。结构的编码方式如下:. → 1,( → 2,) → 3,[ 或 < 或 { → 4,] 或 > 或 } → 5。
self.se_embs = nn.EmbeddingBag(6, d_model - extra_features, padding_idx=0)
将上述两个嵌入相加,然后在每个核苷酸(nt)上拼接来自 BPP 文件的以下特征:最大值、中位数、平均值和标准差。
此外,eternafold BPP 数据以及从二级结构计算出的邻接矩阵通过一个可学习的线性层作为注意力偏置使用。我们使用 48 个矩阵表示常规配对邻接,另外 48 个矩阵表示伪结邻接,再加上 BPP 矩阵(总计 = 48 * 2 + 1)。我们通过 mxfold2 生成额外的二级结构来增强给定数据。
self.algo_conv = nn.Conv2d(48 * 2 + 1, nhead, kernel_size=1, padding=0, bias=True)
位置编码:我们采用原始 transformer 论文中的标准正弦位置编码。为了实现更长的序列泛化,我们将序列中每个核苷酸的位置随机采样自 [0, 512) 区间内的均匀分布,并赋予随机的相对位置。这迫使网络学习核苷酸的相对顺序,而非绝对位置。
# 从 [0, max_seq_len) 中随机采样并排序生成位置列表
rnd_pos = torch.empty((n, s), dtype=torch.int64, device=pred.device)
for row in rnd_pos:
row[:] = torch.randperm(self.max_seq_len, device=pred.device)[:s].sort().values
pos = self.pos(rnd_pos) # N, S, E-4
我们使用批量大小为 32 训练了多个模型,共 100 个轮次,并采用了上述特征的不同消融、不同的 SNR 过滤策略(>1 和 >0.5)以及不同的 GroupKFold 随机种子。最终,我们对 7 个最佳模型的 5 折交叉验证结果进行了预测平均。