GeoStab:几何信息驱动蛋白稳定性突变预测,提升精度与泛化
蛋白质工程领域有个老生常谈的痛点:设计一个突变容易,预测它到底会让蛋白更稳还是更垮,难。我接触过的不少做酶改造和抗体亲和力成熟的同行,手里攒了一堆候选突变,最后还是要靠实验一个个试,周期长、成本高。GeoStab 这个项目瞄准的就是这个环节——用几何结构信息来提升突变后蛋白稳定性变化的预测精度。它解决的核心问题是:传统基于序列或单一结构的预测方法,在跨蛋白家族、跨突变类型时泛化能力不足,而 GeoStab 试图通过引入更丰富的几何表征来补上这块短板。这篇内容适合做计算蛋白设计、酶工程、抗体开发的研究人员和学生参考,也适合对深度学习在结构生物学落地感兴趣的朋友。下面我按自己的理解,把这个项目的设计思路、核心细节、实操流程和踩坑经验完整拆一遍。
1. 项目整体设计与思路拆解
1.1 为什么盯上"几何信息"这个切入点
蛋白稳定性变化预测,本质是给一个突变打分:ΔΔG 是正还是负,幅度多大。过去几年主流做法分两派,一派吃序列,用蛋白语言模型或者进化信息做输入;另一派吃结构,把突变位点周围的原子坐标喂进图神经网络。两派各有软肋——纯序列方法丢掉了空间邻近关系,一个在序列上隔了很远但在三维空间里紧挨着的残基相互作用,它捕捉不到;纯结构方法则常常把几何信息处理得比较粗糙,比如只用到原子间距离,忽略了角度、二面角、局部朝向这些更细的几何约束。
GeoStab 的思路很明确:既然突变对稳定性的影响,归根结底是局部 packing、氢键网络、疏水核心这些几何因素在起作用,那就把几何表征做深做透。它不是在序列特征上简单拼接几个距离特征,而是围绕突变位点构建了一套多层次的几何描述,让模型能"看见"突变前后局部环境的变化。这个选择背后的逻辑是:稳定性变化的物理本质是能量差,而能量差直接关联到原子间的几何排布,几何信息越完整,模型越有可能逼近真实的物理图像。
我个人的判断是,这个切入点选得务实。比起堆更大的语言模型,几何特征的精细化是一条性价比更高的路,尤其对数据量有限的稳定性数据集来说,参数量小、归纳偏置强的模型反而不容易过拟合。
1.2 整体架构的分层设计
GeoStab 的架构可以理解成三层。第一层是结构预处理层,负责把 PDB 结构转成模型能吃的几何图表示,包括节点特征和边特征的定义;第二层是几何编码层,用图神经网络或者等变网络把局部几何信息编码成残基级或原子级的嵌入;第三层是预测头,把突变位点及其邻域的嵌入聚合起来,回归出 ΔΔG 值。
这个分层设计的好处是模块解耦。预处理层可以独立替换,比如换一种邻域定义或者换一种边特征;编码层可以尝试不同的 backbone;预测头也可以从回归改成分类(预测稳定/不稳定)。我在复现类似项目时特别看重这种解耦,因为调参阶段你往往需要快速替换某一层做消融,耦合太紧的代码会让你改一处崩三处。
提示:如果你打算基于 GeoStab 做二次开发,建议先把预处理层和编码层之间的接口定义清楚,输入输出的张量形状、特征维度、邻域半径这些参数固定下来,后面换模块会省很多事。
1.3 与同类方案的取舍对比
拿它和几类常见方案比一下会更清楚定位。纯序列语言模型类方法,优势是能利用海量无标注序列预训练,冷启动能力强,但对局部几何不敏感;基于距离图的 GNN 方法,能捕捉空间邻近,但距离是标量,丢失了方向性;等变网络方法几何信息最完整,但计算开销大、对数据量要求高。GeoStab 走的是中间路线——用相对轻量的几何特征组合,在信息完整度和计算成本之间找平衡。
这个取舍对实际使用者意味着什么?意味着它更适合中等规模数据集上的快速迭代,而不是追求极致精度的大规模预训练场景。我在酶改造项目里用这类模型时,通常把它当作第一轮筛选工具,先把明显有害的突变滤掉,把实验资源集中在模型认为有希望的那部分候选上,这个用法下轻量模型的性价比最高。
2. 核心细节解析与实操要点
2.1 结构预处理:邻域怎么定义很关键
突变位点的邻域定义直接决定了模型能看到多少上下文。GeoStab 这类方法通常以突变残基为中心,取一个空间半径内的所有残基作为邻域。半径取多大有讲究:太小,比如 6 埃,只能看到紧邻的几个残基,氢键网络和二级结构上下文都不完整;太大,比如 15 埃,会把大量无关残基卷进来,噪声增加、计算量上升。实践中 8 到 10 埃是比较常见的起点,这个范围大致覆盖了两到三个残基壳层,能包含大部分直接相互作用。
除了半径,还要考虑是否按残基还是按原子建图。按残基建图计算量小,但会丢失侧链原子的精细位置;按原子建图信息完整,但图规模会膨胀好几倍。我的经验是,如果目标是预测突变对 packing 的影响,侧链原子信息不能丢,至少要把侧链重原子纳入;如果算力紧张,可以对邻域内的残基做采样或者只保留主链加关键侧链原子。
另一个容易忽略的点是缺失残基和缺失原子的处理。晶体结构里经常有柔性 loop 缺失或者侧链原子没解析出来,直接喂进模型会导致邻域特征不完整。稳妥的做法是标记缺失并用合理的占位特征,而不是简单丢弃,否则模型会学到"有缺失就预测某个方向"的虚假关联。
2.2 几何特征的构造清单
几何特征这块是 GeoStab 的核心竞争力所在,我按重要性列一下通常会包含的几类。第一类是距离特征,包括突变残基与邻域残基的 Cα-Cα 距离、侧链质心距离、最近重原子距离,这几类距离从不同粒度刻画邻近程度。第二类是角度特征,比如两个残基的 Cα 连线与各自侧链朝向的夹角,能反映相对取向。第三类是二面角特征,主链的 phi/psi/omega 和侧链的 chi 角,这些直接关联构象柔性。第四类是接触类型特征,比如是否形成氢键、是否属于疏水接触、是否参与盐桥,这类特征往往需要基于几何规则判定。
构造这些特征时有个实操细节:所有几何量都要做归一化或者分箱处理。距离用连续的实数喂进网络容易受尺度影响,常见做法是转成高斯径向基展开,把连续距离映射到一组固定中心的高维向量;角度和二面角则可以用 sin/cos 编码避免周期性断裂。这些处理看起来琐碎,但对模型收敛影响很大,我见过不少复现失败案例就栽在特征没归一化上。
| 特征类别 | 具体内容 | 处理方式 | 注意事项 |
|---|---|---|---|
| 距离 | Cα-Cα、侧链质心、最近重原子 | 高斯径向基展开 | 半径截断要统一 |
| 角度 | 残基间相对取向 | sin/cos 编码 | 注意坐标系一致性 |
| 二面角 | 主链 phi/psi、侧链 chi | sin/cos 编码 | 缺失角要标记 |
| 接触类型 | 氢键、疏水、盐桥 | 独热或多热编码 | 判定阈值需固定 |
2.3 突变信息的编码方式
突变本身怎么编码,是另一个容易被低估的细节。最朴素的做法是把野生型残基和突变型残基各做一个独热编码拼起来,但这样丢失了氨基酸的理化性质相似性。更好的做法是引入氨基酸的物化属性向量,比如疏水性、体积、电荷、极性,这样模型能理解"从亮氨酸突变成异亮氨酸"和"从亮氨酸突变成天冬氨酸"在性质上的差异程度不同。
还有一种做法是用突变前后的结构差异作为输入,也就是分别对野生型和突变型结构做几何特征提取,然后取差值或者拼接。这个做法信息最完整,但前提是你得有突变型的结构——通常需要用侧链重构工具生成。侧链重构本身有误差,会引入噪声,所以是否采用这条路线要看你的场景:如果突变主要影响侧链 packing,重构值得做;如果突变影响的是主链柔性,重构的帮助有限。
注意:侧链重构工具的选择和参数设置会显著影响结果,建议固定一套重构流程并在所有样本上一致使用,避免不同样本用了不同重构策略导致特征分布漂移。
3. 实操过程与核心环节实现
3.1 数据准备与清洗流程
动手之前先把数据理清楚。稳定性变化数据集通常来自实验测定的 ΔΔG,格式一般是蛋白 PDB 编号、突变位点、野生型残基、突变型残基、ΔΔG 值。清洗环节要做几件事:去掉 ΔΔG 缺失或标注矛盾的条目;检查 PDB 结构是否可获取、分辨率是否可接受(一般优于 2.5 埃比较稳妥);核对突变位点在结构中的残基编号是否与数据集一致,这里经常出现编号偏移的坑,比如数据集用的是成熟蛋白编号而结构用的是包含标签的编号,差几个号就会导致取错残基。
数据划分要特别小心。随机划分会导致同一蛋白的不同突变分散在训练集和测试集,模型实际上记住了这个蛋白的局部环境,测试精度虚高。更合理的做法是按蛋白划分,保证测试集里的蛋白在训练时没见过,这样评估的才是泛化能力。如果数据量允许,还可以做按家族划分或者按突变类型划分,看模型在跨家族、跨突变类型时的表现。
# 数据清洗伪代码示意 import pandas as pd df = pd.read_csv("stability_dataset.csv") # 去掉缺失值 df = df.dropna(subset=["ddg", "pdb_id", "wt", "mut", "position"]) # 过滤异常值,ΔΔG 通常在 -5 到 5 kcal/mol 之间 df = df[(df["ddg"] > -5) & (df["ddg"] < 5)] # 按蛋白划分,避免同源泄漏 proteins = df["pdb_id"].unique() test_proteins = proteins[:int(len(proteins) * 0.2)] test_df = df[df["pdb_id"].isin(test_proteins)] train_df = df[~df["pdb_id"].isin(test_proteins)]3.2 特征提取与图构建的落地
特征提取阶段,我习惯先把每个 PDB 结构解析成残基列表,记录每个残基的坐标、类型、主链和侧链原子。然后对每个突变样本,以突变位点为中心取邻域,构建局部图。节点是邻域内的残基,边按空间距离阈值连接,边特征就是前面说的距离、角度、接触类型。
这里有个性能优化点:同一个 PDB 上往往有多个突变样本,邻域提取可以缓存,不必每个样本都重新解析结构。我一般会先按 PDB 分组,解析一次结构,然后对组内每个突变分别取邻域。这个优化在数据量上千之后能省不少时间。
图构建完成后,节点特征和边特征要统一维度,方便批量处理。不同蛋白的邻域残基数不一样,需要 padding 或者用能处理变长图的框架。用 PyTorch Geometric 的话,直接把多个小图拼成一个大图加 batch 向量就行,比较省心。
3.3 模型训练的关键参数与调参经验
训练阶段几个参数值得单独说。学习率方面,图网络类模型我通常从 1e-3 起步,配合余弦退火或者 ReduceLROnPlateau,观察验证集损失是否平稳下降。批大小受图规模限制,邻域取 10 埃时单图节点数大概几十到一百多,批大小 16 到 32 比较常见。损失函数用 MSE 或 Huber,Huber 对异常值更鲁棒,稳定性数据里偶尔有测量误差大的样本,用 Huber 会稳一些。
正则化不能省。这类数据集规模通常不大,几千到几万条,模型很容易过拟合。dropout 设在 0.1 到 0.3 之间,权重衰减 1e-4 到 1e-5,再配合早停,验证集连续若干轮不降就停。我踩过的坑是早停的 patience 设太小,模型在验证集上抖动一下就被停掉,错过了后面的下降,一般 patience 设 10 到 20 轮比较合适。
评估指标别只看 MSE 或 MAE,还要看 Pearson 和 Spearman 相关系数,以及把 ΔΔG 二值化后的分类准确率(比如以 0 为界判断稳定/不稳定)。实际做突变筛选时,排序能力比绝对误差更重要,Spearman 高说明模型能把好的突变排在前面,这对实验设计更有指导意义。
| 参数 | 推荐范围 | 说明 |
|---|---|---|
| 学习率 | 1e-3 起步 | 配合调度器 |
| 批大小 | 16-32 | 受图规模限制 |
| dropout | 0.1-0.3 | 防过拟合 |
| 权重衰减 | 1e-4~1e-5 | 配合早停 |
| 早停 patience | 10-20 | 避免过早停止 |
3.4 推理与结果解读
模型训好之后,推理阶段要注意输入的一致性——新样本的预处理流程必须和训练时完全一致,邻域半径、特征归一化参数、缺失值处理策略都不能变。我见过有人推理时忘了加载训练集的归一化统计量,直接用测试集自己的统计量,结果分布对不上,预测全偏。
结果解读上,ΔΔG 的符号比绝对值更可靠。模型预测某个突变显著降低稳定性(ΔΔG 明显为负),这个信号通常比较可信;预测接近 0 的突变,误差往往较大,这类突变在实际筛选中优先级可以放低。另外建议对预测结果做不确定性估计,比如用 dropout 多次采样取方差,方差大的样本说明模型没把握,实验验证时可以优先排除或者单独处理。
4. 常见问题与排查技巧实录
4.1 训练损失不降或震荡
这是最常见的开局问题。排查顺序我一般是:先看数据管道有没有 bug,比如特征是不是全零、标签有没有对齐错位;再看学习率是不是太大,图网络对学习率比较敏感,1e-3 不降就试 1e-4;然后看特征归一化,距离特征没做径向基展开或者角度没做 sin/cos 编码,很容易导致训练不稳。还有一个隐蔽原因是图构建时边特征维度和节点特征维度对不上,或者 batch 拼接时索引错乱,这类 bug 不会报错但会让模型学不到东西,建议用小批量数据先跑通再放大。
4.2 验证集表现远低于训练集
典型的过拟合信号。先确认数据划分是不是按蛋白划分的,如果随机划分,验证集虚高反而不容易发现过拟合;按蛋白划分后如果差距大,说明模型记住了训练蛋白的局部特征。应对手段:加大 dropout、加权重衰减、减小模型容量、增加数据增强(比如对结构做轻微扰动、对邻域做随机采样)。数据增强这块要谨慎,几何扰动太大会破坏物理合理性,扰动幅度控制在合理范围内。
4.3 跨家族泛化差
如果测试集换成训练时没见过的蛋白家族,性能掉得厉害,说明模型学到的特征不够普适。这时候可以检查特征里有没有隐含家族偏置,比如某些接触类型特征在特定家族里分布很偏。缓解办法包括:在特征层面做更严格的归一化、引入更多与家族无关的几何量、用多任务学习让模型同时预测一些辅助任务(比如局部 packing 密度)来逼它学通用表征。
4.4 常见问题速查表
| 现象 | 可能原因 | 排查与解决 |
|---|---|---|
| 损失不降 | 学习率过大、特征未归一化 | 降学习率、检查特征处理 |
| 损失震荡 | 批大小过小、数据噪声 | 增大批大小、换 Huber 损失 |
| 验证集虚高 | 随机划分导致泄漏 | 改为按蛋白划分 |
| 跨家族掉点 | 特征含家族偏置 | 加强归一化、多任务辅助 |
| 推理全偏 | 预处理不一致 | 固定并复用训练期统计量 |
| 预测方差大 | 邻域信息不足 | 扩大半径、检查缺失原子 |
4.5 几个独家避坑心得
第一个心得:残基编号对齐这件事,一定要写脚本自动核对,别靠肉眼。我吃过亏,数据集里某个蛋白的编号和结构差了一位,导致整个蛋白的突变特征全取错,模型在这个蛋白上表现异常但又不报错,查了很久才发现。
第二个心得:邻域半径不要一刀切。不同大小的蛋白、不同埋藏深度的突变位点,合适的邻域范围其实不一样。表面残基的邻域可以小一点,核心残基的邻域要大一点才能覆盖完整的疏水环境。可以按溶剂可及性做自适应半径,这个改动不大但往往有稳定提升。
第三个心得:把野生型结构的预测作为基线。也就是说,先让模型预测野生型自身的"稳定性分数"(可以用一个辅助头),再看突变前后的差值。这个做法能把蛋白本身的难度差异归一化掉,对跨蛋白泛化有帮助,我在几个项目里试下来都比直接回归 ΔΔG 稳。
第四个心得:实验验证的反馈要回流。模型筛出来的突变做了实验后,不管成功失败,结果都值得记录下来重新加入训练集。稳定性数据本来就稀缺,每一批实验数据都是宝贵的增量,迭代几轮之后模型在你关心的蛋白家族上会明显更准。
5. 影响范围与延伸思考
GeoStab 这类几何驱动的稳定性预测方法,影响的不只是学术 benchmark 上的几个小数点。落到实际场景,酶工程里提高热稳定性、抗体开发里优化框架区稳定性、工业酶改造里延长货架期,这些工作都依赖对突变效应的准确预判。模型每提升一点排序能力,实验端就能少筛一批候选,省下的是实打实的时间和成本。
从技术演进角度看,几何表征这条路还有不少可挖的空间。比如把几何特征和序列语言模型的嵌入做融合,让模型既有进化信息的广度又有几何信息的精度;再比如引入分子动力学采样的构象系综,而不是只用单一晶体结构,来捕捉突变对柔性的影响。这些方向我在关注,但落地时还是要回到数据量和算力的现实约束上,别为了追新架构把可复现性丢了。
我自己在这个方向上的体会是,工具再花哨,最后决定成败的往往是数据质量和评估方式。把数据清洗做扎实、把划分做严谨、把评估指标选对,比换一个更复杂的网络结构带来的提升更实在。GeoStab 给了一个不错的几何建模起点,真正要用好它,还是得结合自己手头的蛋白体系和实验条件去调、去验、去迭代。