基于电子等排体的3D分子生成模型 ShEPhERD - 评测

发布于:2025-04-14 ⋅ 阅读:(19) ⋅ 点赞:(0)

一、背景介绍

ShEPhERD 是一个由 MIT 开发的一个 3D 相互作用感知的 ligand-based的分子生成模型,以 arXiv 预印本的形式发表于 2024 年,被ICLR2025 会议接收。文章链接:https://openreview.net/pdf?id=KSLkFYHlYg

ShEPhERD 是一种基于去噪扩散概率模型 (DDPM) 的SE3等变扩散模型,其独特之处在于它主要联合建模 3D 分子图结构及其形状、静电势 (ESP) 表面和药效团等相互作用特征,而无需显式编码蛋白质口袋的信息。该模型能够学习和生成具有特定相互作用特征的分子,可以满足骨架跃迁、电子等排体合并的分子设计任务。此外,作者还设计了一个3D相似性分数以评估生成的分子满足特定相互作用模式的情况。

二、模型简介

设计新分子以通过与其环境的物理化学相互作用实现特定功能,是化学科学领域的核心课题。例如,在早期药物发现中,常需调控小分子配体的三维形状、静电势表面和非共价相互作用,以增强其对蛋白质靶标的选择性结合能力。均相催化剂设计则要求开发金属有机、有机甚至多肽类催化剂,通过特定非共价作用稳定反应过渡态。超分子化学同样通过优化主客体相互作用,广泛应用于光响应材料设计、生物医学及结构导向的沸石合成等领域。

在基于配体的药物设计中,设计具有靶向三维相互作用的分子面临诸多挑战。药物化学中的生物等排骨架跃迁旨在替换大分子中的子结构,同时保持生物活性。替换后的骨架通常保留关键的生化特征,如静电性质或药效团,这些特征涵盖非定向(疏水、离子)和定向(氢键供体/受体、芳香 π-π 堆积、卤键)非共价相互作用。当跃迁延伸至整个配体时,"配体跃迁"技术可帮助识别复杂天然产物的可合成类似物,模仿其三维生化作用模式。在命中化合物扩展中,该技术还可通过提出从拓扑相似的 "me-too" 化合物到全新化学型的替代活性分子,实现已知生物活性分子的多样化。值得注意的是,配体跃迁无需蛋白质靶标的结构信息。最新进展中,生物等排骨架跃迁已超越单一分子改造:Wills 团队(2024)通过"生物等排片段融合"技术,用单一配体替代多个独立结合蛋白质的片段,整合其三维结合特征。

ShEPhERD 聚焦生物等排药物设计中的三大经典任务:三维相似性约束的配体跃迁、蛋白质不可知的生物活性命中多样化及生物等排片段融合(下图所示)。这些任务的共同目标是发现分子图结构迥异,但三维分子间相互作用特征高度相似的新分子。传统方法通过三维相似性评分函数虚拟筛选化学库,查询分子与参考分子在形状、静电势和/或药效团的相似性。然而,基于相似性的虚拟筛选存在显著局限:无法突破已知化学库的边界、非定向搜索效率低下,且对构象柔性分子的多重几何形态评分耗时较长。

为此,作者开发了通用型生成建模框架,实现高效的三维生物等排分子设计。研究动机基于以下发现:(1)配体药物发现需要设计具有特定三维相互作用的新分子;(2)Harris 等(2023)发现,基于结构的药物设计三维生成模型难以生成与蛋白质形成生化相互作用(如氢键)的配体,提示需要建立新的分子相互作用建模策略;(3)大量非药物领域的化学设计场景需要调控小分子物理化学相互作用,但相关数据稀缺,亟需零样本/少样本生成方法。

作者提出 ShEPhERD 三维生成模型,该模型在无环境约束下学习小分子三维化学结构与其形状、静电势及药效团(统称"相互作用特征")的关联关系。创新点包括:

作者提出 ShEPhERD 三维生成模型,该模型在无环境约束下学习小分子三维化学结构与其形状、静电势及药效团(统称"相互作用特征")的关联关系。创新点包括:

