EvoFormer 架构总览:编码器-解码器结构

好,咱们进入到核心了。EvoFormer 是整个 AlphaFold 的大脑,而它的骨架,说白了就是一套编码器-解码器结构。你可能会问,为什么不是直接拿个 Transformer 往上怼?嗯,蛋白质折叠这个事儿,信息维度太杂了——有序列信息、有进化信息、有结构约束信息。你需要一种方式,让这些信息在模型中高效地流动和交互。这就是 EvoFormer 设计的意义所在。

为什么是编码器-解码器?

我自己习惯将 EvoFormer 看作一个“三阶段生产流水线”。

  • 编码器阶段:负责把输入的原始特征(MSA、Pair)进行深度的信息抽取和融合。
  • 信息瓶颈/循环阶段:这是最特别的部分,一个48层的循环模块,是整个流水线的核心加工区。
  • 解码器阶段:把处理好的特征,最终解码成实实在在的3D结构坐标。

但要注意,这里的编码器和解码器并不像传统的 NLP 模型那样完全分离。EvoFormer 的编码器和解码器,共享了大部分的计算逻辑。我曾经在复现这个结构的时候,花了很长时间才适应这种“你中有我,我中有你”的设计。

一个小提示: 如果你去读论文,会发现原始论文里并没有严格区分“编码器”和“解码器”这两个词。这是我为了方便理解做的一个简化映射。论文中更强调“Pair 表示”和“MSA 表示”的相互更新。

编码器干了什么?

编码器的输入有两条线:MSA 表示和 Pair 表示。它不直接输出结构,而是输出一个高度抽象的、融合了蛋白质家族进化信息的“隐空间表示”。

这里的关键操作叫 EvoFormer Block。每一层 Block 都包含两个子模块:

  • MSA 堆栈 (MSA Stack):对 MSA 内的序列进行行和列的自注意力与交叉注意力。说白了,就是让每一行(某条同源序列)知道自己和其他行(其他同源序列)的关系,同时也让每个位置(列)知道自己和其他位置的关系。
  • Pair 堆栈 (Pair Stack):处理残基对之间的距离和方向信息。它在更新 Pair 表示时,会引入 MSA 堆栈的输出结果。我刚开始看这里时,觉得这就像是在做“结构化沟通”——MSA 负责全局对话,Pair 负责局部细节,两者互通有无。

编码器并不深。原始的 EvoFormer 在编码器阶段只堆叠了 2~4 个 Block。真正的深度在后面。

解码器在干什么?

解码器这部分,我认为是 EvoFormer 最精髓的地方。它不再是简单的堆叠,而是引入了一个 循环结构。

具体来说,编码器的输出(MSA 表示和 Pair 表示)会被喂入一个 48 层 的循环块。每一层的计算,都会“回收”并使用上一轮循环产生的 结构模块的预测结果。这就形成了一个闭环:

  1. 当前层的 MSA 和 Pair 表示,再加上上一轮预测的结构信息(主链扭转角、残基坐标等)作为输入。
  2. 计算更新后的 MSA 和 Pair 表示。
  3. 将新的表示送入结构模块,预测当前轮的结构。
  4. 当前轮的结构信息再送回给下一层循环。

坦白说,我第一次看到这个操作时,有点懵。为什么要把预测结果再塞回去?后来我自己跑实验时才明白:这是一种典型的 自洽性迭代 (Recursive Refinement)。每一次循环,模型都在“看到”自己预测的结构是什么样的,然后根据这个来修正自己内部的表示,让预测越来越准。

循环结构的伪代码

我这里写一个简化的伪代码,帮你理解这个循环过程,实际代码会复杂很多,但核心逻辑是这样的:

# 初始化
msa_repr = encode_msa(input_msa)
pair_repr = encode_pair(input_pair)
predicted_xyz = None

# 循环 48 次
for i in range(48):
    # 1. 回收上一轮的结构信息
    if i > 0 & predicted_xyz is not None:
        msa_repr = update_msa_with_xyz(msa_repr, predicted_xyz)
        pair_repr = update_pair_with_xyz(pair_repr, predicted_xyz)
    
    # 2. 执行 EvoFormer Block
    msa_repr = msa_stack(msa_repr, pair_repr)
    pair_repr = pair_stack(pair_repr, msa_repr)
    
    # 3. 结构模块解码
    predicted_xyz = structure_module(pair_repr, msa_repr)

# 最终输出
final_structure = predicted_xyz
避坑指南: 我曾经在实现时,跳过 Pair 堆栈的更新,直接让 MSA 输出到结构模块。结果发现训练 loss 不下去。调试了整整一周才意识到,Pair 表示才是结构预测的“骨架”,MSA 是“肌肉”,两者缺一不可。不要试图简化这个流程。

EvoFormer 整体架构图

为了让上面这些文字更直观,我画了一张图。你把它当作一个全局地图,然后结合前面的文字,一步步走一遍,会清晰很多。

EvoFormer 编码器-解码器架构总览 MSA 输入 Pair 输入 编码器 (2-4 层 Block) MSA ↔ Pair 交互 循环模块 (48 层) MSA 堆栈 Pair 堆栈 信息交互 结构模块 回收上一轮预测的结构 解码器 (结构模块) 预测主链扭转角 & 残基坐标 3D 结构坐标 .pdb / .cif 图例 输入 / 输出 编码器 循环模块

总结一览表

组件 核心角色 我遇到过的一个坑
编码器 初步融合 MSA 与 Pair 特征,建立初始表示 层数太深时容易过拟合训练集
循环模块 (48层) 通过回收结构信息进行迭代精炼 如果回收步幅设置不当,会导致梯度爆炸
解码器 将精炼后的表示转化为具体原子坐标 直接输出坐标不如先输出扭转角稳定

嗯,这就是 EvoFormer 的宏观骨架。我个人觉得,理解这个编码器-循环-解码器的“闭环”设计理念,比你死记硬背多少层要有意义得多。后面我们会深入到每一层的具体计算细节,到时候你就知道这个“闭环”为什么能 work 了。