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)不是决定性因素,数据规模与质量影响更大;
- 嵌入是一种更好的特征,不是「模型已理解蛋白」——下游数据量仍是瓶颈;
- 必须与氨基酸组成、理化性质等简单基线比较——很多任务上差距不大。
延伸资源
- ESM-2:143《ESM-2 论文精读》;ESMFold:116《ESMFold 论文精读》;
- 抗体可开发性:367《抗体可开发性 Developability》;蛋白设计:150《ProteinMPNN 论文精读》;多模态:164《多模态模型》。