(1)构建基于点云的分子形状、静电势表面及药效团表征体系,适配对称保持的 SE(3) 等变扩散模型;
(2)建立联合去噪扩散概率模型(DDPM),同步学习三维分子(原子类型、键类型、坐标)及其形状、静电势表面、药效团的联合分布。除形状与静电的带属性点云扩散/去噪外,通过单位球面矢量扩散/去噪实现药效团方向性建模,采用图像修复技术实现特定相互作用条件下的采样;
借鉴虚拟筛选思路,构建形状、静电势及药效团相似性函数,评估生成分子与相互作用特征的自洽性,条件生成分子与目标特征的相似性。实验表明,ShEPhERD 可生成与目标特征具有显著增强相互作用相似性的多样化三维分子结构,经半经验 DFT 几何弛豫后仍保持优势;(注:条件生成分子与目标特征的相似性在此文档中不做介绍)
(3)在类药数据集训练后,计算机模拟验证 ShEPhERD 可轻松实现天然产物配体跃迁设计、保持蛋白结合模式的生物活性分子多样化,以及生物等排片段融合等任务。

2.1 模型结构简介

ShEPhERD 中,描述一个分子使用了3D分子图、表面形状、静电势表面、药效团四个维度进行描述。3D分子图 (x1) 的节点是原子类型和电荷,边为化学键。表面形状 (x2) 是根据小分子的溶剂化可接触表面采样得到的点云。表面形状的点的库伦势作为静电势表面 (x3)。药效团 (x4) 由药效团的坐标、类型以及药效团的方向组成,带方向的药效团包含 HBD,HBA, 芳香环、halogen-carbon bonds,不带方向的药效团包含疏水物、阴离子、阳离子和锌结合剂。提取药效团的方法是基于 SMART 模式从小分子中直接提取。

ShEPhERD 的模型结构如下图。ShEPhERD 使用的是DDPM的模型架构,在上述的3D分子图(x1)、表面形状(x2)、静电势表面(x3)、药效团(x4)上进行联合(同时)添加噪音/去噪(注意,药效团、静电势、表面并不是像蛋白口袋的条件,而是类似原子节点等一起被添加噪音/去噪)。

使用一个神经网络预测的是真实的噪音(注意,3D分子图、表面形状、静电势表面、药效团不同部分添加的噪音有点不同),这一点和difflinker类似。神经网络由 embedding 模块、联合分布模块、去噪模块组成。在训练的时候, ShEPhERD 允许只学习特定的数据分布(即,表面形状、静电势表面、药效团作为条件对分子图进行学习;或者以表面形状、静电势表面作为条件,对药效团和分子图进行学习;也可以对四种数据同时进行学习)。

在采样过程中,也是从各向同性的噪音开始采样。如果是条件采样,那么使用inpainting的策略,即先将药效团、静电势表面、形状表面的不同t下的状态计算出来,然后在去噪过程中,逐步替代。这一过程中,需要指定原子数和药效团数。

作者从 GDB17中提取了药相关的2.8M分子(ShEPhERD-GDB17 数据集,分子的非H原子数小于17)以及包含1.6M分子的MOSES(ShEPhERD-MOSES-aq 数据集,分子的非H原子数小于27)作为训练集。

2.2 模型性能

但是作者没有详细与其他模型进行对比,只有案例,如下。

在无条件生成时:

在条件生成时:

三、ShEPhERD 评测

3.1 文件结构

复制代码项目:

git clone https://github.com/coleygroup/shepherd.git

本代码库包含用于训练和从 ShEPhERD 扩散生成模型进行采样分子的代码。该模型学习三维分子结构及其形状、静电性质和药效团的联合分布。在推理阶段,ShEPhERD 可用于生成具有目标三维相互作用特征的三维构象新分子。

项目的文件结构如下所示:

.
├── shepherd_score_utils/                       # dependencies from shepherd-score Github repository
├── shepherd_chkpts/                            # trained model checkpoints (from pytorch lightning)
├── paper_experiments/                          # inference scripts for all experiments in preprint
├── samples/                                    # empty dir to hold outputs from paper_experiments/*.py
├── jobs/                                       # empty dir to hold ouputs from train.py
├── conformers/                                 # conditional target structures for experiments, and (sample) training data
├── parameters/                                 # hyperparameter specifications for all models in preprint
├── model/                                      # model architecture
│   │   ├── equiformer_operations.py            # select E3NN operations from (original) Equiformer
│   │   ├── equiformer_v2_encoder.py            # slightly customized Equiformer-V2 module
│   │   └── model.py                            # module definitions and forward passes
│   ├── utils/                                  # misc. functions for forward passes
│   ├── egnn/                                   # customized re-implementation of EGNN
│   └── equiformer_v2/                          # clone of equiformer_v2 codebase, with slight modifications for shepherd
├── train.py                                    # main training script
├── lightning_module.py                         # pytorch-lightning modules and training pipeline
├── datasets.py                                 # torch_geometric dataset class (for training)
├── inference.py                                # inference functions; see Jupyter notebooks for example uses
├── RUNME_conditional_generation_MOSESaq.ipynb  # Jupyter notebook for conditional generation, using MOSES_aq P(x1,x3,x4) model
├── RUNME_unconditional_generation.ipynb        # Jupyter notebook for unconditional generation, for all models
├── environment.yml                             # conda environment requirements
└── README.md

