ESP-DNN 本地部署实战: 7 年前的图卷积静电势模型一秒复现DFT精度的静电势表面

你在做先导化合物优化时想在几分钟内看到候选分子的静电势(ESP)表面,而不是排队等 DFT;你想给 espsim、MMGBSA 或者可视化软件喂一套"近 DFT 质量"的原子电荷,而不是粗糙的 Gasteiger;你找到 Astex 2019 年开源的 ESP-DNN 图卷积模型,结果发现官方仓库是 Python 2.7 + TF 1.10 的"化石",python -m esp_dnn.predict 第一行就报 SyntaxError。本文带你从 git clone 开始,用 6 处最小补丁把 ESP_DNN 搬到 Python 3.11 + TF 2.15,跑通 ligand / protein 双模式,4 个配体 + 2 个蛋白的 PQR 输出与官方 ground truth md5 逐字节一致,阿司匹林 / 咖啡因新分子全链 0.2--0.3 秒出结果。

关键词:ESP-DNN、静电势表面、图卷积网络、Astex、PQR、PLI、off-center 电荷、Python 3 迁移

相关教程与核心文献

资源 链接 与本文关系
AstexUK/ESP_DNN 仓库 github.com/AstexUK/ESP_DNN 部署对象:DNN 模型 + PLI 二进制 + 示例
Rathi P, Ludlow RF, Verdonk ML, J. Med. Chem. 2020, 63(16), 8778--8790 10.1021/acs.jmedchem.9b01129 原始论文(online 2019-09-25)
NGL Viewer nglviewer.org/ngl 官方推荐的 ESP 表面可视化
AstexUK/esp-surface-generator github.com/AstexUK/esp-surface-generator NGL 表面网格导出脚本
PQR 格式说明 apbs-pdb2pqr 文档 输出格式:坐标+电荷+半径

先看看文章中模型的效果:

训练集以及测试集上的验证

针对训练集生成的DFT-fp对DNN模型进行了训练。 上图显示了DFT-fp Δq值相对于训练集的预测DNN-fp值的曲线图。 显然,该模型能够很好地描述训练集分子的点电荷,R2 = 0.996,RMSE Δq= 0.010e。 此外,在整个训练集中RMSEV平均值为3.30 kcal / mol,DNN-fp 模型生成的ESP表面质量近似于DFT-fp计算出的ESP表面。 将DNN-fp模型在测试集中的表现,显示DNN-fp和DFT-fpΔq值之间具有良好的相关性,其中R2 = 0.981,RMSE Δq= 0.022e。

通过DNN-fp模型生成的ESP表面积

§1. 这个工具解决什么问题

众所周知,分子的静电性质可以用于预测化学反应性及分子间相互作用。常规流程是在分子表面移动单位电荷探针、逐点算 ESP 再着色,得到 ESP 表面。药物研发中静电互补优化至关重要------但 ESP 表面质量基本取决于底层静电模型,而蛋白-配体相互作用的静电效应只有量子力学(QM)计算能准确捕获,DFT 单分子动辄小时级,没法当常规药物设计工具用。

行业折中一直很尴尬:

电荷模型 速度 质量 问题
Gasteiger / MMFF 毫秒 粗糙 σ-hole、孤对方向性几乎不体现
AM1-BCC 分钟级 中等 仍是半经验拟合,非 ESP 直接拟合
RESP / DFT-fp 小时级 精确 无法交互式使用
ESP-DNN ~0.2 s/分子 近 DFT 需本地部署 + Py2 迁移

ESP-DNN 的卖点是:给配体预测的原子电荷外加 off-center 虚拟电荷点(孤对、σ-hole、p 轨道位置),一起写进 PQR 文件,直接喂 NGL/APBS 生成"近 DFT 质量"的 ESP 表面------化学家在可视化软件里编辑结构的间隙,ESP 表面就能刷新。

§2. 方法速览:图卷积 DNN 怎么学静电势

论文的构建路线(也是用户实测背景素材的对应关系):

