蛋白质语言模型入门:用 Python 提取 ESM-2 向量
蛋白质序列是一串氨基酸字母,而多数机器学习工具需要数值输入。蛋白质语言模型可以把序列转成向量,用于检索、聚类,或作为监督学习任务的输入特征。
本文用小型 ESM-2 模型在 CPU 上为每条序列提取一个 320 维向量,保存为 JSON,并检查输出形状。你也会看到,为什么向量相似度不能直接证明蛋白质功能。掌握基础 Python 即可,不需要训练模型或开展实验。
1. 蛋白质语言模型学到了什么?
掩码蛋白质语言模型在训练时隐藏部分氨基酸 token,再根据周围序列预测它们。模型的隐藏状态由此表示每个残基及其序列上下文。ESM 使用这类掩码语言建模方法,详见 Transformers 官方 ESM 文档。
Embedding(嵌入向量)是一组学习得到的数值特征。某个维度通常不能直接解释成温度、电荷或酶活性。后续任务可以利用这些特征,但向量本身不是测量结果。
先区分三种输出:
| 任务 | 输出 | 还需要检查什么 |
|---|---|---|
| 序列表征 | 每个残基或每条蛋白质的向量 | 这些特征是否有助于具体任务 |
| 结构预测 | 预测的三维坐标 | 置信度及其与独立证据的一致性 |
| 蛋白质生成 | 候选序列,也可能包含其他属性 | 候选蛋白质是否具有预期行为 |
本文使用 ESM-2 提取表征。ESMFold 在表征基础上增加结构预测架构;ESM3 是另一个涉及序列、结构和功能的多模态模型。ESM 项目仓库 和 ESM3 论文 分别介绍了这些模型。运行下面的 ESM-2 示例,不会同时运行 ESMFold 或 ESM3。
2. 安装小型 ESM-2 示例
使用 PyTorch 支持的较新 Python 版本,先创建独立环境:
mkdir protein-lm-demo
cd protein-lm-demo
python3 -m venv .venv
. .venv/bin/activate
python -m pip install torch 'transformers==4.57.6'
这里选择 facebook/esm2_t6_8M_UR50D:6 层、约 800 万参数、320 维隐藏状态。模型配置文件 也记录了这些维度。它适合用短序列跑通流程,但本文不把它当作所有任务的最佳选择。
首次运行需要联网下载公开模型文件,之后可复用 Hugging Face 缓存。代码固定了模型版本,默认使用 CPU,这个示例不需要 GPU 或 API key。依赖包和模型文件仍会占用磁盘空间。
3. 把序列转成向量
保存为 protein_embeddings.py:
import argparse
import json
from pathlib import Path
import torch
import torch.nn.functional as F
from transformers import AutoTokenizer, EsmModel
MODEL_ID = "facebook/esm2_t6_8M_UR50D"
REVISION = "c731040fcd8d73dceaa04b0a8e6329b345b0f5df"
AMINO_ACIDS = set("ACDEFGHIKLMNPQRSTVWY")
# Artificial strings for a software exercise; no biological function is claimed.
SEQUENCES = ["ACDEFGHIKLMNPQRSTVWY", "MKTAYIAKQRQISFVKSHFSRQ"]
def embed(sequences, tokenizer, model):
for sequence in sequences:
if not 1 <= len(sequence) <= 1022:
raise ValueError("Use sequences of 1-1022 residues; no silent truncation")
if set(sequence) - AMINO_ACIDS:
raise ValueError("This demo accepts only the 20 standard uppercase amino acids")
if not sequences:
raise ValueError("Provide at least one sequence")
batch = tokenizer(
sequences, padding=True, truncation=False,
return_special_tokens_mask=True, return_tensors="pt",
)
special = batch.pop("special_tokens_mask").bool()
residues = batch["attention_mask"].bool() & ~special
lengths = residues.sum(dim=1)
expected = torch.tensor([len(s) for s in sequences])
if not torch.equal(lengths, expected):
raise ValueError("Token counts do not match residue counts")
with torch.inference_mode():
hidden = model(**batch).last_hidden_state
weights = residues.unsqueeze(-1).to(hidden.dtype)
pooled = (hidden * weights).sum(dim=1) / lengths.unsqueeze(-1)
if not torch.isfinite(pooled).all():
raise ValueError("Non-finite embedding values")
return pooled, hidden.shape, lengths.tolist()
def main():
parser = argparse.ArgumentParser(description="Extract ESM-2 protein embeddings on CPU")
parser.add_argument("--output", type=Path, default=Path("embeddings.json"))
args = parser.parse_args()
if args.output.exists():
parser.error("Output exists; choose another --output path")
tokenizer = AutoTokenizer.from_pretrained(MODEL_ID, revision=REVISION)
model = EsmModel.from_pretrained(
MODEL_ID, revision=REVISION, add_pooling_layer=False,
)
model.eval()
vectors, token_shape, lengths = embed(SEQUENCES, tokenizer, model)
similarity = F.cosine_similarity(vectors[0:1], vectors[1:2]).item()
result = {
"model": MODEL_ID,
"revision": REVISION,
"pooling": "last-layer mean over residue tokens only",
"sequences": SEQUENCES,
"embeddings": vectors.tolist(),
}
with args.output.open("x", encoding="utf-8") as output:
json.dump(result, output, indent=2, allow_nan=False)
output.write("\n")
print("Residue counts:", lengths)
print("Token tensor:", tuple(token_shape))
print("Protein tensor:", tuple(vectors.shape))
print(f"Cosine similarity (not a probability): {similarity:.6f}")
print(f"Saved {args.output}")
if __name__ == "__main__":
main()
输入中的两条字符串仅用于软件演示,不是评测数据,也不表示它们具有真实的蛋白质活性。确认示例可运行后,可把 SEQUENCES 换成自己的氨基酸序列。
脚本只接受 20 种标准氨基酸的大写字母。真实数据可能包含不确定或非标准残基,扩展输入规则之前,应先确定处理方式。不要把 FASTA 标题行、核酸序列、比对缺口或终止符直接放入列表。只含 A、C、G、T 的 DNA 字符串也会通过字符检查,因此还必须核实原始数据类型。
1,022 个残基是本示例采用的保守上限,不是所有蛋白质模型的通用限制。超长输入会被拒绝,避免静默截断。处理大数据集时,把长度接近的序列分成小批次;每批最长的序列会决定需要填充多少位置。
为什么平均之前要先过滤 token?
分词器会添加特殊 token,并给较短序列补齐长度。attention_mask 标记有效输入位置,但其中仍包含特殊 token。代码再结合 special_tokens_mask,让均值只覆盖氨基酸位置。
最后一层隐藏状态的形状是 (批次, token 位置, 隐藏维度)。对残基位置取均值后,输出变成 (批次, 隐藏维度)。add_pooling_layer=False 关闭未使用的 pooler;脚本自行完成明确的均值池化。
均值池化适合作为基线,但会把整条序列压缩成一个向量,可能掩盖局部差异。如果任务要求逐位置预测,应保留残基级表征。这里使用的隐藏状态输出见 ESM API 文档。
4. 运行并检查结果
python protein_embeddings.py --help
python protein_embeddings.py --output embeddings.json
python -m json.tool embeddings.json > /dev/null
python -m pip freeze > requirements-tested.txt
与形状相关的输出应为:
Residue counts: [20, 22]
Token tensor: (2, 24, 320)
Protein tensor: (2, 320)
较长字符串有 22 个残基,加上两个特殊 token,共 24 个位置。较短字符串会被填充到相同长度。脚本还会打印余弦相似度,并保存向量、输入序列、模型版本和池化方式。
独立检查保存的文件:
python - <<'PYTHON'
import json
import math
with open("embeddings.json", encoding="utf-8") as source:
result = json.load(source)
vectors = result["embeddings"]
assert len(vectors) == len(result["sequences"]) == 2
assert all(len(vector) == 320 for vector in vectors)
assert all(math.isfinite(x) for vector in vectors for x in vector)
print("Validated: 2 protein embeddings, 320 finite values each")
PYTHON
本文在 CPU 上使用 Python 3.14.7、PyTorch 2.14.0、Transformers 4.57.6 和上述固定模型版本运行了示例。输出形状与上面一致,两条人工输入的余弦相似度为 0.937591。这个较高的数值恰好提醒我们:输入没有功能标签,不能凭这个分数推断功能相同。不同环境可能出现小幅数值差异。
脚本不会覆盖已有输出文件。再次运行时,用 --output 指定新路径。如果需要记录依赖版本,把 requirements-tested.txt 和 JSON 一起保存。
5. 如何使用向量,又不误读结果?
余弦相似度比较的是向量方向。它不是概率,不是序列比对的一致性百分比,也不能证明两个蛋白质功能相同。对于本示例的均值向量,没有一个通用阈值可以直接确认生物学关系。
2026 年的一项 Nature Methods 蛋白质表征不确定性研究 考察了不同模型和序列类型的嵌入表现。对本教程而言,实际启示是:应在你真正打算使用的数据上评价表征。
开始做分类实验时,我会先冻结 ESM-2,为带标签的数据集提取向量,再训练一个简单分类器。先与氨基酸组成等基础特征比较,再决定是否值得微调模型。
如果目标是预测不熟悉的蛋白质家族,随机划分训练集和测试集可能产生误导:两边可能存在近缘序列。应按预期用途采用考虑序列相似性的划分,并且只在训练部分拟合标准化或特征选择步骤。DataSAIL 论文 解释了跨数据划分的相似性为何可能抬高评测成绩。本文的两条字符串只能检查程序流程,不能评价模型质量。
常见问题
| 现象 | 处理方式 |
|---|---|
| 模型下载失败 | 检查网络或代理能否访问 Hugging Face,恢复连接后重试。 |
| 找不到匹配的 PyTorch 安装包 | 根据 PyTorch 官方安装页 选择支持的 Python 和平台组合,再重建环境。 |
| 输入校验失败 | 去掉标题行和空白,检查氨基酸记法及长度;不要静默删除有生物学意义的符号。 |
| 内存占用过高 | 减少每批序列数和序列长度,单独处理长序列。 |
| 每条蛋白质不是 320 个数值 | 检查模型版本、池化维度,以及是否误保存了残基向量。 |
| 输出文件已存在 | 使用 --output embeddings-second-run.json。 |
下一步
现在你已有一条从蛋白质序列到向量文件的可重复流程,并明确处理了特殊 token、填充和输出检查。下一步可以选择一个小型带标签数据集,在符合预期用途的数据划分上测试冻结表征的基线。成功提取向量与得到有用的生物学预测,需要分别验证。
- 原文作者:春江暮客
- 原文链接:https://www.bobobk.com/protein-language-models-esm2-embeddings.html
- 版权声明:本作品采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。