3.2 安装环境

根据说明文档创建 ShEPhERD 项目运行环境,下面环境安装流程已经经过作者的测试。(注:此环境可能有些问题,需要配合合适的 CUDA,驱动,pytorch 版本,整个安装过程比较困难,可以后台联系也可以下载博客中的资源文档)。逐行运行如下命令。

conda create --name ShEPhERD python=3.8.13 -y
conda activate ShEPhERD
conda install merv::envvar-pythonnousersite-true
conda deactivate

conda activate ShEPhERD
# 添加 conda-forge 频道到软件源列表
conda config --append channels conda-forge 

# 清理 pip 缓存
pip cache purge
pip3 cache purge

# 指定临时文件存储路径
export TMPDIR='/var/tmp'

conda install pytorch==1.12.1 torchvision==0.13.1 torchaudio==0.12.1 cudatoolkit==11.3.1 -c pytorch
conda install pyg=2.2.0 -c pyg
pip install jupyterlab

pip install pip==24.0
pip install pytorch-lightning==1.6.3
pip install setuptools==59.5.0

pip install rdkit
conda install xtb
pip install open3d
conda install h5py

pip install numpy --upgrade

3.3 下载数据集

项目的 conformers/ 文件夹包含作者文章中实验用到的天然产物、PDB 配体和片段的 3D 结构。该目录还包含在条件生成评估中使用的来自 GDB-17 的 100 个测试集结构。

conformers/gdb/example_molblock_charges.pkl 包含来自 ShEPhERD-GDB-17 训练数据集的示例训练数据。conformers/moses_aq/example_molblock_charges.pkl 包含来自ShEPhERD-MOSES_aq 训练数据集的示例训练数据。

项目提供完整训练数据集(总文件大小约为 14 GB),下载链接为:https://www.dropbox.com/scl/fo/rgn33g9kwthnjt27bsc3m/ADGt-CplyEXSU7u5MKc0aTo?rlkey=fhi74vkktpoj1irl84ehnw95h&e=1&st=wn46d6o2&dl=0 。网页如下所示:

数据下载后,上传到 ./shepherd_data 文件夹中。

3.4 模型推理

项目提供两个运行推理的 Jupyter notebook 文件:RUNME_unconditional_generation_MOSESaq.ipynb 和RUNME_conditional_generation_MOSESaq.ipynb 。可以按照 jupyter 的操作说明梳理模型推理流程。

paper_experiments/ 目录包含预印本论文中用于运行实验的脚本。这些脚本在被命令行调用之前,需要先复制到父目录(与 README 文件同级的目录)。部分脚本(如paper_experiments/run_inference_*_unconditional_*_.py)需要接受一些额外的命令行参数,这些参数可通过对应脚本中的 argparse 命令查看具体说明(在相应脚本中有详细说明)。

当前推理脚本支持通过"部分修复(partial inpainting)"技术,实现包含目标药效团超集的分子条件生成。

3.4.1 无条件生成场景

根据项目提供的 RUNME_unconditional_generation.ipynb 文件查看无条件场景的生成流程。在无条件生成场景下,从高斯噪音中采样 T 时刻的状态,进行 T 步的去噪,生成 0 时刻的状态,最终获得原子类型、电荷、共价键及药效团信息等。

导入依赖库

import open3d 
from shepherd_score_utils.generate_point_cloud import (
    get_atom_coords, 
    get_atomic_vdw_radii, 
    get_molecular_surface,
    get_electrostatics,
    get_electrostatics_given_point_charges,
)
from shepherd_score_utils.pharm_utils.pharmacophore import get_pharmacophores
from shepherd_score_utils.conformer_generation import update_mol_coordinates

print('importing rdkit')
import rdkit
from rdkit.Chem import rdDetermineBonds

import numpy as np
import matplotlib.pyplot as plt