训练集 :从 eMolecules 和 Sigma-Aldrich 可购买化合物中选取,最大程度覆盖化学空间,约束 HAC≤17、仅含 B/Br/C/Cl/F/H/I/N/O/P/S,共 100,500 个分子 ;测试集从 ChEMBL 构建,共 4,815 个。

标签生成:对每个分子(1)DFT 算电子密度 →(2)生成分子表面 →(3)在表面逐点算 ESP 值 →(4)拟合超额点电荷 Δq(含 off-center 位点),得到 DFT-fp 参照。

模型 :图卷积网络,输入原子特征 + 邻居矩阵,输出每个原子的 Δq。论文数字(测试集):DNN-fp 与 DFT-fp 的 Δq 相关性 R² = 0.981,RMSE = 0.022 e (训练集 R² = 0.996,RMSE = 0.010 e);测试集 ESP 表面平均 RMSE_V 3.27 kcal/mol,与 DFT-fp 自身的 3.15 几乎持平,显著优于 AM1-BCC ESP。速度上比 DFT 快 4 个数量级。

推理链路(部署后实际跑的就是这条):

复制代码
.mol → RDKit MolToPDBBlock → pdb
                            → DNN 预测重原子 Δq → dq.dat
                                                → PLI 二进制(加 off-center 点)→ .pqr

带电分子的处理:模型只在中性分子 上训练,推理时先把输入中性化再预测,随后 apply_charge_correction 对形式电荷原子加 0.4×formal-charge 增量、等价原子(羧酸根两个 O 等)电荷取平均。这意味着对带电配体它的 ESP 形貌预测有系统性局限(后文 §10)。

§3. 本地部署:从 clone 到跑通

4.1 系统要求

README 写"仅支持 64 位 Linux"------严格说这是 PLI 二进制的要求:ext/pli/bin/pli 是静态链接 ELF (file 输出 statically linked,5.3 MB),任何 x86-64 Linux 直接 exec,无需装任何动态库;Windows 用户走 WSL2 即可。Python 部分纯跨平台。

4.2 克隆仓库

复制代码
git clone --depth 1 https://github.com/AstexUK/ESP_DNN.git
cd ESP_DNN

实测 20,909 个文件、311 MB ------大头是 PLI 的 pdbdict 和模型文件(esp_dnn/model/trained_model.h5 约 300 MB 级 h5 + norm_params.pkl)。clone 一次几分钟。

4.3 建环境(Py3.11 + TF 2.15)

官方 environment.yml 是化石级配置:

复制代码
# 官方 environment.yml(2019)
python=2.7.15, rdkit==2018.09.3, keras==2.2.4,
tensorflow==1.10.0, numpy==1.16.2, xarray==0.11.3

Py2.7 环境在 2026 年的机器上已经没有维护意义,直接建 Py3 环境(TF 2.15 的 wheel 只到 Python 3.11,这是版本锚点):

复制代码
# venv(uv 或 python -m venv 均可)
uv venv --python 3.11 ./venv
uv pip install --python ./venv/bin/python \
  "numpy==1.26.4" pandas xarray \
  "tensorflow==2.15.0" rdkit scipy

GPU 非必需:本机实测纯 CPU 推理单分子 0.2--0.3 s,消费级机器完全够用,模型也小(加载 1.9 s)。

4.4 第一跑:SyntaxError

复制代码
python -m esp_dnn.predict -m ligand -i examples/ligands

File ".../esp_dnn/predict.py", line 412
    except AIChargeError, e:
           ^^^^^^^^^^^^^^^^
SyntaxError: multiple exception types must be parenthesized

Py2 的 except X, e: 语法,Py3.11 连 import 都过不去。这就是需要补丁的信号。

§4. 6 处最小补丁:Py2/TF1 → Py3.11/TF2.15

原则:只改兼容性,不动算法逻辑,改完以官方 ground truth 的 md5 做回归验证。共 6 处,按文件分组:

