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-geometric、e3nn。首次运行会下载预训练权重。
关键参数
| 参数 | 含义 | 建议 |
|---|---|---|
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 交叉验证,三者一致才采纳。