print('importing torch')
import torch
import torch_geometric
from torch_geometric.nn import radius_graph
import torch_scatter

import pickle
from copy import deepcopy
import os
import multiprocessing
from tqdm import tqdm

import sys
sys.path.insert(-1, "model/")
sys.path.insert(-1, "model/equiformer_v2")

print('importing lightning')
import pytorch_lightning as pl
from pytorch_lightning.callbacks import ModelCheckpoint
from pytorch_lightning.loggers import CSVLogger

from lightning_module import LightningModule
from datasets import HeteroDataset

import importlib

from inference import *

选择在 ShEPhERD-GDB-17 数据集上训练的 P(x1,x2), P(x1,x3), P(x1,x4) 联合分布模型中的一个。x1 定义的是有机分子图,包含原子类型、电荷、坐标及共价键信息。x2 定义的是点云形状信息。x3 定义的是静电势表面信息。x4 定义的是药效团特征信息。

# pick one
#chkpt = 'shepherd_chkpts/x1x2_diffusion_gdb17_20240824_submission.ckpt'
#chkpt = 'shepherd_chkpts/x1x3_diffusion_gdb17_20240824_submission.ckpt'
chkpt = 'shepherd_chkpts/x1x4_diffusion_gdb17_20240824_submission.ckpt'

在这里,选择文章中评估使用的 checkpoint (shepherd_chkpts/x1x3x4_diffusion_mosesaq_20240824_submission.ckpt)。

chkpt = 'shepherd_chkpts/x1x3x4_diffusion_mosesaq_20240824_submission.ckpt' # checkpoint used for evaluations in preprint

加载模型到可用的设备上,并手动设置模型内部设备属性:

device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')
model_pl = LightningModule.load_from_checkpoint(chkpt)
params = model_pl.params
model_pl.to(device)
model_pl.model.device = device

设置 batch_size(批次大小) 和 n_atoms (原子数)、num_pharmacophores (药效团数目)。如果不建模药效团(x4)的模型,就设置成 5,起个占位的作用。batch_size 原本为 10 ,但超过了显存,所以改成了 5 。

batch_size = 5
n_atoms = 60
num_pharmacophores = 10 # set to 5 (just a dummy value) if using a model that does not model x4

设置破坏对称性的噪声注入配置。

# use to break symmetry during unconditional generation
T = params['noise_schedules']['x1']['T']  # T == 400
inject_noise_at_ts = list(np.arange(130, 80, -1))  # [150](实际生成空列表,需修正为 np.arange(130, 79, -1))
inject_noise_scales = [1.0] * len(inject_noise_at_ts)
harmonize = True
harmonize_ts = [80]
harmonize_jumps = [20]

分子生成函数调用代码具体如下:

generated_samples = inference_sample(
    model_pl,
    batch_size=batch_size,
    N_x1=n_atoms,
    N_x4=num_pharmacophores, 
    unconditional=True,
    prior_noise_scale=1.0,
    denoising_noise_scale=1.0,
    inject_noise_at_ts=inject_noise_at_ts,
    inject_noise_scales=inject_noise_scales, 
    harmonize=harmonize,
    harmonize_ts=harmonize_ts,
    harmonize_jumps=harmonize_jumps,
)

如果遇到一下报错,那就是遇到上文提到的CUDA/pytorch/驱动版本不兼容了:

RuntimeError: CUDA error: CUBLAS_STATUS_INVALID_VALUE when calling `cublasSgemm( handle, opa, opb, m, n, k, &alpha, a, lda, b, ldb, &beta, c, ldc)`

batch_size 设置为 5,显存占用大约为 22 GB,采样过程耗时约 2 分钟。采样生成的结构信息保存在 generated_samples 变量中。generated_samples 的类型是 List[Dict] ,列表中的每个字典包含四个键 x1, x2, x3, x4,分别表示分子结构、表面几何、表面电荷/药效团特征。

每个样本返回结构信息如下所示:

'x1': {
          'atoms': np.ndarray (N_x1,) of ints for atomic numbers.
          'bonds': np.ndarray of bond types between every atom pair.
          'positions': np.ndarray (N_x1, 3) Coordinates of atoms.
      },
'x2': {
          'positions': np.ndarray (75, 3) Coordinates of surface points.
      },
'x3': {
          'charges': np.ndarray (75, 3) ESP at surface points.
          'positions': np.ndarray (75, 3) Coordinates of surface points.
      },
