分享

DGL&RDKit|基于GCN与基于3D描述符的分子溶解度预测模型对比

 DrugAI 2022-04-19

1

GCN

图卷积神经网络(Graph Convolutional Networks, GCN )

图卷积的原理


处理图形或网络的数据形式存在许多重要的实际问题,如社交网络、知识图形、蛋白质相互作用网络和分子图形等。然而,将深度学习应用于这些图形数据是非常重要的,因为它具有独特地图特征。人们非常关注神经网络模型对这种结构化图形数据的概括。过去的几年中,许多论文重新讨论推广神经网络以处理任意结构化图形的问题。下面的小节中给出了图的表示和图卷的两种方式,即空间卷积和谱卷积。空间卷积GCN是可区分的消息传递模式,其在局部图形邻域上操作到任意图形。对于社交网络,知识图和分子图等图形,它比谱卷积更受欢迎。谱卷积GCN的思想是利用光谱理论在拓扑图上实现卷积运算,通常用于处理数据,如图像和视频。

图形定义


图(graph)是一种数据格式,它可以用于表示社交网络、通信网络、蛋白分子网络等,图中的节点表示网络中的个体,连边表示个体之间的连接关系。许多机器学习任务例如社团发现、链路预测等都需要用到图结构数据,因此图卷积神经网络的出现为这些问题的解决提供了新的思路。

空间卷积


早期尝试推广结构化数据的判别嵌入中,Dai等人提出了structure2vec,一种用于嵌入图结构化数据的潜变量模型,在图形模型中使用近似推理算法。推理算法的解决方案意味着一个传播方程,其中节点的表示是邻域边缘和来自邻居消息的函数。后来大部分GCN都建立在这个概念之上,并进行了广泛的修改,称为空间卷积。

空间卷积旨在直接在顶点域中构造卷积。关键思想是通过聚合来自其相邻节点的信息来更新某个节点的表示。空间卷积与Weisfeiler-Lehman算法一致,通常用于测试两个图是否是同构,其中节点标签由相邻节点的有序标签集重复地增强。这种传播的基本机制是首先将邻域信息视为图子结构,然后通过将不同的子结构递归地投影到不同的特征空间中,通过可微函数对这种子结构进行建模。邻居和中心节点之间的信息也称为消息。消息传递到中心节点的方式产生表征网络体系结构的不同传播规则。

谱卷积


分子图


图形提供了一种自然的方式来表示化学结构。这种情况下,原子是图的节点,键是边。然后可以为原子类型的节点“着色”,并为键类型“权衡”边缘。该图包含分子图和不同特性的边。

分子图的元素是:

  • 原子

任何两个原子之间仅允许一个连接/键,并且不允许自连接。

2

基于深度图学习框架DGL和GCN预测溶解度

导入库

  1. from collections import namedtuple

  2. import dgl

  3. from dgl import DGLGraph

  4. import dgl.function as fn

  5. import torch

  6. import torch.nn as nn

  7. import torch.nn.functional as F

  8. from torch.utils.data import Dataset, TensorDataset

  9. from torch.utils.data import DataLoader

  10. import torch.optim as optim

  11. from rdkit import Chem

  12. from rdkit.Chem import RDConfig

  13. import os

  14. import copy

  15. import numpy as np

  16. import networkx as nx

  17. import matplotlib.pyplot as plt

  18. import seaborn as sns

  19. %matplotlib inline

定义元素列表

  1. ELEM_LIST = ['C', 'N', 'O', 'S', 'F', 'Si', 'P', 'Cl', 'Br', 'Mg', 'Na', 'Ca', 'Fe', 'Al', 'I', 'B', 'K', 'Se', 'Zn', 'H', 'Cu', 'Mn', 'unknown']

  2. ATOM_FDIM = len(ELEM_LIST) + 6 + 5 + 1

  3. MAX_ATOMNUM =60

  4. BOND_FDIM = 5

  5. MAX_NB = 10