# 文件 Py2/TF1 原写法 Py3/TF2.15 修法 不改会怎样
1 predict.py except AIChargeError, e: except AIChargeError as e: SyntaxError,第一步就死
2 predict.py pickle.load(f)(文本模式) open(..., "rb") + pickle.load(f, encoding="latin1") norm_params.pkl 是 Py2 写的,Py3 默认 ASCII 解码直接 UnicodeDecodeError
3 predict.py tf.get_default_graph() + with self.graph.as_default(): 删除,直接 self.model.predict(...) TF2 无 default graph 概念,AttributeError
4 predict.py self.featurizer.get_mol_fetaures(mol) get_mol_features(mol)(与修好的 featurize.py 统一) AttributeError(原名就是拼写错误)
5 atom_features.py / graph_conv.py / model_factory.py from rdkit.Chem.AtomPairs.Utils import NumPiElectrons;from keras.engine.topology import Layer;keras.optimizers.Adam RDKit 2024+: rdchem.GetNumPiElectrons;TF2: tensorflow.keras.layers.Layer;tensorflow.keras.optimizers.legacy.Adam;另 range(1,19)+[None] → list(range(1,19))+[None] ImportError 逐个爆
6 data_processing.py pickle.load(f) 同 #2 加 encoding="latin1" 同 #2

几点说明:

  • 最隐蔽的是 #2 的 latin1:Py2 pickle 里 str 是原生字节,Py3 默认按 ASCII 解码,文件头几个字节就抛 UnicodeDecodeError,报错位置还不在业务代码里,容易误判成文件损坏。
  • TF2 的 Adam 落点 :tf.keras.optimizers.Adam 在 TF 2.16+ 整体移到 optimizers.legacy,TF 2.15 环境两者都可,但写 legacy 路径向下兼容更稳。
  • graph_conv.py 里 TF1 的 tf.Print 调试层在 TF2 已删,随 #5 一起清理。
  • 模型 h5 本身不需要 任何转换------Keras 2.15 的 load_model(配合自定义层注册)能直接读 2019 年的权重,这是这次迁移成本低的关键。

打完补丁先做语法检查:

复制代码
python -m py_compile esp_dnn/predict.py esp_dnn/atom_features.py \
  esp_dnn/graph_conv.py esp_dnn/model_factory.py \
  esp_dnn/data_processing.py esp_dnn/featurize.py

§5. 安装测试:md5 逐字节复现官方结果

验证标准:仓库 examples/ 里每个输入都带 *.pqr.saved 官方输出,本地跑完 md5sum 逐字节对比。字节级一致 = 补丁没有引入任何数值偏差(浮点都一样,说明 DNN 前向传播 + PLI 参数链完全复现)。

6.1 ligand 模式(README 官方命令)

复制代码
cd ESP_DNN
python -m esp_dnn.predict -m ligand -i examples/ligands -o ./out_lig   # out_lig 需先 mkdir
python -m esp_dnn.predict -m protein -i examples/proteins -o ./out_prot

实测结果(消费级 CPU,2026-09-30):

模式 输入 输出 md5 vs 官方 saved 耗时
ligand lig1.pdb lig1.pdb.pqr(63 行) 一致 全批 4 分子 3.0 s(含模型加载)
ligand lig2.pdb lig2.pdb.pqr(62 行) 一致 ---
ligand lig3.pdb lig3.pdb.pqr(65 行) 一致 ---
ligand lig1_charged.pdb lig1_charged.pdb.pqr(63 行) 一致 ---
protein 1akg_neutral.pdb 27,619 B 一致 全批 0.56 s
protein 5c7a_charged.pdb 177,926 B 一致 ---

6/6 全部 md5 一致。 protein 模式不经过 DNN(氨基酸用参数化电荷,由 PLI 直接处理),所以更快;5c7a_charged 这种上千原子的蛋白也在半秒内完成。

注意 ligand 模式如果输入目录里有 .mol 文件(examples 自带),程序会先用 RDKit 转出 .mol.pdb 再统一处理------所以 examples 里 4 个配体实际产出的 PQR 文件名是 lig1.mol.pdb.pqr 系,和 *.saved 的 md5 是对得上的(lig1.pdb.pqr.saved 与 lig1.mol.pdb.pqr 内容一致,两条路殊途同归)。

§6. 新分子实测:阿司匹林与咖啡因

示例复现只能证明"补丁无害",还要看对新分子的泛化。从 SMILES 出发走完整链路(这是实际使用时的标准姿势):