'x4': {
          'types': np.ndarray (N_x4,) of ints for pharmacophore types.
          'positions': np.ndarray (N_x4, 3) Coordinates of pharmacophores.
          'directions': np.ndarray (N_x4, 3) Unit vectors of pharmacophores.
      }

脚本最后还简单地可视化生成的分子。总的来说,就是处理了生成的分子,进行验证,并显示有效的分子结构。下面是具体的工作流程。

遍历生成的 5 个分子,sample_dict 包含每个分子的详细数据。

for b, sample_dict in enumerate(generated_samples):

构建 XYZ 字符串。从每个分子的字典中提取原子信息(原子符号和位置),并构建一个符合 XYZ 格式的字符串。XYZ 格式是一种常见的原子坐标表示方式。

xyz = '' 
x_ = sample_dict['x1']['atoms']
pos_ = sample_dict['x1']['positions']
xyz += f'{len(x_)}\n{b+1}\n'
for a in range(len(x_)):
    atomic_number_ = int(x_[a])
    position_ = pos_[a]
    xyz += f'{rdkit.Chem.Atom(atomic_number_).GetSymbol()} {str(position_[0].round(3))} {str(position_[1].round(3))} {str(position_[2].round(3))}\n'
xyz += '\n'

从 XYZ 字符串创建分子。尝试通过 RDKit 的 MolFromXYZBlock 方法,从构建的 XYZ 字符串中创建一个分子对象,含电荷。如果创建失败(例如格式错误或无效的原子坐标),它会捕捉到异常并跳过当前样本。

try:
    mol_ = rdkit.Chem.MolFromXYZBlock(xyz)
except Exception as e:
    mol_ = None
    print(f'invalid molecule: {e}')
    continue

for c in [0, 1, -1, 2, -2]:
    mol__ = deepcopy(mol_)
    try:
        rdkit.Chem.rdDetermineBonds.DetermineBonds(mol__, charge=c, embedChiral=True)
    except:
        continue
    if mol__ is not None:
        print(c)
        break 

验证分子,跳过不正常的分子。

try:
    assert sum([a.GetNumRadicalElectrons() for a in mol_.GetAtoms()]) == 0, 'has radical electrons'
    mol_.UpdatePropertyCache()
    rdkit.Chem.GetSymmSSSR(mol_)
except Exception as e:
    mol_ = None
    print(f'invalid molecule: {e}')
    continue

显示分子。代码将有效的分子转换为 SMILES(简化分子输入线性表达式)字符串,并显示该分子。

display(rdkit.Chem.MolFromSmiles(rdkit.Chem.MolToSmiles(mol_)))

在生成的5个分子中包含合法有效的 2 个分子结构如下:

有效分子对应的3D结构如下:

 

3.4.2 条件生成场景

根据项目提供的 RUNME_conditional_generation_MOSESaq.ipynb 文件查看条件场景的生成流程。在条件生成场景下,首先对目标相互作用特征(形状、电势和药效团)进行前向噪声化,得到 t 时刻的状态。在反向去噪过程中,将 ShEPhERD 去噪的状态替换为噪声化的目标。

导入依赖库。

import open3d 
from shepherd_score_utils.generate_point_cloud import (
    get_atom_coords, 
    get_atomic_vdw_radii, 
    get_molecular_surface,
    get_electrostatics,
    get_electrostatics_given_point_charges,
)
from shepherd_score_utils.pharm_utils.pharmacophore import get_pharmacophores
from shepherd_score_utils.conformer_generation import update_mol_coordinates

print('importing rdkit')
import rdkit
from rdkit.Chem import rdDetermineBonds

import numpy as np
import matplotlib.pyplot as plt

print('importing torch')
import torch
import torch_geometric
from torch_geometric.nn import radius_graph
import torch_scatter

import pickle
from copy import deepcopy
import os
import multiprocessing
from tqdm import tqdm

import sys
sys.path.insert(-1, "model/")
sys.path.insert(-1, "model/equiformer_v2")

print('importing lightning')
import pytorch_lightning as pl
from pytorch_lightning.callbacks import ModelCheckpoint
from pytorch_lightning.loggers import CSVLogger

from lightning_module import LightningModule
from datasets import HeteroDataset

import importlib

from inference import *

选择文章中用来评估的 checkpoint 和基础配置:

chkpt = 'shepherd_chkpts/x1x3x4_diffusion_mosesaq_20240824_submission.ckpt' # checkpoint used for evaluations in preprint

