337

用 GNINA 做 Rescoring:深度学习重打分实战

GNINA 的 CNN 重打分可提升对接姿势质量。这篇给出重打分实战命令、三个分数的正确用法与分级筛选策略。

Vina 跑完初筛之后,下一步通常是重打分(rescoring):用更精细(也更慢)的方法对 top 分子重新排序。GNINA(见 183《GNINA》)的 CNN 重打分是性价比最高的选择之一。

安装

# 预编译二进制最省事
wget https://github.com/gnina/gnina/releases/latest/download/gnina
chmod +x gnina && ./gnina --version

# 或用容器
docker pull gnina/gnina

GNINA 有两个直接的工程便利:直接读 PDB/SDF,不必转 PDBQT--autobox_ligand 自动定盒子,消除了手工填坐标这个高频错误源。

两种重打分模式

# 模式一:只对已有姿势重打分(最快,不重新搜索)
./gnina -r receptor.pdb -l vina_poses.sdf \
        --score_only --cnn_scoring rescore \
        -o rescored.sdf --log rescore.log

# 模式二:重新对接 + CNN 重打分(推荐,姿势也会改善)
./gnina -r receptor.pdb -l top_ligands.sdf \
        --autobox_ligand ref_ligand.sdf --autobox_add 4 \
        --exhaustiveness 32 --num_modes 9 --seed 42 \
        --cnn_scoring rescore \
        -o gnina_out.sdf --log gnina.log
--cnn_scoring 行为 相对速度 建议
none 只用经验打分(等价 smina) 超大库初筛
rescore 搜索用经验分,最终姿势用 CNN 重打分 约 2× 默认推荐
refinement CNN 参与姿势精修 约 20× 少量重点分子
all 全程 CNN 最慢 研究用途

三个分数:含义完全不同

from rdkit import Chem
import pandas as pd

supp = Chem.SDMolSupplier("gnina_out.sdf")
rows = []
for mol in supp:
    if mol is None:
        continue
    rows.append({
        "name": mol.GetProp("_Name"),
        "affinity": float(mol.GetProp("minimizedAffinity")),   # kcal/mol,越负越好
        "cnn_score": float(mol.GetProp("CNNscore")),           # 0~1,姿势可信度
        "cnn_affinity": float(mol.GetProp("CNNaffinity")),     # pK 尺度,越大越好
    })
df = pd.DataFrame(rows)
  • minimizedAffinity:Vina 类经验打分,kcal/mol,越负越好;
  • CNNscore:CNN 判断该姿势「像不像真实结合姿势」的概率,0~1。它评价的是姿势可信度,不是亲和力
  • CNNaffinity:CNN 预测的亲和力(pK 尺度),越大越好。

混用这三个是最常见的误用。正确的组合方式:

# 两阶段筛选:先用 CNNscore 过滤姿势不可信的,再按亲和力排序
reliable = df[df["cnn_score"] > 0.5]        # 姿势可信
print(f"{len(reliable)}/{len(df)} 个姿势通过可信度过滤")

ranked = reliable.sort_values("cnn_affinity", ascending=False)
print(ranked.head(20))

# 也可看共识:三个分数都排在前面的分子最可信
df["rank_aff"] = df["affinity"].rank()
df["rank_cnn"] = df["cnn_affinity"].rank(ascending=False)
df["consensus"] = df["rank_aff"] + df["rank_cnn"]
print(df.nsmallest(20, "consensus"))

分级筛选策略

# 完整的分级流程 —— 每级成本上升、数量下降

# 第 1 级:性质与结构过滤(百万级 → 几十万)
#   RDKit 规则过滤 + PAINS/反应性过滤

# 第 2 级:Vina 快速对接(几十万 → 前 5%)
#   exhaustiveness=8,最快

# 第 3 级:GNINA rescore(前 5% → 前 0.5%)
./gnina -r receptor.pdb -l top5pct.sdf \
        --autobox_ligand ref.sdf --exhaustiveness 32 \
        --cnn_scoring rescore --seed 42 -o rescored.sdf

# 第 4 级:相互作用指纹核对(见 338)
#   ProLIF 检查是否复现共晶的关键相互作用

# 第 5 级:物理有效性检查
#   PoseBusters 检查键长键角、空间冲突

# 第 6 级:MM/GBSA 或短程 MD(前 100 → 前 30)

# 第 7 级:人工审查(见 217 的 mols2grid)
#   药化专家过一遍,挑最终的 20~30 个

# 第 8 级:采购可得性核查(见 232、233)

第 4、5、7 级最容易被跳过,但它们是命中率的关键。只按分数取 top 100 去买,很可能大部分是打分函数的漏洞。

CNN 重打分到底带来多少提升

  • 姿势预测确有改善:在 PDBbind 类基准上,CNN 重打分把 top-1 姿势 RMSD ≤ 2 Å 的成功率较纯 Vina 提高了可观幅度。这是它最扎实的收益。
  • 富集度提升因靶点而异:有些体系明显、有些几乎没有。不要预期它能解决打分函数的根本问题。
  • 存在训练分布偏置:CNN 在 PDBbind 等结构数据上训练,对新颖化学型或罕见靶点家族要谨慎。
  • 建议自行验证:如果手上有该靶点的已知活性/非活性分子,跑一遍看富集度是否真的改善,再决定是否纳入常规流程。
# 用已知活性分子验证富集效果
def enrichment_factor(scores, labels, frac=0.01):
    """EF@1%: 前 1% 中活性分子的富集倍数"""
    n_top = max(1, int(len(scores) * frac))
    order = np.argsort(scores)          # 分数越小越好时用升序
    top_hits = labels[order[:n_top]].sum()
    return (top_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))
# 后者明显更高才值得纳入流程

常见坑与提示

  • 分清三个分数:CNNscore 是姿势可信度,CNNaffinity 才是亲和力预测;
  • 默认用 --cnn_scoring rescore,性价比最好;
  • 分级筛选中不要跳过相互作用核对与人工审查
  • 用自家已知活性分子算 EF@1%,验证重打分是否真的有提升。

延伸资源