GNINA 在 smina(Vina 的分支)基础上加入了三维卷积神经网络重打分。它的上手门槛比原版 Vina 更低——能直接读 PDB/SDF、能自动定盒子,这两个改进消除了对接流程中最常见的错误来源。
安装与第一次运行
# 最简单:下载预编译的二进制
wget https://github.com/gnina/gnina/releases/latest/download/gnina
chmod +x gnina
./gnina --version
# 或用 Docker
docker pull gnina/gnina
# ---- 第一次运行:用参考配体自动定盒子 ----
./gnina \
-r receptor.pdb \
-l ligands.sdf \
--autobox_ligand crystal_ligand.sdf \
--autobox_add 4 \
-o docked.sdf \
--seed 42 \
--exhaustiveness 16 \
--num_modes 5
# 【相对 Vina 的三个便利】:
# 1) 直接读 PDB/SDF —— 【不需要转 PDBQT】
# → 省掉一个高频出错的环节(见 013)
# 2) --autobox_ligand 自动定盒子
# → 不用手工填坐标(另一个高频错误)
# 3) 输出直接是 SDF
# → 可以直接用 RDKit 读
#
# 【必须加 --seed】:搜索有随机性,不固定则结果不可复现
CNN 模式的选择
# --cnn_scoring 有四个选项
#
# none 不用 CNN(等价于 smina)
# rescore 【推荐】用经验打分搜索,CNN 只对最终姿势重打分
# refinement CNN 参与姿势精修(慢约 10 倍)
# all 全程 CNN(最慢)
#
# 【为什么推荐 rescore】:
# 搜索阶段需要评估大量候选姿势 → 用快的经验打分
# 排序阶段只有几个姿势 → 用准的 CNN
# → 【性价比最优】
./gnina -r receptor.pdb -l ligands.sdf \
--autobox_ligand ref.sdf \
--cnn_scoring rescore \
--cnn crossdock_default2018 \
-o out.sdf --seed 42
# 【CNN 模型的选择】:
# crossdock_default2018 默认,通用
# dense、general_default2018 其它训练配置
# → 可以用 --cnn <name> 指定
# → 【也可以用多个模型集成】:
# --cnn crossdock_default2018_ensemble
#
# GPU 加速:
# GNINA 自动使用 GPU(如果可用)
# → 【CNN 打分在 CPU 上会很慢】
三个分数的正确使用
| 分数 | 含义 | 用法 |
|---|---|---|
minimizedAffinity |
经验打分(kcal/mol) | 越负越好 |
CNNscore |
姿势可信度(0~1) | 不是亲和力! |
CNNaffinity |
CNN 预测的亲和力(pK 尺度) | 越大越好 |
# 【最常见的误用】:把 CNNscore 当作活性排序的依据
# 它回答的是「这个姿势像不像真实的结合姿势」,
# 不是「这个分子结合得多强」
#
# 【推荐的组合用法】:
from rdkit import Chem
import pandas as pd
rows = []
for mol in Chem.SDMolSupplier("docked.sdf"):
if mol is None:
continue
rows.append({
"name": mol.GetProp("_Name"),
"affinity": float(mol.GetProp("minimizedAffinity")),
"cnn_score": float(mol.GetProp("CNNscore")),
"cnn_affinity": float(mol.GetProp("CNNaffinity")),
"smiles": Chem.MolToSmiles(mol),
})
df = pd.DataFrame(rows)
# 每个分子只保留最好的姿势
df = df.sort_values("cnn_score", ascending=False)
df = df.drop_duplicates(subset=["name"], keep="first")
# 【第一步:用 CNNscore 过滤姿势不可信的】
df = df[df["cnn_score"] > 0.5]
# 【第二步:在剩余的里按亲和力排序】
df = df.sort_values("cnn_affinity", ascending=False)
# 【第三步:用配体高效性纠正大小偏倚】(见 095)
from rdkit.Chem import Descriptors
df["heavy_atoms"] = df["smiles"].apply(
lambda s: Descriptors.HeavyAtomCount(Chem.MolFromSmiles(s)))
df["LE"] = -df["affinity"] / df["heavy_atoms"]
print(df.head(20))
使用前的必做步骤:重对接验证
# 【这一步不做,所有结果都不可信】(见 096)
#
# 做法:把共晶配体取出,重新对接回去
./gnina -r receptor.pdb -l crystal_ligand.sdf \
--autobox_ligand crystal_ligand.sdf --autobox_add 4 \
--cnn_scoring rescore --seed 42 \
-o redock.sdf
# 计算 RMSD
from rdkit import Chem
from rdkit.Chem import rdMolAlign
ref = Chem.RemoveHs(Chem.SDMolSupplier("crystal_ligand.sdf")[0])
for i, mol in enumerate(Chem.SDMolSupplier("redock.sdf")):
if mol is None:
continue
rmsd = rdMolAlign.CalcRMS(Chem.RemoveHs(mol), ref)
print(f"姿势 {i+1}: RMSD = {rmsd:.2f} Å, "
f"CNNscore = {mol.GetProp('CNNscore')}")
# 【判据】:
# top-1 RMSD ≤ 2.0 Å → 通过
# 2~3 Å → 边缘,谨慎使用
# > 3 Å → 【失败,先排查再继续】
#
# 排查方向:
# - 结构准备(质子化态、缺失原子,见 105)
# - 盒子是否覆盖了整个口袋
# - 是否删掉了必要的辅因子/金属/关键水
# - 配体的立体化学与互变异构体
#
# 【建议】:对该靶点的 3~5 个共晶结构都做,
# 得到该体系上的成功率
常用参数速查
| 参数 | 作用 | 建议 |
|---|---|---|
--exhaustiveness |
搜索强度 | 筛选 8~16;精确 32~64 |
--num_modes |
输出姿势数 | 5~9 |
--seed |
随机种子 | 必须设置 |
--cnn_scoring |
CNN 模式 | rescore |
--autobox_ligand |
参考配体定盒子 | 强烈推荐 |
--autobox_add |
盒子外扩(Å) | 4~8 |
--minimize |
只做局部优化 | 修正外部姿势 |
--score_only |
只打分不搜索 | 评估外部姿势 |
--cpu |
CPU 线程数 | 按机器配置 |
结果的后处理
- 物理有效性检查:用 PoseBusters 检查键长键角与空间冲突(见 096《Binding Pose》);
- 相互作用核对:用 PLIP/ProLIF 检查是否复现了关键相互作用——这是独立于打分函数的判据(见 108《PLIP》、109《ProLIF》);
- 与其它方法交叉验证:与 Vina、DiffDock、Boltz 比较一致性(见 346《比较 DiffDock、Vina、GNINA》);
- 人工审查:用化学的眼睛看一遍——埋藏的未配对极性基团是明确的红旗(见 094《静电互补 Electrostatic Complementarity》);
- 不要跳过这些步骤:它们比调对接参数更能提高结果质量。
关键要点
- 直接读 PDB/SDF + 自动定盒子消除了对接中两个最常见的错误来源;
--cnn_scoring rescore是性价比最优的模式;- CNNscore 是姿势可信度,不是亲和力——先用它过滤,再按 CNNaffinity 排序;
- 重对接验证(RMSD ≤ 2 Å)是使用前的必做步骤,不通过则结果不可信。
延伸资源
- GNINA 论文:099《GNINA 论文精读》;GNINA 工具:183《GNINA》;实战:337《用 GNINA 做 Rescoring》;
- Vina 教程:017《AutoDock Vina 教程》;姿势验证:096《Binding Pose》;smina:100《Smina》。