device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')

model_pl = LightningModule.load_from_checkpoint(chkpt)
params = model_pl.params
model_pl.to(device)
model_pl.model.device = device

该场景下的生成测试基于项目提供的 conformers/np/molblock_charges_NPs.pkl 文件,该文件里包括 3 个分子三维结构和电荷信息。有以下方式可以提取,静电势表面、表面、以及药效团:

1. 基于天然产物的相互作用特征进行条件化

接下来,从项目提供的分子信息文件 conformers/np/molblock_charges_NPs.pkl 中加载分子数据,然后进行处理和计算,以便提取分子特征,比如电荷、分子表面、药效团(pharmacophores)和电场等信息。

加载数据 (pickle 读取)。打开一个名为 molblock_charges_NPs.pkl 的文件,并将其中的数据加载到 molblocks_and_charges 变量中。该文件保存了分子结构和电荷数据,并且可以存储多个分子(该文件包含 3 个分子)。molblocks_and_charges 是一个列表,其中每个元素包含分子的 MolBlock 数据和电荷数据。

mol = rdkit.Chem.MolFromMolBlock(molblocks_and_charges[index][0], removeHs=False)  # target natural product
charges = np.array(molblocks_and_charges[index][1])  # xTB partial charges in implicit water
with open('conformers/np/molblock_charges_NPs.pkl', 'rb') as f:
    molblocks_and_charges = pickle.load(f)  # len(molblocks_and_charges) == 3

选择目标天然产物。这里,index 选择了第一个天然产物(分子)。index 也可以是 1 或 2,表示选择不同的分子。

index = 0  # 0, 1, 2

从 MolBlock 生成分子对象 (rdkit)。使用 rdkit.Chem.MolFromMolBlock 从 MolBlock 格式的数据中构建一个分子对象 (mol)。charges 是从 molblocks_and_charges 中提取的电荷数据,转换为 NumPy 数组,表示的是在隐式水环境下的部分电荷。

mol = rdkit.Chem.MolFromMolBlock(molblocks_and_charges[index][0], removeHs=False)  # target natural product
charges = np.array(molblocks_and_charges[index][1])  # xTB partial charges in implicit water

选择的天然产物分子的结构如下:

提取分子坐标并进行中心化。mol.GetConformer().GetPositions() 获取分子的三维坐标。mol_coordinates - np.mean(mol_coordinates, axis=0) 将分子坐标进行中心化,即将分子坐标的质心移到原点(这样可以减少坐标的偏移对后续计算的影响)。update_mol_coordinates 用来更新分子对象的坐标。

mol_coordinates = np.array(mol.GetConformer().GetPositions())
mol_coordinates = mol_coordinates - np.mean(mol_coordinates, axis=0)
mol = update_mol_coordinates(mol, mol_coordinates)

获取分子相关的条件性目标。centers 是分子中各个原子的三维坐标。get_atomic_vdw_radii(mol) 获取分子中各个原子的范德华半径(Van der Waals radii)。get_molecular_surface 计算分子的表面,使用了范德华半径、表面采样点的数量、探针半径等参数。

centers = mol.GetConformer().GetPositions()
radii = get_atomic_vdw_radii(mol)
surface = get_molecular_surface(
    centers, 
    radii, 
    params['dataset']['x3']['num_points'], 
    probe_radius=params['dataset']['probe_radius'],
    num_samples_per_atom=20,
)

获取药效团特征。get_pharmacophores 函数用于从分子中提取药效团信息。multi_vector 和 check_access 是通过参数 params 提供的,用来控制药效团计算的不同细节。

pharm_types, pharm_pos, pharm_direction = get_pharmacophores(
    mol,
    multi_vector=params['dataset']['x4']['multivectors'],
    check_access=params['dataset']['x4']['check_accessibility'],
)

计算电场(静电势)。get_electrostatics_given_point_charges 计算给定电荷分布下的静电场(电势),这里使用了 charges(电荷)、centers(原子坐标)和 surface(分子表面)来计算。

electrostatics = get_electrostatics_given_point_charges(
    charges, centers, surface,
)

2. 基于 PDB 配体的相互作用特征进行条件化

这段代码与前一段代码非常相似,主要区别在于它加载的是 PDB(蛋白质数据库)格式的分子数据,而不是之前的天然产物的 MolBlock 数据。下面会逐步解释这段代码。

