2. 输入特征:MSA与Pair特征的生成原理
坦白讲,EvoFormer 最让我着迷的地方,就是它怎么处理输入数据。你不把原始序列喂进去就完事了——它需要两种非常特殊的特征:MSA 特征和Pair 特征。你可以把它们想象成蛋白质的“社交关系”和“空间关系”。
我第一次调 AlphaFold2 的时候,总觉得 MSA 特征就是个序列对齐嘛,能有多大差别?直到有一次跑一个孤儿蛋白,MSA 深度不够,预测结果直接崩了。嗯,从那以后我才真正重视起这个输入环节。
2.1 MSA 特征:从序列到进化协方差
为什么需要 MSA?说白了,单个序列的信息太少了。你拿一条氨基酸序列,根本看不出哪些位置在空间上靠近。但你找来几千条同源序列一比对——那些共变的位置,极有可能在三维结构里是挨着的。
| 特征维度 | 含义 | 形状示例 |
|---|---|---|
| 目标序列(query) | 你要预测的那条蛋白序列 | L × 1 |
| 同源序列(hit) | 数据库中搜索到的相似序列 | N × L |
| 序列权重(weight) | 每条序列的可靠程度 | N × 1 |
| 删除率(deletion) | 该位置在序列比对中缺失的概率 | N × L |
MSA 的构建流程,我一般走这几步:
- 搜索同源序列——用 HHblits、jackhmmer 这类工具,从 UniRef 或 BFD 数据库里捞。
- 多轮迭代对齐——每次迭代都会扩展序列库,我习惯跑 3 轮,再深就过拟合了。
- 去冗余 & 重加权——太相似的序列会拉低有效深度,我一般设 80% 的序列相似度阈值。
- 计算序列权重——每条序列的重要性不一样,越独特的序列权重越大。
原始特征怎么编码?我直接贴一段我在项目中用过的伪代码逻辑:
# MSA 原始特征编码示意
def encode_msa(sequences):
# sequences: list of strings, length = N
# 1. 独热编码(one-hot)
residues = "ARNDCQEGHILKMFPSTWYV-X"
one_hot = np.zeros((N, L, len(residues)))
for i, seq in enumerate(sequences):
for j, aa in enumerate(seq):
one_hot[i, j, residues.index(aa)] = 1.0
# 2. 有无缺失标记(has_deletion / deletion_value)
has_del = (sequences == '-').astype(float)
del_val = ... # 从对齐质量中提取
# 3. 拼接后投影到 c_m 维(通常 256)
features = np.concatenate([one_hot, has_del, del_val], axis=-1)
return features # shape: (N, L, 22+2) -> 投影后 (N, L, c_m)
你想想看,这个 c_m 维度是个关键参数。我踩过坑——一开始设太小,信息丢失;设太大,又浪费显存。实践下来 256 是个好选择。
2.2 Pair 特征:空间关系的起点
Pair 特征解决的,是“残基 i 和残基 j 之间是什么关系”这个问题。乍一看你可能觉得 MSA 已经包含共变信息了,但 MSA 是二维的(序列 × 位置),还需要一个显式的“残基对”表示。
Pair 特征的来源有两个:
- 相对位置编码——残基 i 和 j 在序列上的距离差,我用正弦余弦函数嵌入。
- 外积均值(Outer Product Mean, OPM)——从 MSA 的行维度上提取协方差。
这里有个细节:OPM 的输入不是原始 MSA,而是经过单层 MLP 投影后的 MSA 表示。我当初写这个模块的时候,手滑把投影层忘了,导致梯度一直不收敛……后来追了三天 bug 才发现。
Pair 特征的最终形状:L × L × c_z,其中 c_z 通常设为 128。你看,它是对称的——一条对角线天然就是自己和自己配对。
2.3 特征生成的完整流水线
我把整个流程画了张图,方便你对照着理解。你注意到没——MSA 和 Pair 在初期就产生了交互,这也是 EvoFormer 比传统方法聪明的地方。
2.4 避坑指南:我踩过的三个坑
在特征生成这块,我总结过几个实战中容易出问题的点:
我曾经接过一个任务,目标蛋白只有 30 条同源序列。按默认配置跑,Pair 特征里的 OPM 项几乎全是噪声。后来我加了模板特征做补充,效果才起来。
我一般会在搜索阶段放宽 E-value 阈值,或者把迭代轮次从 3 增加到 5。如果还不行,就别依赖 OPM 了,Pair 特征里多靠相对位置编码撑着。
最初我懒得算权重,把所有序列一视同仁。结果 MSA 特征里重复的保守区域权重过大,共变信息被淹没了。嗯,后来老老实实加了重加权,收敛速度直接翻倍。
我建议你每次训练时,对 MSA 做随机裁剪——只采样 512 条序列,而不是全量。这样既能降低显存消耗,又能防止模型过拟合某些冗余序列。
2.5 小结:这两组特征有多重要?
说白了,如果你把 EvoFormer 看作一个推理引擎,那 MSA 和 Pair 就是它的燃料。燃料质量不好,引擎再强也跑不起来。我见过太多人把注意力都放在 Transformer 模块上,却忽略了输入端的处理——结果练出来的模型,泛化能力一塌糊涂。
你回头看看这幅流程图,重点抓住两件事:
- MSA 特征是
(N, L, c_m),承载进化共变信息。 - Pair 特征是
(L, L, c_z),表达残基对空间关系。
这两者缺一不可,也互相校准。接下来的章节,我会详细拆解它们如何通过“成对注意力”和“三角乘法”进行交互。——不过那是后话了,先把今天的特征生成原理吃透再说。