144

ProtT5 论文精读:蛋白嵌入是特征工程,不是万能功能预测器

ProtT5 提供了另一条蛋白嵌入路线。这篇讲清编码器-解码器架构的差异、嵌入的正确用法与常见误区。

ProtT5(Elnaggar 等,TPAMI 2021,ProtTrans 系列)用 T5 编码器-解码器架构训练蛋白语言模型。它与 ESM 的 BERT 式路线并行发展,在多个下游任务上表现相当——理解两者的差异有助于选型。

ProtTrans 系列的模型

模型 架构 特点
ProtBERT BERT 掩码语言建模
ProtAlbert ALBERT 参数共享,更省内存
ProtXLNet XLNet 排列语言建模
ProtT5 T5(编码器-解码器) 系列中表现最好
ProtElectra ELECTRA 判别式预训练

论文系统比较了这些架构,结论是 ProtT5 在下游任务上综合最好——这个横向比较本身是论文的重要贡献。

T5 的预训练任务差异

# BERT 式(ESM-2):
#   随机遮住单个 token,预测它
#   MKTA[MASK]IAKQ → Y
#
# T5 式(span corruption):
#   遮住【连续的一段】,用一个哨兵 token 替代,
#   让解码器生成被遮住的整段
#   MKTA[X]KQ → [X] YIA
#
# 差异的影响:
#   T5 需要建模「一段序列」的连贯性
#   → 理论上更适合捕捉局部基序(motif)
#
# 实践中:
#   两者的下游表现差异不大
#   → 【架构选择不是决定性因素】
#   → 数据规模与质量的影响更大
#
# 【使用时的关键点】:
#   ProtT5 是编码器-解码器,
#   但做特征提取时【只用编码器】
#   → 用 T5EncoderModel 而非 T5Model
#   → 省一半参数和显存

使用

pip install transformers torch sentencepiece

import torch
import re
from transformers import T5Tokenizer, T5EncoderModel

tokenizer = T5Tokenizer.from_pretrained(
    "Rostlab/prot_t5_xl_half_uniref50-enc", do_lower_case=False)
model = T5EncoderModel.from_pretrained(
    "Rostlab/prot_t5_xl_half_uniref50-enc")
model = model.eval()
if torch.cuda.is_available():
    model = model.cuda().half()      # 半精度省显存

def embed(sequences, max_len=1024):
    # 【重要】:ProtT5 要求氨基酸之间用空格分开
    #          且非标准氨基酸要替换成 X
    seqs = [" ".join(re.sub(r"[UZOB]", "X", s)) for s in sequences]

    ids = tokenizer.batch_encode_plus(
        seqs, add_special_tokens=True, padding="longest",
        truncation=True, max_length=max_len)
    input_ids = torch.tensor(ids["input_ids"])
    attention_mask = torch.tensor(ids["attention_mask"])
    if torch.cuda.is_available():
        input_ids, attention_mask = input_ids.cuda(), attention_mask.cuda()

    with torch.no_grad():
        emb = model(input_ids=input_ids,
                    attention_mask=attention_mask).last_hidden_state

    results = []
    for i, s in enumerate(sequences):
        L = min(len(s), max_len - 1)
        results.append(emb[i, :L].mean(dim=0).cpu().float().numpy())
    return results

# 【常见错误】:
#   1) 忘记在氨基酸间加空格 → tokenizer 会按 BPE 切分,结果完全错
#   2) 忘记替换 U/Z/O/B → 词表外字符
#   3) 用 T5Model 而非 T5EncoderModel → 浪费一半显存
#   4) 平均池化时把 padding 位置也算进去 → 表示被稀释

「嵌入是特征工程,不是万能功能预测器」

# 常见的误解:
#   「用了蛋白语言模型嵌入,就能预测蛋白功能」
#
# 实际情况:
#
# 1) 嵌入编码的是【序列的统计规律】
#    包括:氨基酸组成、局部基序、进化保守性、
#          隐含的结构倾向
#    不包括:
#      - 翻译后修饰的实际状态
#      - 细胞环境与相互作用伙伴
#      - 表达水平与定位(除非序列中有信号)
#      - 具体的酶动力学参数
#
# 2) 【下游任务的数据量仍是瓶颈】
#    嵌入好不等于任务能做
#    如果只有 50 个标注样本,
#    再好的嵌入也训不出可靠模型
#
# 3) 【嵌入的适用域】
#    对与预训练数据分布差异大的序列
#    (人工设计蛋白、极端环境微生物、
#      非常短的肽段),表示质量下降
#
# 4) 【必须与简单基线比较】
#    对照基线:
#      - 氨基酸组成(20 维)
#      - 二肽组成(400 维)
#      - 理化性质(疏水性、电荷、分子量等)
#      - BLAST 最近邻的标签
#
#    【很多任务上,这些简单特征已经接近语言模型嵌入】
#    → 尤其当任务主要由整体组成决定时
#
# 结论:
#   把嵌入当作【一种更好的特征】,
#   而非「模型已经理解了蛋白」

ProtT5 与 ESM-2 的选择

考量 ProtT5 ESM-2
下游任务表现 大体相当,任务相关
生态与工具 HuggingFace 集成好 官方库功能更多(含结构预测)
模型规模选择 较少 从 8M 到 15B 全覆盖
结构预测 有 ESMFold
零样本变异效应 可以 有成熟方案
显存需求 较大(3B) 可选小模型

实践建议:两个都试,在自己的验证集上比较。它们的表示有互补性——拼接两者的嵌入有时比单独用任一个更好,这是低成本的提升手段。

提升嵌入使用效果的技巧

  • 比较不同层:不同层编码不同层次的信息,应在验证集上选;
  • 池化方式:平均池化最稳健;但对某些任务,只对功能域区域池化比全序列平均更好;
  • 残基级 vs 序列级:预测残基级性质(如二级结构、结合位点)应用残基级表示,不要池化;
  • 降维:小数据时用 PCA 把 1024 维降到 50~100 维,能显著减少过拟合;
  • 与手工特征拼接语言模型嵌入 + 氨基酸组成 + 理化性质,交给梯度提升模型——实践中很稳健;
  • 微调 vs 冻结:数据量 < 数千时,冻结表示通常比微调更好(微调容易过拟合)。

关键要点

  • 做特征提取时只用 T5EncoderModel,且氨基酸之间必须加空格——忘了结果完全错;
  • 架构差异(BERT vs T5)不是决定性因素,数据规模与质量影响更大
  • 嵌入是一种更好的特征,不是「模型已理解蛋白」——下游数据量仍是瓶颈;
  • 必须与氨基酸组成、理化性质等简单基线比较——很多任务上差距不大。

延伸资源