加载 PDB 数据 (pickle 读取)。打开一个名为 molblock_charges_pdb_lowestenergy.pkl 的文件,并将其中的数据加载到 molblocks_and_charges 变量中。这个文件包含了 PDB 配体(ligand)及其电荷信息。

with open('conformers/pdb/molblock_charges_pdb_lowestenergy.pkl', 'rb') as f:
    molblocks_and_charges = pickle.load(f)

选择目标 PDB 配体。在这里,index 被设定为 6,表示从 molblocks_and_charges 中选择第七个配体。index 可以是 0 到 6 之间的任何值,表示选择不同的配体。随后,提取电荷、并计算表面、静电势表面:

index = 6 

mol = rdkit.Chem.MolFromMolBlock(molblocks_and_charges[index][0], removeHs=False)  # target natural product
charges = np.array(molblocks_and_charges[index][1])  # xTB partial charges in implicit water

分子结构如下:

提取分子坐标并进行中心化。mol.GetConformer().GetPositions() 获取分子的三维坐标。通过 mol_coordinates - np.mean(mol_coordinates, axis=0) 将分子坐标进行中心化,使得分子的质心处于原点。update_mol_coordinates(mol, mol_coordinates) 更新分子对象的坐标。

mol_coordinates = np.array(mol.GetConformer().GetPositions())
mol_coordinates = mol_coordinates - np.mean(mol_coordinates, axis=0)
mol = update_mol_coordinates(mol, mol_coordinates)

获取分子相关的条件性目标。centers 变量保存了分子中各个原子的三维坐标。get_atomic_vdw_radii(mol) 获取分子中各个原子的范德华半径(Van der Waals radii)。get_molecular_surface 计算分子的表面,并根据分子的原子坐标、范德华半径以及其他参数(如探针半径、采样点数量等)来确定表面。

centers = mol.GetConformer().GetPositions()
radii = get_atomic_vdw_radii(mol)
surface = get_molecular_surface(
    centers, 
    radii, 
    params['dataset']['x3']['num_points'], 
    probe_radius=params['dataset']['probe_radius'],
    num_samples_per_atom=20,
)

获取药效团特征。get_pharmacophores 提取分子的药效团特征。multi_vector 和 check_access 是从 params 中获取的参数,控制药效团计算的细节,如药效团的类型和分子是否可接近。

pharm_types, pharm_pos, pharm_direction = get_pharmacophores(
    mol,
    multi_vector=params['dataset']['x4']['multivectors'],
    check_access=params['dataset']['x4']['check_accessibility'],
)

electrostatics = get_electrostatics_given_point_charges(
    charges, centers, surface,
)

由于篇幅原因,另一种方式就略过了。

3.4.3 通过 inpainting 进行条件生成

前面介绍了不同的应用场景怎么来获取相互作用特征,接着介绍根据这些特征来进行条件生成的流程。这部分调用了一个函数 inference_sample() 来生成分子样本或进行相关的推理(inference)任务。具体来说,函数的参数配置了不同的生成选项,其中包含了与分子坐标、药效团、表面、电荷等多个特征相关的设置。下面是对每个部分的详细解释。

设置生成分子的原子数目,batch_size 以及药效团数目

n_atoms = 70
batch_size = 2 # 24 GB
num_pharmacophores = len(pharm_types) # must equal pharm_pos.shape[0] if inpainting

调用 inference_sample 函数进行推理生成分子。调用 inference_sample 生成分子样本的代码如下:

generated_samples = inference_sample(
    model_pl,
    batch_size = batch_size,
    
    N_x1 = n_atoms,
    N_x4 = num_pharmacophores,
    
    unconditional = False,
    
    prior_noise_scale = 1.0,
    denoising_noise_scale = 1.0,
    
    inject_noise_at_ts = [],
    inject_noise_scales = [],    
    
    harmonize = False,
    harmonize_ts = [],
    harmonize_jumps = [],
    
    
    # all the below options are only relevant if unconditional is False
    
    inpaint_x2_pos = False, # note that x2 is implicitly modeled via x3
    
    inpaint_x3_pos = True,
    inpaint_x3_x = True,
    
    inpaint_x4_pos = True,
    inpaint_x4_direction = True,
    inpaint_x4_type = True,
    
    stop_inpainting_at_time_x2 = 0.0,
    add_noise_to_inpainted_x2_pos = 0.0,
    
    stop_inpainting_at_time_x3 = 0.0,
    add_noise_to_inpainted_x3_pos = 0.0,
    add_noise_to_inpainted_x3_x = 0.0,
    
    stop_inpainting_at_time_x4 = 0.0,
    add_noise_to_inpainted_x4_pos = 0.0,
    add_noise_to_inpainted_x4_direction = 0.0,
    add_noise_to_inpainted_x4_type = 0.0,
    
    # these are the inpainting targets
    center_of_mass = np.zeros(3), # center of mass of x1; already centered to zero above
    surface = surface,
    electrostatics = electrostatics,
    pharm_types = pharm_types,
    pharm_pos = pharm_pos,
    pharm_direction = pharm_direction,
)