代码来自dgl的 junction tree,生成分子结构图

  1. PAPER = os.getenv('PAPER', False)

  2. def onek_encoding_unk(x, allowable_set):

  3. if x not in allowable_set:

  4. x = allowable_set[-1]

  5. return [x == s for s in allowable_set]

  6. # Note that during graph decoding they don't predict stereochemistry-related

  7. # characteristics (i.e. Chiral Atoms, E-Z, Cis-Trans). Instead, they decode

  8. # the 2-D graph first, then enumerate all possible 3-D forms and find the

  9. # one with highest score.

  10. '''

  11. def atom_features(atom):

  12. return (torch.Tensor(onek_encoding_unk(atom.GetSymbol(), ELEM_LIST)

  13. + onek_encoding_unk(atom.GetDegree(), [0,1,2,3,4,5])

  14. + onek_encoding_unk(atom.GetFormalCharge(), [-1,-2,1,2,0])

  15. + [atom.GetIsAromatic()]))

  16. '''

  17. def atom_features(atom):

  18. return (onek_encoding_unk(atom.GetSymbol(), ELEM_LIST)

  19. + onek_encoding_unk(atom.GetDegree(), [0,1,2,3,4,5])

  20. + onek_encoding_unk(atom.GetFormalCharge(), [-1,-2,1,2,0])

  21. + [atom.GetIsAromatic()])

  22. def bond_features(bond):

  23. bt = bond.GetBondType()

  24. return (torch.Tensor([bt == Chem.rdchem.BondType.SINGLE, bt == Chem.rdchem.BondType.DOUBLE, bt == Chem.rdchem.BondType.TRIPLE, bt == Chem.rdchem.BondType.AROMATIC, bond.IsInRing()]))

  25. def mol2dgl_single(mols):

  26. cand_graphs = []

  27. n_nodes = 0

  28. n_edges = 0

  29. bond_x = []

  30. for mol in mols:

  31. n_atoms = mol.GetNumAtoms()

  32. n_bonds = mol.GetNumBonds()

  33. g = DGLGraph()

  34. nodeF = []

  35. for i, atom in enumerate(mol.GetAtoms()):

  36. assert i == atom.GetIdx()

  37. nodeF.append(atom_features(atom))

  38. g.add_nodes(n_atoms)

  39. bond_src = []

  40. bond_dst = []

  41. for i, bond in enumerate(mol.GetBonds()):

  42. a1 = bond.GetBeginAtom()

  43. a2 = bond.GetEndAtom()

  44. begin_idx = a1.GetIdx()

  45. end_idx = a2.GetIdx()

  46. features = bond_features(bond)

  47. bond_src.append(begin_idx)

  48. bond_dst.append(end_idx)

  49. bond_x.append(features)

  50. bond_src.append(end_idx)

  51. bond_dst.append(begin_idx)

  52. bond_x.append(features)

  53. g.add_edges(bond_src, bond_dst)

  54. g.ndata['h'] = torch.Tensor(nodeF)

  55. cand_graphs.append(g)

  56. return cand_graphs

  57. msg = fn.copy_src(src="h", out="m")

定义GCN

  1. class NodeApplyModule(nn.Module):

  2. def __init__(self, in_feats, out_feats, activation):

  3. super(NodeApplyModule, self).__init__()

  4. self.linear = nn.Linear(in_feats, out_feats)

  5. self.activation = activation

  6. def forward(self, node):

  7. h = self.linear(node.data['h'])

  8. h = self.activation(h)

  9. return {'h': h}

  10. class GCN(nn.Module):

  11. def __init__(self, in_feats, out_feats, activation):

  12. super(GCN, self).__init__()

  13. self.apply_mod = NodeApplyModule(in_feats, out_feats, activation)

  14. def forward(self, g, feature):

  15. g.ndata['h'] = feature

  16. g.update_all(msg, reduce)

  17. g.apply_nodes(func=self.apply_mod)

  18. h = g.ndata.pop('h')

  19. #print(h.shape)

  20. return h

  21. class Classifier(nn.Module):

  22. def __init__(self, in_dim, hidden_dim, n_classes):

  23. super(Classifier, self).__init__()

  24. self.layers = nn.ModuleList([GCN(in_dim, hidden_dim, F.relu),

  25. GCN(hidden_dim, hidden_dim, F.relu)])

  26. self.classify = nn.Linear(hidden_dim, n_classes)

  27. def forward(self, g):

  28. h = g.ndata['h']

  29. for conv in self.layers:

  30. h = conv(g, h)

  31. g.ndata['h'] = h

  32. hg = dgl.mean_nodes(g, 'h')

  33. return self.classify(hg)

模型训练

  1. epoch_losses = []

  2. for epoch in range(200):

  3. epoch_loss = 0

  4. for i, (bg, label) in enumerate(data_loader):

  5. bg.set_e_initializer(dgl.init.zero_initializer)

  6. bg.set_n_initializer(dgl.init.zero_initializer)

  7. pred = model(bg)

  8. loss = loss_func(pred, label)

  9. optimizer.zero_grad()

  10. loss.backward()

  11. optimizer.step()

  12. epoch_loss += loss.detach().item()

  13. epoch_loss /= (i + 1)

  14. if (epoch+1) % 20 == 0:

  15. print('Epoch {}, loss {:.4f}'.format(epoch+1, epoch_loss))

  16. epoch_losses.append(epoch_loss)

  17. plt.plot(epoch_losses, c='b')

模型评价

  1. from sklearn.metrics import accuracy_score

  2. from sklearn.metrics import classification_report

  3. from sklearn.metrics import confusion_matrix

  4. accuracy_score(test_y, pred_y)

 0.7665369649805448

  1. print(classification_report(test_y, pred_y))

3

基于3D分子描述符预测溶解度

导入包

  1. from rdkit.Chem import AllChem

  2. from rdkit.Chem.Descriptors import rdMolDescriptors

  3. from sklearn.preprocessing import normalize

  4. from sklearn.ensemble import RandomForestClassifier

定义3D描述符计算函数,并计算3D描述符

  1. def calc_dragon_type_desc(mol):

  2. return rdMolDescriptors.CalcAUTOCORR3D(mol) + rdMolDescriptors.CalcMORSE(mol) + \

  3. rdMolDescriptors.CalcRDF(mol) + rdMolDescriptors.CalcWHIM(mol)

  4. train_X = normalize([calc_dragon_type_desc(m) for m in train_mols2])

  5. test_X = normalize([calc_dragon_type_desc(m) for m in test_mols2])

基于随机森林的分类

  1. rfc = RandomForestClassifier(n_estimators=100)

  2. rfc.fit(train_X, train_y)

模型评价



参考资料


项目地址:https://github.com/dmlc/dgl

所有示例模型的详细从零教程:

https://docs./tutorials/models/index.htm

DGL | 基于深度学习框架DGL的分子图初探

作者 / 编辑:王建民


DrugAI

    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多