复制代码
from rdkit import Chem
from rdkit.Chem import AllChem

for name, smi in {
    "aspirin": "CC(=O)Oc1ccccc1C(=O)O",
    "caffeine": "CN1C=NC2=C1C(=O)N(C)C(=O)N2C",   # C8H10N4O2, 24 原子
}.items():
    m = Chem.AddHs(Chem.MolFromSmiles(smi))
    AllChem.EmbedMolecule(m, randomSeed=42)
    w = Chem.SDWriter(f"{name}.mol"); w.write(m); w.close()

然后把这两个 .mol 放进一个空目录(目录里不要有别的 pdb),从仓库根目录跑:

复制代码
python -m esp_dnn.predict -m ligand -i /path/to/mols

实测(暖模型后逐分子计时):

分子 原子数 PQR 行数 构成 总电荷 单分子耗时
阿司匹林 21 49 13 重原子 + 8 H + 8 Hlp + 20 Hp −0.0009 e 260 ms
咖啡因 24 51 14 重原子 + 10 H + 5 Hlp + 22 Hp +0.0001 e 216 ms

总电荷归零到 10⁻³ e 量级(推理后减均值 + 等价原子平均),off-center 点数量随杂原子数变化------咖啡因 4 个 N 出 5 个孤对位,阿司匹林酯/羧酸 O 出 8 个。一个小坑提醒:写 SMILES 时先 CalcMolFormula 核一遍分子式再往下走,我第一版咖啡因 SMILES 手滑多写了一个 N,29 原子的"咖啡因"照样全链跑通------工具不会替你检查化学合理性。

速度上:模型加载 1.92 s(一次性),之后每个分子 0.2--0.3 s(DNN 前向 + PLI),确实到了"交互式刷新 ESP"的量级,和论文宣传一致。

§7. PQR 输出解读与可视化

PQR 每行 = PDB 坐标 + 电荷(occupancy 列)+ 半径(bfactor 列)。三种"虚拟原子"的命名语义(从 ext/pli/params/features.pli 实查,H 前缀表示伪原子):

名称 含义 来源
Hh 真实氢原子(带 DNN/Gasteiger 链路的电荷) on-atom
Hlp 孤对(lone pair)off-center 点,如 N/O 孤对方向 ftype lp "lone pair" off-centred
Hp p 轨道 off-center 点,芳香/共轭体系 π 分布 ftype p
Hsh σ-hole off-center 点,卤素/硫反键方向 ftype sh "sigma hole" off-centred

阿司匹林 PQR 节选(第 2 列电荷):

复制代码
HETATM   12  O3 UNL _   1    0.786  -2.058   0.526  -0.4164  1.6000   # 羰基 O, 负电荷集中
HETATM   19 Hlp UNL _   1   -2.694  -0.726   1.877  -0.0060  0.8700   # 孤对虚拟点
HETATM   43 Hlp UNL _   1    1.013  -2.773   0.805  -0.0074  0.8700

可视化按 README 走 NGL Viewer(nglviewer.org/ngl):File 打开 PQR → 添加 surface 表示 → surfaceType=av、radiusType=explicit、colorScheme=electrostatic,colorDomain 建议 -50, 50 kcal/mol,colorScale=rwb。Jupyter 内用 nglview 同参数。要导出表面网格数据(顶点+ESP 值)就用官方配套的 NodeJS 脚本 esp-surface-generator。

对药化工作流,这套 PQR 还有两个下游用法:电荷列直接作为 espsim 的自定义电荷算 ESP 相似度;或与蛋白 PQR(protein 模式产物)一起做静电互补定性检查。

§8. 踩坑清单