batch_size 设置为 2,显存占用大约为 10 GB,采样过程耗时约 2 分钟。采样生成的结构信息保存在 generated_samples 变量中。generated_samples 的类型是 List[Dict] ,列表中的每个字典包含四个键 x1, x2, x3, x4,分别表示分子结构、表面几何、表面电荷/药效团特征。

类似于无条件生成场景,脚本最后也简单地可视化生成的分子。总的来说,就是处理了生成的分子,进行验证,并显示有效的分子结构。

# quick visualization of generated samples
# full analyses, including extensive validity checks, can be performed by following https://github.com/coleygroup/shepherd-score

for b,sample_dict in enumerate(generated_samples):
    
    xyz = '' 
    
    x_ = sample_dict['x1']['atoms']
    pos_ = sample_dict['x1']['positions']
    
    xyz += f'{len(x_)}\n{b+1}\n'
    for a in range(len(x_)):
        atomic_number_ = int(x_[a])
        position_ = pos_[a]
        
        xyz+= f'{rdkit.Chem.Atom(atomic_number_).GetSymbol()} {str(position_[0].round(3))} {str(position_[1].round(3))} {str(position_[2].round(3))}\n'
    xyz+= '\n'
    
    try:
        mol_ = rdkit.Chem.MolFromXYZBlock(xyz)
    except Exception as e:
        mol_ = None
        print(f'invalid molecule: {e}')
        continue
    
    try:
        for c in [0, 1, -1, 2, -2]:
            mol__ = deepcopy(mol_)
            try:
                rdkit.Chem.rdDetermineBonds.DetermineBonds(mol__, charge = c, embedChiral = True)
            except:
                continue
            if mol__ is not None:
                print(c)
                break 
    except Exception as e:
        mol_ = None
        print(f'invalid molecule: {e}')
        continue
    
    mol_ = mol__
    try:
        assert sum([a.GetNumRadicalElectrons() for a in mol_.GetAtoms()]) == 0, 'has radical electrons'
        mol_.UpdatePropertyCache()
        rdkit.Chem.GetSymmSSSR(mol_)
        
    except Exception as e:
        mol_ = None
        print(f'invalid molecule: {e}')
        continue

    display(rdkit.Chem.MolFromSmiles(rdkit.Chem.MolToSmiles(mol_)))
    
    continue

生成的两个分子都是有效分子,结构如下所示:

对应的两个有效分子的 3D 结构如下:

3.5 自定义案例(3WZE)条件分子生成

作为测试,是我们的老演员,3WZE。不同的是,我们选择 3WZE 同一蛋白的不同小分子体系 2QU5 也作为输入。我们的计划是,使用3WZE小分子的药效团、2QU5小分子的表面和电荷,进行分子条件生成。

3WZ E和 2QU5的align结果见下图,可以看到小分子的叠合非常好,分子结构也非常相似,只有骨架中间部分的差异。

分子生成过程详见 RUNME_conditional_generation_3WZE.ipynb 文件,这里就略过。在3090显卡上,生成120个分子耗时1个小时,实际上有效的分子只有82个。

随后,使用类似 DrugHIVE 类似的qvina对接流程,将生成的小分子对接到 3WZE口袋中。具体过程参考 ./3WZE/get_qvina, 这里不再赘述。

以下是生成分子按照对接打分由高到低的结果。大部分的分子是非常类药的,结构也比较合理:

ShEPhERD_3WZE_Results

Top 3的分子结构如下,对接打分分别为:-11.8,-11.1, 11.0。(但是第一个分子结构挺奇怪的,可能是因为化学键重建的时候,原子距离有点异常导致的)

从上述结果来看,ShEPhERD 模型生成的分子还是不错的,值得一试。


网站公告

今日签到

点亮在社区的每一天
去签到