抗体的亲和力成熟——通过引入突变提高与抗原的结合强度——传统上靠大规模文库筛选。AI 的价值在于把「盲目筛选上百万个变体」变成「精准测试几百个候选」,大幅降低实验成本。
方法一:蛋白语言模型的零样本打分
不需要任何该抗体的实验数据,直接用预训练模型给突变打分。这是最容易起步的方法。
import torch, esm
model, alphabet = esm.pretrained.esm2_t33_650M_UR50D()
batch_converter = alphabet.get_batch_converter()
model.eval()
def zero_shot_mutation_scores(sequence, positions=None):
"""对每个位点的所有可能突变打分(掩码语言模型 log-ratio)"""
_, _, tokens = batch_converter([("ab", sequence)])
results = []
positions = positions or range(len(sequence))
for pos in positions:
masked = tokens.clone()
masked[0, pos + 1] = alphabet.mask_idx # +1 因为有起始 token
with torch.no_grad():
logits = model(masked)["logits"]
log_probs = torch.log_softmax(logits[0, pos + 1], dim=-1)
wt = sequence[pos]
wt_lp = log_probs[alphabet.get_idx(wt)].item()
for aa in "ACDEFGHIKLMNPQRSTVWY":
if aa == wt:
continue
score = log_probs[alphabet.get_idx(aa)].item() - wt_lp
results.append({"position": pos + 1, "wt": wt, "mut": aa,
"score": score})
return sorted(results, key=lambda x: -x["score"])
# 用法:只扫 CDR 区域(结合位点)
cdr_positions = list(range(cdr_h3_start, cdr_h3_end))
scores = zero_shot_mutation_scores(heavy_seq, cdr_positions)
print(scores[:20])
重要理解:ESM 的零样本打分衡量的是「这个突变在天然蛋白中常不常见」,即「是否破坏折叠/稳定性」,而不是「是否提高对该抗原的亲和力」。它的正确用法是先滤掉可能破坏抗体结构的突变,再在剩余的突变中寻找亲和力提升。
方法二:抗体专用语言模型
# 在抗体序列上训练的模型,更懂抗体的序列规律
# AntiBERTy、AbLang、IgLM 等
pip install antiberty
from antiberty import AntiBERTyRunner
antiberty = AntiBERTyRunner()
sequences = [heavy_seq, light_seq]
# 获取嵌入表示
embeddings = antiberty.embed(sequences)
# 序列填充(infilling):预测被掩码位置的氨基酸分布
# 可用于评估突变的「抗体样性」
# 与 ESM 的区别:
# ESM 学的是所有蛋白的规律
# AntiBERTy 学的是抗体特有的规律(如 CDR 的高变性是正常的)
# → 对抗体来说,专用模型的打分通常更合理
方法三:用实验数据训练(最有效)
如果做过深度突变扫描(DMS)或有一批变体的实测数据,监督学习的效果远好于零样本:
import numpy as np
from sklearn.model_selection import GroupKFold
import lightgbm as lgb
# 特征化:单点突变的表示
def mutation_features(wt_seq, mutations, embeddings):
"""mutations: [(position, wt_aa, mut_aa), ...]"""
feats = []
for pos, wt, mut in mutations:
# 1) 位点的语言模型嵌入
pos_emb = embeddings[pos]
# 2) 氨基酸性质变化
aa_props = amino_acid_properties(mut) - amino_acid_properties(wt)
# 3) 位点的结构特征(若有结构)
# 溶剂可及性、是否在界面、二级结构
feats.append(np.concatenate([pos_emb, aa_props]))
return np.array(feats)
# 关键:划分必须按位点分组,避免同一位点的不同突变跨划分
gkf = GroupKFold(n_splits=5)
groups = [m[0] for m in mutations] # 按位点分组
scores = []
for tr, te in gkf.split(X, y, groups=groups):
model = lgb.LGBMRegressor(n_estimators=500, verbose=-1)
model.fit(X[tr], y[tr])
pred = model.predict(X[te])
scores.append(np.corrcoef(pred, y[te])[0, 1])
print(f"Spearman: {np.mean(scores):.3f} ± {np.std(scores):.3f}")
按位点分组划分是必须的:如果同一位点的 A→V 在训练集、A→I 在测试集,模型只要记住「这个位点很重要」就能拿高分,不反映真实的泛化能力。
方法四:结构辅助的界面分析
# 若有抗体-抗原复合物结构(实验或预测,见 371)
# 1) 识别界面残基
import MDAnalysis as mda
from MDAnalysis.analysis import distances
u = mda.Universe("complex.pdb")
ab = u.select_atoms("segid H or segid L")
ag = u.select_atoms("segid A")
d = distances.distance_array(ab.positions, ag.positions)
contact_atoms = ab[d.min(axis=1) < 5.0]
interface_residues = set(contact_atoms.resids)
print(f"界面残基: {sorted(interface_residues)}")
# 2) 优先在界面周边(而非界面核心)引入突变
# 界面核心残基通常已优化,改动风险高
# 界面周边(rim)有更多改善空间
# 3) 用 FoldX 或 Rosetta 计算突变的 ΔΔG
# 注意:预测精度有限(RMSE 常 > 1 kcal/mol),
# 只适合粗筛,不能定量
# 4) 寻找可优化的相互作用
# - 未被填满的口袋 → 加大取代基
# - 未配对的极性基团 → 引入互补残基
# - 静电排斥 → 电荷反转
组合策略与实验设计
# 分层筛选,逐级收窄
# 第 1 层:ESM/AntiBERTy 零样本,滤掉破坏结构的突变
# 几千个可能突变 → 几百个
# 第 2 层:结构分析,聚焦界面周边位点
# 几百个 → 100~200 个
# 第 3 层:ΔΔG 计算(FoldX/Rosetta)粗排
# → 优先级排序
# 第 4 层:实验测试
# 单点突变文库(几十到几百个)→ 测亲和力
# 第 5 层:组合有益突变
# 注意:单点有益的突变组合起来不一定叠加
# (上位效应 epistasis 很常见)
# 需要实测组合体
# 实验设计要点:
# - 包含野生型作为内标
# - 包含已知有害突变作为阴性对照
# - 覆盖不同位点而非集中在一处
# - 每个位点试多种氨基酸性质(大小、电荷、极性)
上位效应:组合突变的陷阱
单点突变各自提升亲和力,组合起来未必更好——这是抗体工程中普遍存在的现象:
- 两个突变可能改善同一个相互作用,组合起来只有一份收益;
- 两个突变可能产生空间冲突;
- 累积突变可能损害稳定性或引入免疫原性。
- 实践建议:组合突变必须实测,不能靠单点数据加和推断;同时要监测热稳定性与表达量,避免为亲和力牺牲可开发性。
关键要点
- ESM 零样本打分衡量的是「是否破坏结构」,不是「是否提高亲和力」——用它做过滤而非选优;
- 有实验数据时监督学习效果远好于零样本,但划分必须按位点分组;
- 优先在界面周边而非界面核心引入突变;
- 上位效应普遍存在,组合突变必须实测,不能靠单点加和推断。
延伸资源
- 蛋白语言模型:194《ESM》;结构预测:364《抗体结构预测》、365《CDR Loop 建模》;
- 可开发性:367《抗体可开发性 Developability》;免疫原性:368《免疫原性预测》。