# 症状 根因 修复
1 SyntaxError: multiple exception types must be parenthesized Py2 except X, e: 语法 §5 补丁 #1
2 UnicodeDecodeError(加载 norm_params.pkl) Py2 pickle 字节流 vs Py3 ASCII 默认解码 open(...,"rb") + encoding="latin1"
3 AttributeError: module 'tensorflow' has no attribute 'get_default_graph' TF1 graph API 在 TF2 删除 删掉 graph 上下文直接 predict
4 OSError: Bad output file .../lig2.mol.pdb -o 输出目录不会自动创建 先 mkdir -p 输出目录
5 -o 指定独立目录时只产出 .mol.pdb 中间文件,没有 PQR,日志末尾 No input files found 源码两阶段 glob 脱节:.mol→.pdb 写进输出 目录,但第二阶段 glob *.pdb 只扫输入 目录。官方 examples 能跑通纯粹因为仓库自带 .pdb 不用 -o,让输入输出同目录(PQR 会产在 .mol 旁边);或先把 .mol 转成 .pdb 后放入输入目录
6 ModuleNotFoundError: No module named 'esp_dnn' python -m 需要仓库根在 sys.path cd 进仓库根再跑(README 有说明),或 pip install -e .
7 Windows git clone 报设备名错误 仓库 cif/CON/CON.cif、cif/PRN/PRN.cif 撞 Windows 保留设备名 用 codeload tarball + unzip 绕过
8 想设 PLI_DIR 环境变量 其实不必须 predict.py 自动探测 esp_dnn/ext/pli,只有挪动二进制时才需要设

坑 5 是这次实测抓到的最有价值的一个:它不是报错,而是静默空转 (exit 0、无 PQR、只有一行 INFO 级 No input files found),如果不在 examples 之外的目录试新分子根本发现不了。

§9. 局限与适用边界

  • 训练集全中性 + HAC≤17 + 11 种元素:带电配体的电荷预测靠中性化+0.4 修正近似,ESP 形貌对净电荷差异不敏感(此前实测同一骨架 ±0.4 e 对,ESP 相似度仍高达 0.9+,判别力丢失);超出元素/大小域的分子(金属配体、大环)慎用。
  • 模型是 2019 年定格的:仓库 6 年未更新主模型,后续有 EspalomaCharge 等 GNN 电荷新工作可作对比,但"off-center 点 + 蛋白参数化电荷 + NGL 交互"这套完整链路仍是 Astex 独家。
  • PLI 二进制闭源 (静态 ELF 可执行,参数表 elements.pli/features.pli 可读但不可改):想自定义 off-center 类型或扩元素只能等上游。
  • 何时该切换任务:要绝对精确 → 直接 DFT/RESP;要蛋白-配体互补性打分 → 这套 PQR 定性看可以,定量要上 APBS 类求解器。

参考来源

资源 链接
AstexUK/ESP_DNN github.com/AstexUK/ESP_DNN
Rathi 等 J. Med. Chem. 2020, 63(16), 8778--8790 10.1021/acs.jmedchem.9b01129
NGL Viewer nglviewer.org/ngl
esp-surface-generator github.com/AstexUK/esp-surface-generator
PQR 格式 apbs-pdb2pqr.readthedocs.io
iwatobipen ESP_DNN Py3 fork(早期参照) github.com/iwatobipen/ESP_DNN

系列导航 :本专栏为 CADD 中的机器学习模型 ,本文是静电势预测模型部署方向,专栏全集点 这里。

更多专栏:

蛋白 / 多肽 分子模拟 / 动力学 分子对接 / CADD / 工具 其他
开源蛋白结构推理预测 分子模拟基础 UCSF DOCK系列 agent智能体系列
开源蛋白生成方法实践 分子动力学模拟-Amber rDock系列 化学大模型介绍(2025)
蛋白药物设计-原理与案例剖析 分子动力学模拟-Gromacs LeDock系列 我胡师兄说药
开源多肽设计模型和方法实践 結合自由能 CADD中的机器学习模型 siRNA药物设计模型
开源多肽性质预测 高效计算基本配置 小分子药物设计-原理与案例剖析 ASO药物设计模型
多肽药物设计-原理与案例剖析 作用于DNA/RNA的药物设计实践 开源小分子生成和设计实践 开源药代动力学模拟软件
抗体设计-原理与案例剖析 免疫学计算-原理与案例剖析 合成生物学-原理与案例剖析 AI制药-原理与案例剖析
相关推荐
源于花海2 年前
GCN从理论到实践——基于PyTorch的图卷积网络层实现
人工智能·pytorch·神经网络·gcn·图卷积网络