346

比较 DiffDock、Vina、GNINA:什么时候相信哪个结果

DiffDock、Vina、GNINA 各有适用场景。这篇给出系统的对比方法、判断依据,以及三者一致性检验的实际做法。

手上有三种对接方法,什么时候该信哪个?答案不是「哪个更准」,而是「在什么场景下哪个更可靠」。这篇给出系统的比较方法与决策框架。

三者的原理差异

AutoDock Vina GNINA DiffDock
范式 物理打分 + 随机搜索 搜索 + CNN 重打分 扩散生成
需要口袋 否(盲对接)
速度(单分子) 秒级 秒到十几秒 十几秒到分钟
硬件 CPU 即可 GPU 更快 需 GPU
可复现 固定 seed 即可 固定 seed 即可 采样有随机性
物理合理性 需检查
大规模筛选 最适合 可行 成本高

用自家体系做系统对比

公开基准的结论不能直接套用到你的靶点上,必须自己测:

from rdkit import Chem
from rdkit.Chem import rdMolAlign
import pandas as pd, subprocess, os

def redock_rmsd(method, receptor, ref_ligand_sdf, center, box):
    """重对接验证:能否复现共晶姿势"""
    ref = Chem.RemoveHs(Chem.SDMolSupplier(ref_ligand_sdf)[0])

    if method == "vina":
        subprocess.run(["vina", "--receptor", receptor,
                        "--ligand", "ref.pdbqt",
                        "--center_x", str(center[0]), "--center_y", str(center[1]),
                        "--center_z", str(center[2]),
                        "--size_x", str(box[0]), "--size_y", str(box[1]),
                        "--size_z", str(box[2]),
                        "--exhaustiveness", "32", "--seed", "42",
                        "--out", "vina_out.pdbqt"], check=True)
        subprocess.run(["mk_export.py", "vina_out.pdbqt", "-s", "vina_out.sdf"])
        pose = Chem.RemoveHs(Chem.SDMolSupplier("vina_out.sdf")[0])

    elif method == "gnina":
        subprocess.run(["./gnina", "-r", receptor, "-l", ref_ligand_sdf,
                        "--autobox_ligand", ref_ligand_sdf,
                        "--exhaustiveness", "32", "--seed", "42",
                        "--cnn_scoring", "rescore",
                        "-o", "gnina_out.sdf"], check=True)
        pose = Chem.RemoveHs(Chem.SDMolSupplier("gnina_out.sdf")[0])

    return rdMolAlign.CalcRMS(pose, ref)

# 对该靶点家族的多个共晶结构做重对接
cases = [("1M17", ...), ("4HJO", ...), ("5UG9", ...)]
results = []
for pdb_id, params in cases:
    for method in ["vina", "gnina", "diffdock"]:
        rmsd = redock_rmsd(method, *params)
        results.append({"pdb": pdb_id, "method": method, "rmsd": rmsd})

df = pd.DataFrame(results)
summary = df.groupby("method").agg(
    success_rate=("rmsd", lambda x: (x <= 2.0).mean()),
    median_rmsd=("rmsd", "median"),
)
print(summary)
# 在你的靶点家族上,哪个方法的成功率最高?

富集度对比(如果有活性数据)

import numpy as np

def enrichment_factor(scores, labels, frac=0.01, higher_is_better=False):
    n_top = max(1, int(len(scores) * frac))
    order = np.argsort(-scores if higher_is_better else scores)
    hits = labels[order[:n_top]].sum()
    return (hits / n_top) / (labels.sum() / len(labels))

# 准备:已知活性分子 + 诱饵(性质匹配)
print("Vina  EF@1%:", enrichment_factor(vina_scores, labels))
print("GNINA EF@1%:", enrichment_factor(cnn_affinity, labels, higher_is_better=True))
# DiffDock 的置信度不适合做富集排序,不参与此项比较

# 富集度才是虚拟筛选真正关心的指标,
# 而它与「姿势预测成功率」不是一回事

决策框架

# 场景 → 推荐方法

if 口袋未知 and 无同源结构:
    → DiffDock 做探索,找出候选口袋位置
    → 再用 Vina/GNINA 在该口袋做精细对接

elif 需要筛选百万级以上化合物库:
    → Vina(成本最低)
    → top 5% 用 GNINA 重打分

elif 有共晶结构 and 做类似物对接:
    → Vina 或 GNINA(重对接验证通过后)
    → 关键分子上 MM/GBSA 或 FEP

elif 关注姿势准确度 and 分子数量少:
    → GNINA(--cnn_scoring refinement)
    → 或 Boltz/Chai-1 复合物预测(见 344)

elif 需要亲和力排序:
    → 三者都不可靠
    → 用 FEP(见 201)或直接做实验

一致性检验:最实用的做法

from rdkit.Chem import rdMolAlign
import itertools

def consensus_check(poses_dict, threshold=2.0):
    """检查多个方法给出的姿势是否一致"""
    names = list(poses_dict.keys())
    agreements = {}
    for a, b in itertools.combinations(names, 2):
        try:
            rmsd = rdMolAlign.CalcRMS(poses_dict[a], poses_dict[b])
            agreements[f"{a}-{b}"] = rmsd
        except Exception:
            agreements[f"{a}-{b}"] = None

    consistent = sum(1 for v in agreements.values()
                     if v is not None and v <= threshold)
    total = sum(1 for v in agreements.values() if v is not None)
    return agreements, consistent, total

poses = {"vina": vina_pose, "gnina": gnina_pose, "diffdock": diffdock_pose}
agr, ok, total = consensus_check(poses)
for pair, rmsd in agr.items():
    print(f"{pair:20s} RMSD = {rmsd:.2f} Å" if rmsd else f"{pair}: N/A")

if ok == total:
    print("✓ 三个方法完全一致 —— 结合模式可信度高")
elif ok >= 1:
    print("~ 部分一致 —— 需要进一步验证(如 MD 或实验)")
else:
    print("✗ 各说各话 —— 该体系的结合模式尚不明确,不应据此做设计决策")

这是三种方法组合使用的最大价值:它们原理完全不同,如果给出一致的姿势,可信度远高于任何单一方法的高分;如果各说各话,那本身就是「不要在此基础上做决策」的重要警示。

共同的根本限制

无论哪种方法,打分与实测亲和力的相关性都很弱。算法差别主要体现在姿势预测上,而不是排序能力。选方法时应该问「它能不能给出正确的结合姿势」,而不是「它的分数准不准」。真正的亲和力预测需要 FEP 或实验。

常见坑与提示

  • 公开基准结论不能直接套用,必须在自家靶点家族上做重对接对比
  • 姿势预测成功率与虚拟筛选富集度是两回事,要分开评估;
  • 三方法一致性检验是最实用的可信度判据
  • 三者都不能可靠预测亲和力,排序需要 FEP 或实验。

延伸资源