DiffDock 的独特能力是盲对接——不需要指定口袋位置就能预测结合姿势。但它生成的姿势有相当比例通不过物理有效性检查,因此后处理是必需而非可选的。
安装与运行
# 推荐用官方的 conda 环境(依赖较复杂)
git clone https://github.com/gcorso/DiffDock.git
cd DiffDock
conda env create -f environment.yml
conda activate diffdock
# ---- 单个复合物 ----
python -m inference \
--protein_path receptor.pdb \
--ligand_description "CC(=O)Nc1ccc(O)cc1" \
--out_dir results/ \
--samples_per_complex 40 \
--inference_steps 20
# ligand_description 可以是 SMILES 或 SDF 路径
# ---- 批量(用 CSV)----
# input.csv:
# complex_name,protein_path,ligand_description,protein_sequence
# cmpd1,rec.pdb,CC(=O)Nc1ccc(O)cc1,
# cmpd2,rec.pdb,ligands/cmpd2.sdf,
python -m inference \
--protein_ligand_csv input.csv \
--out_dir results/ \
--samples_per_complex 20
# ---- 输出 ----
# results/<complex_name>/
# rank1_confidence-0.85.sdf
# rank2_confidence-0.42.sdf
# ...
# 【文件名中的数字是置信度分数】
# ---- 关键参数 ----
# --samples_per_complex 生成候选数(10~40)
# 越多越可能包含正确姿势,但耗时线性增长
# --inference_steps 去噪步数(默认 20)
# 增大可能提升质量
# --actual_steps 实际执行的步数(可小于 inference_steps)
必需的后处理流程
# 【只用 rank1 而不做检查,是最常见的误用】
# ---- 第 1 步:物理有效性检查 ----
pip install posebusters
from posebusters import PoseBusters
import pandas as pd
buster = PoseBusters(config="dock")
df = buster.bust(["results/cmpd1/rank1.sdf"], None, "receptor.pdb")
# 检查项包括:
# 分子层面:SMILES 合法、键长键角、芳环平面性、
# 立体化学、内部空间冲突
# 复合物层面:与蛋白的空间冲突、是否在口袋内
#
# 【任何一项不通过 → 该姿势不应用于设计决策】
passed = df.iloc[0].all()
print("通过所有检查:", passed)
# ---- 第 2 步:局部优化修正 ----
# 对轻微不合理的姿势,用力场优化修正
smina -r receptor.pdb -l results/cmpd1/rank1.sdf \
--minimize --minimize_iters 1000 -o rank1_min.sdf
# ---- 第 3 步:用经验打分作第二判据 ----
smina -r receptor.pdb -l rank1_min.sdf --score_only
# 【DiffDock 的置信度不是亲和力】
# → 需要独立的打分
# ---- 第 4 步:相互作用核对 ----
# 用 PLIP/ProLIF 检查是否复现关键相互作用(见 108、109)
# ---- 第 5 步:与传统方法比较 ----
# 与 Vina/GNINA 的结果比较一致性(见 346)
# ---- 完整的后处理脚本 ----
import glob
from rdkit import Chem
from posebusters import PoseBusters
import subprocess, re
def postprocess(complex_dir, receptor):
buster = PoseBusters(config="dock")
kept = []
for path in sorted(glob.glob(f"{complex_dir}/rank*.sdf")):
# 物理检查
df = buster.bust([path], None, receptor)
if not df.iloc[0].all():
continue
# 提取置信度
m = re.search(r"confidence([-\d.]+)", path)
conf = float(m.group(1)) if m else None
# smina 重打分
out = subprocess.run(
["smina", "-r", receptor, "-l", path, "--score_only"],
capture_output=True, text=True).stdout
aff = re.search(r"Affinity:\s+([-\d.]+)", out)
kept.append({"path": path, "confidence": conf,
"smina_score": float(aff.group(1)) if aff else None})
return kept
# 【判读】:
# 置信度高 + 通过物理检查 + smina 分数好 + 复现关键相互作用
# → 才是可用的姿势
多次采样的一致性判断
# 【这是最实用的可信度判据】
import numpy as np
from rdkit import Chem
from rdkit.Chem import rdMolAlign
import glob
def sampling_consistency(complex_dir, top_k=10, threshold=2.0):
"""检查前 k 个姿势是否聚集在同一位置"""
mols = []
for path in sorted(glob.glob(f"{complex_dir}/rank*.sdf"))[:top_k]:
m = Chem.SDMolSupplier(path)[0]
if m:
mols.append(Chem.RemoveHs(m))
rmsds = []
for i in range(len(mols)):
for j in range(i + 1, len(mols)):
try:
rmsds.append(rdMolAlign.CalcRMS(mols[i], mols[j]))
except Exception:
continue
if not rmsds:
return None
rmsds = np.array(rmsds)
agree = (rmsds < threshold).mean()
return {
"median_rmsd": float(np.median(rmsds)),
"agreement": float(agree),
"verdict": ("高一致性,可信" if agree > 0.7 else
"中等,需进一步验证" if agree > 0.4 else
"【各说各话,不应据此决策】"),
}
# 【判读逻辑】:
# 模型如果真的「知道」配体结合在哪,
# 多次采样应该聚集在同一位置
# → 分散说明模型不确定
适用场景的判断
| 场景 | DiffDock 是否合适 |
|---|---|
| 不知道口袋在哪 | 最合适——原生优势 |
| 探索可能的结合位点 | 合适,作为假设生成 |
| 与传统方法交叉验证 | 推荐——原理不同,一致性有意义 |
| 口袋已知的常规对接 | 优势不明显,Vina/GNINA 更成熟 |
| 大规模虚拟筛选 | 不适合——无可靠的亲和力信号 |
| 先导优化的细微排序 | 不适合 |
| 新颖靶点(与训练集差异大) | 谨慎——泛化能力存疑 |
常见问题
- 环境安装困难:依赖 PyTorch Geometric、e3nn 等,版本组合敏感。建议严格按官方 environment.yml,或用容器;
- 显存不足:大蛋白 + 多采样会占用较多显存,可减少
samples_per_complex; - 置信度与实际质量不完全对应:高置信度不保证物理合理,必须做 PoseBusters 检查;
- 对大蛋白的处理:模型会先做口袋预测再对接,超大蛋白可能效果下降;
- 配体的准备:SMILES 输入时的质子化态与立体化学要正确。
关键要点
- 原生优势是盲对接;口袋已知时相对传统方法优势不明显;
- 置信度不是亲和力,且高置信度不保证物理合理;
- PoseBusters 检查 + smina 局部优化是必需的后处理,不是可选项;
- 多次采样的一致性是最实用的可信度判据——分散就不该据此决策。
延伸资源
- DiffDock 论文:101《DiffDock 论文精读》;方法对比:346《比较 DiffDock、Vina、GNINA》;
- 姿势验证:096《Binding Pose》;smina:100《Smina》;GNINA 教程:016《GNINA 教程》。