SQUID - 形状条件下的基于分子片段的3D分子生成等变模型 评测

SQUID 是一个形状条件下基于片段的3D分子生成模型,给一个3D参考分子,SQUID 可以根据参考分子的形状,基于片段库,生成与参考分子形状非常相似的分子。

SQUID 模型来自于 ICLR 2023 文章(2022年10月6日提交),对应的arix文献为《Equivariant Shape-Conditioned Generation of 3D Molecules for Ligand-Based Drug Design》, 文章链接为:https://arxiv.org/abs/2210.04893

SQUID是: Shape-Conditioned Equivariant Generator for Drug-Like Molecules的缩写(神奇的脑回路),是麻省理工 Keir Adams 和 Connor W. Coley的工作,似乎两人研究领域都是AI4science。

一、模型介绍

1.1 基本介绍

基于形状的虚拟筛选在基于配体的药物设计中至关重要,主要目的是识别与已知配体具有相似 3D 形状的分子。传统方法(例如:openeye 的 ROCS)依赖于枚举的化学库,这限制了新化学空间的探索。

基于形状的 3D 分子生成模型,需要完成任意构象的成对比较、形状相似性度量、2D 图拓扑对 3D 形状的依赖以及可旋转键的灵活性处理等任务。为此,作者开发了SQUID模型。SQUID 是在 形状条件下 3D 分子生成模型,可以用于在形状条件下的化学空间探索任务。

SQUID 模型将分子形状使用等变点云网络( equivariant point cloud networks)编码成旋转不变形的的等变特征;使用图神经网路将2D的化学结构变分编码为化学的不变特征;然后,将化学的不变特征和形状的等变特征合并在一起,生成多样性的 3D 分子。

在 3D 分子的生成过程中,SQUID 使用基于片段的策略,固定局部键长和角度(人工分子),优先考虑可旋转键。大大简化了 3D 坐标生成,可以生成类药物分子,同时保持化学和几何有效性。SQUID 设计一个可旋转的键评分网络,学习局部键旋转如何影响整体形状,使我们的解码器能够生成最适合目标形状的 3D 构象。

SQUID 3D分子生成过程示意图:

SQUID 模型可以解开控制 2D 化学特性和 3D 形状的潜在变量,从而能够零样本生成与任何编码目标具有相似形状的拓扑不同分子。SQUID 模型既预测分子图,也进行分子的坐标预测,但是使用基于片段的策略,而且是在形状条件下,分子从无到有的生成。简单的来说,在片段库内,根据参考分子的3D形状,生成形状极为相似的分子。

在 github 中,作者给了一段视频,看起来模型非常有意思,可以在生成形状非常像的小分子:

SQUID_demo

1.2 模型框架介绍

SQUID 使用编码器-解码器架构,将 3D 分子形状和化学身份进行编码,并生成与目标形状相匹配的新分子。在分子图的表示方面,节点(原子)特征有:原子质量、原子编号的独热编码、原子电荷和芳香性的独热编码、单键、双键、芳香键和三键的数量独热编码;边的特征为键序的独热编码。在形状表示方面,通过 3D 分子中从每个原子中心的高斯分布中采样点云来表示分子的 3D 形状。

SQUID的编码器和解码器结构如下图:

特征的编码分为三块:(1)片段编码器:使用图神经网络(GNN)对片段进行编码,得到全局的片段嵌入。(2)形状编码器:使用 VN-DGCNN (Vector Neurons (VN)-based equivariant point cloud encoder VN-DGCNN)将分子的点云编码为等变的每点向量特征,并进行局部均值池化,得到每个原子的等变表示。(3)使用GNN将分子图编码为学习到的原子嵌入,同时条件化在不变的形状特征上。

在编码完成后,混合形状和变分特征,将变分原子特征通过线性变换与等变形状特征混合,并通过VN-MLP进行处理,生成全局的等变和不变表示。

在分子生成的过程(解码过程)中,从目标分子中提取一个小的子结构作为生成的起始结构。在生成过程中,每次新添加原子或片段之前,对部分分子进行编码,确保与目标形状对齐。在分子图上使用图生成器,预测是否停止生成、选择焦点原子、选择新片段、预测附着位点和键序。生成过程中,动作选择会遵循已知的化学价规则,以确保化学有效性。新生成的键使用键打分器,模型通过预测旋转角度ψfoc来最大化生成分子与目标形状的相似性。

因此,SQUID是一个基于片段的、形状条件下的、自回归的 3D 分子生成的 VAE 模型,目的是生成有效的、符合特定 3D 形状的分子。

关于 SQUID 模型的损失,训练模型损失的表达式如下:

L_{total} ​= L_{graph-gen}​ + β_{KL}​ L_{KL}​ + β_{next-shape}​ L_{next-shape}​ + β_{∅-shape} ​L_{∅-shape}​ + L_{bond-scoring}​

其中,L_{graph-gen}代表分子图生成的损失,具体包括:分子可继续生长与否损失、生长点选择损失、下一个片段选择损失、下一个片段的附着位点损失、以及链接分子片段与下一个片段的键类型的损失,这些损失都是交叉熵损失。L_{KL}代表散度损失,约束变分编码。L_{next-shape}为辅助损失,用于引导生成器生成形状相似的多样化分子,表示生成分子与目标形状之间的匹配程度。L_{bond-scoring}是旋转键的回归评分损失。β为各种损失的权重,是模型的超参数。

