345

用 DiffDock 预测 Docking Pose:深度学习对接实战

DiffDock 用扩散模型做盲对接,不需要指定口袋。这篇给出部署运行命令、置信度的正确读法与结果验证流程。

DiffDock(见 184《DiffDock》)的独特能力是盲对接:不需要事先知道口袋在哪,模型直接在整个蛋白上采样结合姿势。这在口袋未知或存在多个候选口袋时很有价值。

部署

git clone https://github.com/gcorso/DiffDock.git
cd DiffDock
mamba env create -f environment.yml
conda activate diffdock

# 单个复合物
python -m inference --config default_inference_args.yaml \
  --protein_path receptor.pdb \
  --ligand "COc1cc2ncnc(Nc3ccc(F)c(Cl)c3)c2cc1OCCCN1CCOCC1" \
  --out_dir results/
# 批量:用 CSV 传入
# input.csv
complex_name,protein_path,ligand_description,protein_sequence
lig1,receptor.pdb,CC(=O)Oc1ccccc1C(=O)O,
lig2,receptor.pdb,COc1ccc2nc(N)sc2c1,

python -m inference --config default_inference_args.yaml \
  --protein_ligand_csv input.csv --out_dir results/

需要 GPU(推荐 ≥16 GB 显存),依赖 torch-geometrice3nn。首次运行会下载预训练权重。

关键参数

参数 含义 建议
samples_per_complex 采样姿势数 10~40;越多越可能采到正确姿势
inference_steps 扩散去噪步数 默认 20 通常够用
--no_final_step_noise 末步去噪声 开启通常更稳
actual_steps 实际执行步数 可略小于 inference_steps

置信度的正确读法

import glob, re, os
import pandas as pd

# 输出文件名形如 rank1_confidence-0.42.sdf
rows = []
for f in glob.glob("results/*/rank*_confidence*.sdf"):
    m = re.search(r"rank(\d+)_confidence(-?[\d.]+)\.sdf", os.path.basename(f))
    if m:
        rows.append({"complex": os.path.basename(os.path.dirname(f)),
                     "rank": int(m.group(1)),
                     "confidence": float(m.group(2)),
                     "path": f})

df = pd.DataFrame(rows).sort_values(["complex", "rank"])
print(df.head(10))
置信度 解读 建议
> 0 较可信 值得进入下一步分析
−1.5 ~ 0 不确定 需交叉验证
< −1.5 低可信 基本不应采纳

置信度不是亲和力。它只回答「这个姿势像不像真的」,完全不回答「这个分子结合得强不强」。用它做虚拟筛选排序是常见误用——DiffDock 不适合用来排序化合物活性。

必做的验证流程

# 1) 物理有效性检查 —— 这一步对 DiffDock 尤其重要
from posebusters import PoseBusters

buster = PoseBusters(config="dock")
results = buster.bust(df["path"].tolist(), None, "receptor.pdb")

pass_rate = results.all(axis=1).mean()
print(f"通过物理检查的姿势比例: {pass_rate:.1%}")

# 独立评测发现,DiffDock 生成的姿势中
# 相当比例存在键长键角畸变或与蛋白空间冲突。
# 不做这一步就直接分析,很可能建立在错误结构上。

failed_checks = results.columns[~results.all()].tolist()
print("常见失败项:", failed_checks[:5])
# 2) 与传统对接交叉验证
from rdkit import Chem
from rdkit.Chem import rdMolAlign

diffdock_pose = Chem.SDMolSupplier("results/lig1/rank1_confidence0.42.sdf")[0]
vina_pose     = Chem.SDMolSupplier("vina_out.sdf")[0]
gnina_pose    = Chem.SDMolSupplier("gnina_out.sdf")[0]

for name, pose in [("Vina", vina_pose), ("GNINA", gnina_pose)]:
    try:
        rmsd = rdMolAlign.CalcRMS(diffdock_pose, pose)
        print(f"DiffDock vs {name}: RMSD = {rmsd:.2f} Å")
    except Exception as e:
        print(f"{name}: 无法比较 ({e})")

# 三条不同原理的方法给出一致姿势 → 可信度大增
# 各说各话 → 该体系的结合模式尚不明确
# 3) 能量最小化,修正物理问题
# 用 OpenMM 在受体固定的情况下最小化配体
# 或用 smina 做局部优化
smina -r receptor.pdb -l diffdock_pose.sdf \
      --minimize --minimize_iters 1000 -o minimized.sdf

独立评测提出的质疑

  • 训练集相似性影响大:多项独立工作指出,在 PDBBind 时间划分下 DiffDock 的成功率明显低于原论文数字;对与训练集相似度低的新蛋白,表现下降显著。
  • 物理合理性问题:PoseBusters 等检查发现相当比例的姿势通不过基本物理检验。
  • apo / 预测结构上更弱:在非共晶结构上成功率比在 holo 结构上低不少,而这恰恰是「没有结构所以要盲对接」的典型处境——这个矛盾需要正视。
  • 后续版本有改进:DiffDock-L 等扩大了训练数据并改善泛化,选版本时留意。

务实的定位

  • 用它做口袋未知时的探索——这是它相对传统对接的真正优势场景;
  • 把姿势当假设而非结论,与 Vina/GNINA 交叉对比;
  • 不要用置信度做亲和力排序
  • 选中的姿势做能量最小化或短程 MD,滤掉物理上站不住的结果;
  • 如果已知口袋位置,传统对接通常更可靠也更快

常见坑与提示

  • 优势是盲对接(不需已知口袋),不是精度也不是排序能力;
  • 置信度衡量姿势可信度,绝不等于亲和力
  • 结果必须过 PoseBusters 物理检查,畸变姿势比例不低;
  • 与 Vina/GNINA 交叉验证,三者一致才采纳。

延伸资源