关于模型的数据集,模型使用来自MOSES数据集的药物样分子进行训练,最大原子数量为27。从数据集中提取了100个片段(涵盖24种原子类型),确保每个片段包含两个或两个以上的原子,主要为环状片段,如苯环、萘环等。然后以此为基础,生成包含 1.3M 个 3D 分子的最终数据集。

每个训练分子从其3-6个原子的 3D 子结构中提取一个子结构,作为生成的起始结构。对每个分子生成一个 3D 构象,设定非环键的键长为其经验均值,并使用启发式规则固定非环键角。虽然这种处理忽略了真实分子的键变形,但对全局形状影响较小,并且可以在不严重改变形状的情况下恢复精细几何结构。

1.3 模型性能介绍

论文对SQUID模型的性能进行了两个方面的评估,分别是:形状条件下的多样性分子生成 和 形状限制下的分子优化。

1.3.1 形状条件下的多样性分子生成

形状条件下的多样性分子生成的目的是:评估模型生成与目标形状相似但化学结构多样的分子的能力。采用的方法是,对于测试集中的1000个分子,使用不同的\lambda(分别为0.3和1.0,采样分布,代表后验知识的比例),每个分子生成50个候选分子。使用高斯描述计算的体积重叠,量化生成分子与目标分子的3D形状相似性(simS);使用RDKit计算的Tanimoto相似性(simG),量化生成分子与目标分子的化学结构相似性。

生成的分子中。simG< 0.7 或者 0.3的分子的 simS 分布,结果如下图:

图中表明,使用λ=0.3时,生成的分子化学多样性较低,但形状相似性较高;使用λ=1.0时,生成的分子化学多样性较高,但形状相似性稍有下降。另外,其他统计表明,99%的生成分子是新颖的,95%是独特的,且100%化学有效,87%的生成分子没有任何空间冲突,表明SQUID生成的3D分子具有较高的真实性和化学有效性。从图C来看,生成分子的 3D 结构与目标3D结构之间非常相似。

1.3.2 形状限制下的分子优化

形状限制下的分子优化的目的是:SQUID 在保持高形状相似度的同时,优化分子的某些目标属性(如生物活性、毒性等)的能力。使用的方法是:使用GaucaMol基准中的目标属性,通过遗传算法对种子分子的变分编码进行迭代变异,生成目标分数更高且形状相似的分子。

SQUID 项目的的 github 连接: https://github.com/keiradams/SQUID

二、环境安装

复制项目代码:

git clone https://github.com/keiradams/SQUID.git

项目目录如下:

.
├── data # 下载的数据
├── dataset_generation
├── data.zip # 下载的数据
├── environment.yml
├── LICENSE
├── models
├── MO_virtual_screening
├── our_test
├── paper_results.zip # 下载的数据
├── README.md
├── RUN_ME.ipynb
├── shape_conditioned_generation_dataset_baseline.py
├── shape_conditioned_generation_evaluations.py
├── shape_constrained_optimization_evaluations.py
├── similarity-all_hits.txt
├── similarity-all.txt
├── test
├── trained_models
├── train_graph_generator.py
├── train_scorer.py
└── utils

创建conda环境:

conda env create -f environment.yml

在创建环境的过程中,会出现以下报错:

Pip subprocess error:
ERROR: Could not find a version that satisfies the requirement openeye-toolkits==2022.1.1 (from versions: none)
ERROR: No matching distribution found for openeye-toolkits==2022.1.1failedCondaEnvException: Pip failed

这是在安装 openeye-toolkits 找不到包的问题。openeye-toolkits 不是一个开源的免费工具,因此无法直接通过 pip 安装。

如果有 openeye-toolkits 安装包,可以通过 python setup.py install 安装(注:linse需要放置在安装包的 Other/Proprietary License 文件夹内)。执行完 python setup.py install ,pip 还会从 pipy 中安装其中的依赖,但是会找不到,因为openeye-toolkits 不是开源的,因此也找不到依赖,直接kill就好。

openeye 工具包使用 ROCS(Rapid Overlay of Chemi cal Structures) 将 3D 分子刚性对齐,并计算相似性。如果没有 openeye 的 linsece 可以使用 Shaep 代替,但是计算效率会下降。

Shaep 实现基于形状和静电势的分子结构刚性叠加功能。Shaep 的下载地址是:ShaEP。下载完成后,直接解压即可以用,无需安装。解压后放置在 ../目录中。

在使用 conda env create -f environment.yml 安装环境的时候,由于 openeye 包的安装失败,打断了其他模块的安装,会导致其他模块没有安装。因此需要继续安装:

conda install pyg pytorch-cluster pytorch-scatter -c pyg

三、SQUID 分子生成测试

3.1 Notebook中的示例

作者提供了一个 notebook (./RUN_ME.ipynb),在其中以示例,给出了使用方法介绍。

在运行之前,需要下载数据集。下载链接为:https://figshare.com/s/3d2f8fd57d9a65fe237e

下载后,解压到 ./ 目录中,会有一个data文件夹,比较大,共 47G。其中,包含模型训练和测试需要的 MOSES 数据集、100个分子片段组成的片段库、过滤后的分子库、RDKIT生成的小分子的3D构象以及固定烷基化学键距离和角度前后的构象、预训练图生成器和键打分器的数据。

解压完成后的 ./data文件目录为:

.
└── MOSES2├── max_future_rocs_data_artificial_alpha_2_0├── MOSES2_training_val_atom_fragment_associations_array.npy├── MOSES2_training_val_AtomFragment_database.pkl├── MOSES2_training_val_atoms_pointer.npy├── MOSES2_training_val_bond_lookup.pkl├── MOSES2_training_val_bonds_pointer.npy├── MOSES2_training_val_canonical_terminalSeeds_unmerged_future_rocs_database_all_reduced.pkl├── MOSES2_training_val_edge_features_array.npy├── MOSES2_training_val_edge_index_array.npy├── MOSES2_training_val_filtered_database_artificial_mols.pkl├── MOSES2_training_val_filtered_database.pkl├── MOSES2_training_val_node_features_array.npy├── MOSES2_training_val_unique_atoms.npy├── MOSES2_training_val_xyz_array.npy├── MOSES2_training_val_xyz_artificial_array.npy├── MOSES2_train_smiles_split.csv├── MOSES2_val_smiles_split.csv├── test_MOSES.csv├── test_MOSES_filtered_artificial_mols.pkl├── training_split_arrays├── train_MOSES.csv└── validation_split_arrays4 directories, 19 files

以下是 Notebook 中的内容。

导入模块:

import torch_geometric
import torch
import torch_scatter
import math
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from copy import deepcopy
import rdkit
import rdkit.Chem
import rdkit.Chem.AllChem
import rdkit.Chem.rdMolTransforms
import networkx as nx
import random
from tqdm import tqdm
from rdkit.Chem import rdMolTransforms
import itertools
import os
import pickleimport torch.nn as nn
import torch.nn.functional as F
from models.vnn.models.vn_layers import *
from models.vnn.models.utils.vn_dgcnn_util import get_graph_featurefrom utils.general_utils import *from models.EGNN import *
from models.models import *

模型的超参数,包括,先验插值比例 \lambda,局部生长停止的阈值、标准偏差程度,是否使用等变网络。

interpolate_to_GNN_prior = 1.0 # 'prior'
stop_threshold = 0.01
variational_GNN_factor = 1.0
ablateEqui = False

准备对10个分子进行测试,每个分子生成20个分子。

repetitions = 20
total_evaluations = 10 

加载必要的数据,包括:片段库、键长表、原子种类字典、测试集的3D分子

# 使用人工的分子,即分子中烷基键的长固定
use_artificial_mols = TrueAtomFragment_database = pd.read_pickle('data/MOSES2/MOSES2_training_val_AtomFragment_database.pkl')
AtomFragment_database = AtomFragment_database.iloc[1:].reset_index(drop = True) # removing stop token from AtomFragment_database
fragment_library_atom_features = np.concatenate(AtomFragment_database['atom_features'], axis = 0).reshape((len(AtomFragment_database), -1))bond_lookup = pd.read_pickle('data/MOSES2/MOSES2_training_val_bond_lookup.pkl')
unique_atoms = np.load('data/MOSES2/MOSES2_training_val_unique_atoms.npy') test_mol_df = pd.read_pickle('data/MOSES2/test_MOSES_filtered_artificial_mols.pkl')

test_mol_df 是一个 dataframe 对象,如下图:

使用人工分子,然后打乱顺序

test_mols = list(test_mol_df.artificial_mol)random.seed(0)
indices_to_evaluate = list(range(0, len(test_mols)))
random.shuffle(indices_to_evaluate)val_mols = [test_mols[i] for i in indices_to_evaluate]
val_mols_index = indices_to_evaluate

图生成器的 checkpoint、键打分器的 checkpoint:

if not ablateEqui:model_3D_PATH = 'trained_models/graph_generator.pt'rocs_model_3D_PATH = 'trained_models/scorer.pt'
else:model_3D_PATH = 'trained_models/graph_generator_ablateEqui.pt'rocs_model_3D_PATH = 'trained_models/scorer_ablateEqui.pt'

初始化图生成器和键打分器,并加载权重:

# HYPERPARAMETERS for 3D graph generator
pointCloudVar = 1. / (12. * 1.7) model_3D = Model_Point_Cloud_Switched(input_nf = 45, edges_in_d = 5, n_knn = 5, conv_dims = [32, 32, 64, 128], num_components = 64, fragment_library_dim = 64, N_fragment_layers = 3, append_noise = False, N_members = 125 - 1, EGNN_layer_dim = 64, N_EGNN_layers = 3, output_MLP_hidden_dim = 64, pooling_MLP = False, shared_encoders = False, subtract_latent_space = True,variational = False,variational_mode = 'inv', # not usedvariational_GNN = True,mix_node_inv_to_equi = True,mix_shape_to_nodes = True,ablate_HvarCat = False,predict_pairwise_properties = False,predict_mol_property = False,ablateEqui = ablateEqui,old_EGNN = False,).float()model_3D.load_state_dict(torch.load(model_3D_PATH, map_location=next(model_3D.parameters()).device), strict = True)
model_3D.eval()# HYPERPARAMETERS for ROCS scorer
rocs_pointCloudVar = 1. / (12. * 1.7) rocs_model_3D = ROCS_Model_Point_Cloud(input_nf = 45, edges_in_d = 5, n_knn = 10, conv_dims = [32, 32, 64, 128], num_components = 64, fragment_library_dim = 64,N_fragment_layers = 3, append_noise = False, N_members = 125 - 1, EGNN_layer_dim = 64, N_EGNN_layers = 3, output_MLP_hidden_dim = 64, pooling_MLP = False, shared_encoders = False, subtract_latent_space = True,variational = False,variational_mode = 'inv', # not usedvariational_GNN = False,mix_node_inv_to_equi = True,mix_shape_to_nodes = True,ablate_HvarCat = False,ablateEqui = ablateEqui,old_EGNN = False,).float()rocs_model_3D.load_state_dict(torch.load(rocs_model_3D_PATH, map_location=next(rocs_model_3D.parameters()).device), strict = True)
rocs_model_3D.eval()

执行SQUID,生成分子

# muting noisy warnings
from rdkit import RDLogger
import warnings
lg = RDLogger.logger()
lg.setLevel(RDLogger.CRITICAL)
warnings.filterwarnings("ignore", category=DeprecationWarning) 
warnings.filterwarnings("ignore", category=UserWarning) 
warnings.filterwarnings("ignore", category=Warning) 
np.warnings.filterwarnings('ignore', category=np.VisibleDeprecationWarning)file_idx = 0reference_mols_list = []
unaligned_mols_list = []for m_idx, m_ in enumerate(val_mols):seed = 0random.seed(seed)np.random.seed(seed = seed)torch.manual_seed(seed)m = deepcopy(m_)mol = deepcopy(m)# 生成 3D构象 即坐标xyz = np.array(mol.GetConformer().GetPositions())# 中心center_of_mass = np.sum(xyz, axis = 0) / xyz.shape[0]# 去中心xyz_centered = xyz - center_of_mass# 计算每个原子的隐向量for i in range(0, mol.GetNumAtoms()):x,y,z = xyz_centered[i]mol.GetConformer().SetAtomPosition(i, Point3D(x,y,z))m = deepcopy(mol)mol_target = deepcopy(m)ring_fragments = get_ring_fragments(mol_target)all_possible_seeds = get_all_possible_seeds(mol_target, ring_fragments)terminal_seeds = filter_terminal_seeds(all_possible_seeds, mol_target)select_seeds = get_starting_seeds(mol_target, AtomFragment_database, fragment_library_atom_features, unique_atoms, bond_lookup)if len(select_seeds) == 0:continuerandom_seed_selection = random.randint(0, len(select_seeds) - 1)select_seeds = [select_seeds[random_seed_selection]] * repetitionsrepeated_rocs = []repeated_tanimoto = []reference_mols = []unaligned_mols = []for seed in select_seeds:mol = deepcopy(m)# extracting starting seed and preparing to generateframe_generation, frame_rocs = get_frame_terminalSeeds(mol, seed, AtomFragment_database, include_rocs = True)positions = list(frame_rocs.iloc[0].positions_before)start = 0for i in range(len(frame_generation)):if (set(frame_generation.iloc[i].partial_graph_indices) == set(positions)) & (frame_generation.iloc[i].next_atom_index == -1):start = i + 1breakif len(frame_generation.iloc[0].partial_graph_indices) == 1: # seed is a terminal ATOMterminalSeed_frame = frame_generation.iloc[0:start].reset_index(drop = True)sequence = get_ground_truth_generation_sequence(terminalSeed_frame, AtomFragment_database, fragment_library_atom_features)mol = deepcopy(terminalSeed_frame.iloc[0].rdkit_mol_cistrans_stereo)partial_indices = deepcopy(terminalSeed_frame.iloc[0].partial_graph_indices_sorted)final_partial_indices = deepcopy(terminalSeed_frame.iloc[-1].partial_graph_indices_sorted)ring_fragments = get_ring_fragments(mol)add_to_partial = [list(f) for p in final_partial_indices for f in ring_fragments if p in f]add_to_partial = [item for sublist in add_to_partial for item in sublist]final_partial_indices = list(set(final_partial_indices).union(add_to_partial))queue_indices = deepcopy(terminalSeed_frame.iloc[0].focal_indices_sorted)_, seed_mol, queue, positioned_atoms_indices, atom_to_library_ID_map, _, _, _ = generate_seed_from_sequence(sequence, mol, partial_indices, queue_indices, AtomFragment_database, unique_atoms, bond_lookup, stop_after_sequence = True)seed_node_features = getNodeFeatures(seed_mol.GetAtoms())for k in atom_to_library_ID_map:seed_node_features[k] = AtomFragment_database.iloc[atom_to_library_ID_map[k]].atom_featuresG = get_substructure_graph(mol, final_partial_indices)G_seed = get_substructure_graph(seed_mol, list(range(0, seed_mol.GetNumAtoms())), node_features = seed_node_features)nm = nx.algorithms.isomorphism.generic_node_match(['atom_features'], [None], [np.allclose])em = nx.algorithms.isomorphism.numerical_edge_match("bond_type", 1.0)GM = nx.algorithms.isomorphism.GraphMatcher(G, G_seed, node_match = nm, edge_match = em)assert GM.is_isomorphic()idx_map = GM.mappingelse: # seed is a terminal FRAGMENTpartial_indices = deepcopy(frame_generation.iloc[0].partial_graph_indices_sorted)final_partial_indices = partial_indicesseed_mol = generate_conformer(get_fragment_smiles(mol, partial_indices))idx_map = get_reindexing_map(mol, partial_indices, seed_mol)positioned_atoms_indices = sorted([idx_map[f] for f in final_partial_indices])atom_to_library_ID_map = {} # no individual atoms yet generatedqueue = [0] # 0 can be considered the focal root nodefor i in final_partial_indices:x,y,z = mol.GetConformer().GetPositions()[i]seed_mol.GetConformer().SetAtomPosition(idx_map[i], Point3D(x,y,z)) starting_queue = deepcopy(queue)try:_, updated_mol, _, _, _, N_rocs_decisions, _, _, _, chirality_scored = generate_3D_mol_from_sequence(sequence = [], partial_mol = deepcopy(seed_mol), mol = deepcopy(mol_target), positioned_atoms_indices = deepcopy(positioned_atoms_indices), queue = starting_queue, atom_to_library_ID_map = deepcopy(atom_to_library_ID_map), model = model_3D, rocs_model = rocs_model_3D,AtomFragment_database = AtomFragment_database,unique_atoms = unique_atoms, bond_lookup = bond_lookup,N_points = 5, N_points_rocs = 5,stop_after_sequence = False,mask_first_stop = False,stochastic = False, chirality_scoring = True,stop_threshold = stop_threshold,steric_mask = True,variational_factor_equi = 0.0,variational_factor_inv = 0.0, interpolate_to_prior_equi = 0.0,interpolate_to_prior_inv = 0.0, use_variational_GNN = True, variational_GNN_factor = variational_GNN_factor, interpolate_to_GNN_prior = interpolate_to_GNN_prior, rocs_use_variational_GNN = False, rocs_variational_GNN_factor = 0.0, rocs_interpolate_to_GNN_prior = 0.0,pointCloudVar = pointCloudVar, rocs_pointCloudVar = rocs_pointCloudVar,)pred_rocs = get_ROCS(torch.tensor(np.array(updated_mol.GetConformer().GetPositions())), torch.tensor(np.array(mol.GetConformer().GetPositions())))tanimoto = rdkit.DataStructs.FingerprintSimilarity(*[rdkit.Chem.RDKFingerprint(x) for x in [mol, updated_mol]])reference_mols.append(mol)unaligned_mols.append(updated_mol)repeated_rocs.append(pred_rocs.item())repeated_tanimoto.append(tanimoto)except Exception as e:print(f'error during 3D generation -- {e}')continueprint(f'Encoded Molecule {file_idx + 1}:')print('(non-aligned) shape scores:', [np.round(r, 3) for r in repeated_rocs])print('tanimoto chemical similarity:', [np.round(r, 3) for r in repeated_tanimoto])print()file_idx += 1reference_mols_list.append(reference_mols[0])unaligned_mols_list.append(unaligned_mols)if file_idx == total_evaluations:break

运行输出。生成的分子保存在 unaligned_mols_list, 参考分子保存在 reference_mols_list。每一个分子没有经过 align 之前的形状相似性、化学结构相似性:

Encoded Molecule 1:
(non-aligned) shape scores: [0.45, 0.645, 0.55, 0.627, 0.617, 0.512, 0.435, 0.802, 0.37, 0.532, 0.438, 0.527, 0.778, 0.61, 0.553, 0.579, 0.822, 0.517, 0.807, 0.476]
tanimoto chemical similarity: [0.266, 0.253, 0.271, 0.352, 0.208, 0.232, 0.332, 0.465, 0.304, 0.257, 0.27, 0.372, 0.375, 0.244, 0.252, 0.289, 0.327, 0.348, 0.276, 0.255]
warning: bond distance between atoms 8 and 8 unknown
Encoded Molecule 2:
(non-aligned) shape scores: [0.421, 0.529, 0.492, 0.708, 0.486, 0.663, 0.68, 0.627, 0.693, 0.707, 0.582, 0.721, 0.784, 0.655, 0.633, 0.674, 0.513, 0.516, 0.704, 0.494]
tanimoto chemical similarity: [0.338, 0.416, 0.188, 0.316, 0.315, 0.215, 0.342, 0.27, 0.339, 0.291, 0.277, 0.302, 0.296, 0.285, 0.46, 0.278, 0.318, 0.266, 0.248, 0.195]
Encoded Molecule 3:
(non-aligned) shape scores: [0.431, 0.399, 0.382, 0.625, 0.512, 0.663, 0.414, 0.627, 0.748, 0.467, 0.568, 0.434, 0.686, 0.815, 0.573, 0.448, 0.571, 0.472, 0.576, 0.389]
tanimoto chemical similarity: [0.287, 0.172, 0.135, 0.254, 0.29, 0.207, 0.179, 0.252, 0.297, 0.142, 0.271, 0.16, 0.187, 0.265, 0.298, 0.274, 0.16, 0.306, 0.279, 0.265]
Encoded Molecule 4:
(non-aligned) shape scores: [0.251, 0.374, 0.785, 0.333, 0.371, 0.416, 0.533, 0.384, 0.365, 0.379, 0.361, 0.336, 0.499, 0.352, 0.356, 0.349, 0.472, 0.329, 0.602, 0.411]
tanimoto chemical similarity: [0.243, 0.24, 0.246, 0.2, 0.232, 0.223, 0.181, 0.206, 0.237, 0.223, 0.195, 0.161, 0.207, 0.227, 0.2, 0.194, 0.171, 0.162, 0.258, 0.179]
Encoded Molecule 5:
(non-aligned) shape scores: [0.439, 0.475, 0.649, 0.666, 0.328, 0.605, 0.574, 0.419, 0.345, 0.516, 0.701, 0.209, 0.233, 0.57, 0.666, 0.515, 0.528, 0.54, 0.631, 0.59]
tanimoto chemical similarity: [0.164, 0.147, 0.165, 0.172, 0.136, 0.218, 0.159, 0.167, 0.119, 0.157, 0.173, 0.18, 0.137, 0.169, 0.15, 0.148, 0.154, 0.159, 0.171, 0.152]
Encoded Molecule 6:
(non-aligned) shape scores: [0.72, 0.534, 0.828, 0.375, 0.767, 0.729, 0.818, 0.675, 0.65, 0.553, 0.607, 0.587, 0.767, 0.678, 0.444, 0.764, 0.753, 0.663, 0.625, 0.598]
tanimoto chemical similarity: [0.215, 0.236, 0.2, 0.169, 0.198, 0.181, 0.15, 0.21, 0.169, 0.232, 0.19, 0.185, 0.245, 0.164, 0.209, 0.215, 0.181, 0.166, 0.181, 0.208]
Encoded Molecule 7:
(non-aligned) shape scores: [0.367, 0.416, 0.445, 0.328, 0.443, 0.482, 0.397, 0.27, 0.198, 0.583, 0.464, 0.513, 0.52, 0.579, 0.53, 0.408, 0.418, 0.472, 0.239, 0.152]
tanimoto chemical similarity: [0.199, 0.238, 0.238, 0.197, 0.245, 0.248, 0.177, 0.246, 0.119, 0.2, 0.213, 0.227, 0.187, 0.283, 0.216, 0.222, 0.189, 0.322, 0.191, 0.227]
Encoded Molecule 8:
(non-aligned) shape scores: [0.525, 0.801, 0.493, 0.626, 0.485, 0.72, 0.585, 0.618, 0.607, 0.717, 0.442, 0.456, 0.617, 0.585, 0.691, 0.435, 0.584, 0.735, 0.663, 0.47]
tanimoto chemical similarity: [0.23, 0.357, 0.207, 0.249, 0.118, 0.282, 0.221, 0.246, 0.226, 0.212, 0.211, 0.276, 0.098, 0.259, 0.2, 0.322, 0.333, 0.212, 0.155, 0.374]
Encoded Molecule 9:
(non-aligned) shape scores: [0.487, 0.689, 0.618, 0.622, 0.66, 0.728, 0.522, 0.687, 0.667, 0.535, 0.687, 0.594, 0.802, 0.443, 0.798, 0.645, 0.555, 0.898, 0.72, 0.683]
tanimoto chemical similarity: [0.256, 0.184, 0.276, 0.281, 0.336, 0.34, 0.328, 0.303, 0.33, 0.338, 0.42, 0.337, 0.52, 0.2, 0.336, 0.331, 0.178, 0.479, 0.441, 0.326]
Encoded Molecule 10:
(non-aligned) shape scores: [0.47, 0.489, 0.557, 0.726, 0.644, 0.705, 0.43, 0.488, 0.543, 0.668, 0.402, 0.394, 0.463, 0.428, 0.388, 0.708, 0.519, 0.745, 0.265, 0.63]
tanimoto chemical similarity: [0.444, 0.347, 0.389, 0.285, 0.231, 0.255, 0.225, 0.279, 0.164, 0.325, 0.213, 0.228, 0.202, 0.21, 0.254, 0.279, 0.232, 0.352, 0.234, 0.26]

因为我们没有 OpenEye,所以只能使用 shaep 代替 OpenEye 计算生成分子与参考分子之间的形状相似性。然后提取最为形状相似的分子。

首先, 需要将参考分子和生成的分子保存成sdf。保存目录为 ./test文件夹,需要先创建。

from rdkit import Chem# 保存参考分子
sdf_writer = Chem.SDWriter('./test/ref.sdf')
for i, mol in enumerate(reference_mols_list):sdf_writer.write(mol)
sdf_writer.close()# 保存生成的分子
sdf_writer = Chem.SDWriter('./test/gen.sdf')
for i, mol in enumerate(unaligned_mols_list):for k, mol_i in enumerate(mol):sdf_writer.write(mol_i)
sdf_writer.close()

使用 shaep 计算形状相似性,参考分子作为查询分子,生成分子作为分子库

结果保存在 similarity-all.txt 文件中。注意:下面的命令不能重复运行,原生成的文件需要先删除

! ../shaep -q ./test/ref.sdf  ./test/gen.sdf  ./test/similarity-all.txt --onlyshape

运行输出:

ShaEP version 1.3.1.f5e1226987eb27f7ad273010d5ca3cb8beb9fe02 64-bit build Feb 22 2020 15:04:35
Copyright (c) 2007-2020 Mikko J. Vainio and J. Santeri Puranen.
Copyright (c) 2010 Visipoint Ltd. www.visipoint.fi
All rights reserved.1 molecule and 200 structures processed in 00:00:03.130000 wall,
00:00:02.660000 user,
00:00:00 system,
00:00:02.660000 total CPU time.

使用如下函数,解析./test/similarity-all.txt,提取各参考分子最相似的数据库分子

文本解析函数:

import numpy as npdef get_similarity(path='./test/similarity-3.txt', ref_num=10, lib_num=20):with open(path, 'r') as f:lines = f.readlines()# 提取矩阵data = lines[1].strip().split('\t')[6+ref_num:]# 相似度数值化data = [float(i) if i != 'nan' else 0 for i in data ]# 矩阵data = np.array(data)data.resize(lib_num, ref_num)return data

保存 每个参考分子对应的最相似分子

results = get_similarity(path='./test/similarity-all.txt', ref_num=10, lib_num=200)
index = np.argmax(results, axis=0)
index

对应的序号是:

array([  7,  34,  53,  62,  99, 116, 198, 141, 177, 197])

提取最相似的分子

max_align_mol = []# 生成分子组成库
unaligned_mols_list_ = [j for i in unaligned_mols_list for j in i]# 提取分子
for i, num in enumerate(index):sdf_writer = Chem.SDWriter('./test/best_align_{}_{}.sdf'.format(i, num))sdf_writer.write(unaligned_mols_list_[num])sdf_writer.close()max_align_mol.append(unaligned_mols_list_[num])

可视化结果,2D分子结构

from IPython.core.display import Image, display
from rdkit.Chem import Drawdef get_2D_mol(mol):mol_2D = deepcopy(mol)rdkit.Chem.rdDepictor.Compute2DCoords(mol_2D)return mol_2Dfor i in range(len(max_align_mol)):print('{}: encoded, decoded'.format(i))print('aligned shape similarity:', results[index[i], i])display(Draw.MolsToGridImage([get_2D_mol(reference_mols_list[i]), get_2D_mol(max_align_mol[i])]))

输出示例,参考分子1(左),最相似的分子右:

参考分子1为目标生成的分子示例:

以下为一些生成的3D分子示例,其中,绿色为参考分子,cayan色为AQUID模型生成的分子,紫色为经过对齐以后的分子。可以看到形状匹配的非常好。

参考分子 1, aligned shape similarity: 0.920819

参考分子2,aligned shape similarity: 0.918124

参考分子3, aligned shape similarity: 0.905439

以下是作者的源代码,使用openeye计算生成分子与原分子的形状相似性。(缺少linsece 无法运行)

计算相似性:

# Note that we can safely ignore these aromaticity warnings.from utils.openeye_utils import *all_shape_similarity = []
for r, ref in enumerate(reference_mols_list):ROCS_out = ROCS_shape_overlap(unaligned_mols_list[r], ref)aligned_shape_similarity = [ROCS_out[i][1] for i in range(len(ROCS_out))]all_shape_similarity.append(aligned_shape_similarity)

选择形状相似性最好的分子:

best_generated_indices = [np.argmax(s) for s in all_shape_similarity]
best_generated_mols = [unaligned_mols_list[i][best_generated_indices[i]] for i in range(len(best_generated_indices))]

可视化,展示最相似的分子:

from IPython.core.display import Image, display
from rdkit.Chem import Drawdef get_2D_mol(mol):mol_2D = deepcopy(mol)rdkit.Chem.rdDepictor.Compute2DCoords(mol_2D)return mol_2D# Displaying most shape-similar molecules
for i in range(len(best_generated_mols)):print('encoded, decoded')print('aligned shape similarity:', all_shape_similarity[i][best_generated_indices[i]])display(Draw.MolsToGridImage([get_2D_mol(reference_mols_list[i]), get_2D_mol(best_generated_mols[i])]))

3.2 使用自己的分子案例

使用的测试案例是,3WZE晶体的小分子。如下图:

SQUID生成的最相似小分子,以及align到晶体小分子以后,如下图:(绿色为晶体的参考小分子,canyan是生成的小分子,紫色是生成的小分子align到晶体小分子以后的)

奇怪的是,SQUID直接生成的pose与参考分子之间有比较大的距离,但是经过align以后,形状还是非常相似的,形状相似度为0.86。参考分子和生成的最相似的分子的2D结构如下图:

针对3WZE晶体的小分子生成的所有20个分子的2D结构如下图。从图中看,生成小分子的结构比较类药,但是有的小分子偏小。这可能是因为形状的限制,在生成分子时,很容易留下空白的边缘。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/383101.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【iOS】isMemberOfClassisKindOfClass

目录 前言class方法isMemberOfClass和isKindOfClass实例方法分析类方法分析 实例验证总结 前言 认识这两个方法之前&#xff0c;首先要了解isa指向流程和继承链&#xff08;【iOS】类对象的结构分析&#xff09;关系&#xff0c;以便理解得更透彻 上经典图&#xff1a; 要注意…

动态代理更改Java方法的返回参数(可用于优化feign调用后R对象的统一处理)

动态代理更改Java方法的返回参数&#xff08;可用于优化feign调用后R对象的统一处理&#xff09; 需求原始解决方案优化后方案1.首先创建AfterInterface.java2.创建InvocationHandler处理代理方法3. 调用 实际运行场景拓展 需求 某些场景&#xff0c;调用别人的方法&#xff0…

深入浅出WebRTC—DelayBasedBwe

WebRTC 中的带宽估计是其拥塞控制机制的核心组成部分&#xff0c;基于延迟的带宽估计是其中的一种策略&#xff0c;它主要基于延迟变化推断出可用的网络带宽。 1. 总体架构 1.1. 静态结构 1&#xff09;DelayBasedBwe 受 GoogCcNetworkController 控制&#xff0c;接收其输入…

贪心算法(算法篇)

算法之贪心算法 贪心算法 概念&#xff1a; 贪心算法是一种思想&#xff0c;并不是一种算法&#xff0c;贪心算法是分阶段地工作&#xff0c;在每一个阶段&#xff0c;可以认为所作决定是好的&#xff0c;而不考虑将来地后果。算法的每个阶段总是选择当前阶段最优&#xff0…

Kafka Producer之数据重复和乱序问题

文章目录 1. 数据重复2. 数据乱序 为了可靠性&#xff0c;Kafka有消息重试机制&#xff0c;但是同时也带来了2大问题 1. 数据重复 消息发送到broker后&#xff0c;broker记录消息数据到log中&#xff0c;但是由于网络问题&#xff0c;producer没有收到acks&#xff0c;于是再次…

Axure设计之轮播图(动态面板+中继器)

轮播图&#xff08;Carousel&#xff09;是一种网页或应用界面中常见的组件&#xff0c;用于展示一系列的图片或内容&#xff0c;通常通过自动播放或用户交互&#xff08;如点击箭头按钮&#xff09;来切换展示不同的内容。轮播图能够吸引用户的注意力&#xff0c;有效展示重要…

新手小白的pytorch学习第十一弹-----Computer Vision创建基础模型使用FashionMNIST

目录 PyTorch Computer Vision0 PyTorch 中 Computer vision 的库1 获得一个数据集1.1 查看数据的输入和输出形状1.2 可视化数据 2 准备 DataLoader3 Model 0: 创建一个 baseline model3.1 设置损失函数、优化器和评估指标3.2 创建一个函数来给我们的实验计时3.3 在批量数据集上…

萝卜快跑:自动驾驶的先锋与挑战

萝卜快跑&#xff1a;自动驾驶的先锋与挑战 近段时间&#xff0c;由萝卜快跑引发的自动驾驶事件如火如荼&#xff0c;成为科技领域的热门话题。萝卜快跑作为自动驾驶领域的重要参与者&#xff0c;其最新事件引发了广泛的关注和讨论。 萝卜快跑是百度推出的自动驾驶出行服务平台…

20240724-然后用idea创建一个Java项目/配置maven环境/本地仓储配置

1.创建一个java项目 &#xff08;1&#xff09;点击页面的create project&#xff0c;然后next &#xff08;2&#xff09;不勾选&#xff0c;继续next &#xff08;3&#xff09;选择新项目名称&#xff0c;新项目路径&#xff0c;然后Finsh&#xff0c;在新打开的页面选择…

Hadoop、Hive、HBase、数据集成、Scala阶段测试

姓名&#xff1a; 总分&#xff1a;Hadoop、Hive、HBase、数据集成、Scala阶段测试 一、选择题&#xff08;共20道&#xff0c;每道0.5分&#xff09; 1、下面哪个程序负责HDFS数据存储&#xff08; C &#xff09; A. NameNode B. Jobtracher C. DataNode D. Sec…

鸿蒙界面开发

界面开发 //构建 → 界面 build() {//行Row(){//列Column(){//文本 函数名(参数) 对象.方法名&#xff08;参数&#xff09; 枚举名.变量名Text(this.message).fontSize(40)//设置文本大小.fontWeight(FontWeight.Bold)//设置文本粗细.fontColor(#ff2152)//设置文本颜色}.widt…

3.JAVA-IDEA

IDEA介绍 下载安装 实际操作 免费试用&#xff0c;可以选第一个自己找到密匙开锁 首先新建project项目 建立空项目 起名并存储位置选择 确定创建项目 成功新建项目&#xff0c;开始新建模块 新建或导入模块 新建java模块 修改名称&#xff0c;位置保持默认 同样yes建立 ok即可 …

2 YOLO8的使用

1 介绍 YOLOv8是YOLO (You Only Look Once) 目标检测模型系列的最新版本&#xff0c;由Ultralytics公司开发和维护。YOLOv8是在先前版本的基础上进行的重大更新&#xff0c;不仅提升了性能&#xff0c;还增加了更多的功能&#xff0c;它不仅能够进行目标检测&#xff0c;还能完…

职业教育综合布线实验实训室建设应用案例

在信息技术迅猛发展的今天&#xff0c;综合布线技术已成为智能建筑、数据中心等基础设施不可或缺的一部分。唯众&#xff0c;作为职业教育领域的先行者&#xff0c;早在多年前便洞悉行业趋势&#xff0c;率先涉足综合布线实训室的建设&#xff0c;凭借丰富的经验和持续的创新&a…

phpstorm配置xdebug3

查看php路径相关信息 php --ini安装xdebug https://www.jetbrains.com/help/phpstorm/2024.1/configuring-xdebug.html?php.debugging.xdebug.configure php.ini 配置 在最后添加&#xff0c;以下是我的配置 [xdebug] zend_extension/opt/homebrew/Cellar/php8.1/8.1.29/p…

决策树 和 集成学习、随机森林

决策树是非参数学习算法&#xff0c;可以解决分类问题&#xff0c;天然可以解决多分类问题&#xff08;不同于逻辑回归或者SVM&#xff0c;需要通过OVR&#xff0c;OVO的方法&#xff09;&#xff0c;也可以解决回归问题&#xff0c;甚至是多输出任务&#xff0c;并且决策树有非…

【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第三篇 嵌入式Linux驱动开发篇-第五十一章 添加设备树节点

i.MX8MM处理器采用了先进的14LPCFinFET工艺&#xff0c;提供更快的速度和更高的电源效率;四核Cortex-A53&#xff0c;单核Cortex-M4&#xff0c;多达五个内核 &#xff0c;主频高达1.8GHz&#xff0c;2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT…

通过4G模块EC600N向阿里云物联网平台物模型上面发送字符串,现在发送int数据是成功的,发送字符串就是不成功

&#x1f3c6;本文收录于《CSDN问答解惑-专业版》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收…

轻量化YOLOv7系列:结合G-GhostNet | 适配GPU,华为诺亚提出G-Ghost方案升级GhostNet

轻量化YOLOv7系列&#xff1a;结合G-GhostNet | 适配GPU&#xff0c;华为诺亚提出G-Ghost方案升级GhostNet 需要修改的代码models/GGhostRegNet.py代码 创建yaml文件测试是否创建成功 本文提供了改进 YOLOv7注意力系列包含不同的注意力机制以及多种加入方式&#xff0c;在本文…

【Python】Facebook开源时间序列数据预测模型Prophet

文章目录 一、简介二、项目的文件解读三、Prophet类主要方法和参数3.1 主要参数3.2 主要方法 四、用法示例 一、简介 Prophet 是由 Facebook 开发的一个开源工具&#xff0c;用于时间序列数据的预测。它特别适用于处理具有强季节性和趋势的时间序列数据&#xff0c;并且对节假…