GNN在生化任务上的工程实现学习笔记

目录 

1. 预备知识

GNN Framework

2. GNN使用PyG(PyTorch第三方库)的实现

torch_geometric 官方文档

 1. Message Passing

 继承MessagePassing基类的GCNConv

 2. 节点/边表征以及图表征

node /edge level:

graph level:节点嵌入——节点表征——图池化——图的表征——线性变换

【Zachary‘s karate club network数据集进行PyG小白入门实战 】 

1. 数据集

2. 数据集展示(使用networkx可视化展示) 

3. Graph Neural Network网络定义

4. 输出特征展示

5. 训练模型(Semi-supervised)

【一些PyG的数据类型】

Data 类(PyG的数据结构)

Batch类【详情可参考】

batch属性说明

3. 生化任务的一些第三方包

rdkit 官方文档

1. 如何画分子&一些分子操作方法的引入

Drawing Molecules

Some Simple Calculation

2. 子结构搜索&分子指纹

4. 引用

️一些视频【强烈安利】

其他


1. 预备知识

GNN Framework

GNN可以被抽象为Aggregate和两个步骤,分为MPNN(Messege Passing Neural Networks)和NLNN(Non-local Neural Networks)    

GNN在生化任务上的工程实现学习笔记_第1张图片

  • 本文主要focus在MPNN上    

    GNN在生化任务上的工程实现学习笔记_第2张图片

(1)公式中h代表各个层中某个节点的隐向量,它是随机初始化的(或者加入原有节点特征)。a是某个节点i,对于他的邻居j们,通过某个聚合函数(sum、average...)aggregate获得的消息(在代码中可能会使用torch_scatter.scatter 做scatter_sum/scatter_mean运算)

通过某个函数将(l-1)层的隐向量和(l)层的消息combine(消息传递框架中也叫做update),就能够获得(l)层的隐向量。

此外,仅仅获得每个节点的隐向量还不足以表征整个图,所以需要一个图读出(Readout)函数来将很多节点的隐向量表征为一个统一长度的向量表示

这个readout function可以是对各个节点的隐向量做max pooling、average pooling获得特征(graph pooling操作:在代码中可能会使用torch_scatter.scatter 做scatter_sum/scatter_mean运算);也可以是引入一个上帝节点连接每个图中的节点,取最后一层中上帝节点的隐向量作为表征;还可以是differentiable hierarchical pooling、Edge Pooling、Set2Set等方法。

广义角度上的理解,可参考此文:图机器学习——5.5 广义 GNN 框架:消息传递与聚合_Kanny广小隶的博客-CSDN博客_gnn聚合5. 广义视角下的 GNN 框架GNN 层的本质为:消息(Message) + 聚合(Aggregation)。在这一视角下的有许多不同的实例:GCN,GraphSAGE,GAT等,下面我们将着重讲解这三个不同的实例。GNN 的总体框架分别包括:1)消息 与 2)聚合,也就是一个网络层中的操作。3)层与层之间的连接4)图增广(Graph augmentation) 与 5)学习目标(Learning Object)下面我们一部分一部分进行分析。1)消息(Message)图网络中的https://kanny.blog.csdn.net/article/details/124929624?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2~default~CTRLIST~Rate-1-124929624-blog-118027083.pc_relevant_aa_2&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2~default~CTRLIST~Rate-1-124929624-blog-118027083.pc_relevant_aa_2&utm_relevant_index=1


2. GNN使用PyG(PyTorch第三方库)的实现

torch_geometric 官方文档

torch_geometric — pytorch_geometric documentationhttps://pytorch-geometric.readthedocs.io/en/latest/modules/root.html

PyTorch Geometric,简称Pyg。Pyg库包含易用的小批量加载器(mini-batch loader)、多GPU支持、大量常见基准数据集和有用的变换,适用于任意图像、三维网格(3D mesh)和点云。基于PyTorch构建,用于处理不规则结构化输入数据(如图、点云、流形)。除了一般的图形数据结构和处理方法外,它还包含从关系学习到3D数据处理等领域中最新发布的多种方法。通过利用稀疏 GPU 加速、提供专用的 CUDA 内核以及为不同大小的输入样本引入高效的小批量处理,PyTorch Geometric 实现了很高的数据吞吐量
 

安装踩坑过后的友情提示:建议严格参考官方GitHub安装说明!!!

(踩坑解决详情见4的其他部分)GitHub - pyg-team/pytorch_geometric: Graph Neural Network Library for PyTorchGraph Neural Network Library for PyTorch. Contribute to pyg-team/pytorch_geometric development by creating an account on GitHub.https://github.com/pyg-team/pytorch_geometric

 1. Message Passing

消息传递范式是一种聚合邻接节点信息(称为邻域聚合,节点的邻居的消息聚合到节点本身更新中心节点信息的范式,它将卷积算子推广到了不规则数据领域,实现了图与神经网络的连接。消息传递的详情可参考,讲得很清晰:图神经网络task2——消息传递_Wmnyang的博客-CSDN博客这是目录1、消息传递范式2、MessagePassing基类2.1、MP类和基本方法3、MessagePassing实例3.1、三个函数的数学定义3.2、`GCNConv`实现步骤(1)向邻接矩阵添加自环边(2)对节点的特征矩阵进行线性变换(3)对变换后的节点特征进行标准化(4)归一化j中的节点特征(5)将节点特征求和参考文献1、消息传递范式消息传递是实现GNN的一种通用框架和编程范式。它从聚合与更新的角度归纳总结了多种GNN模型的实现,它的思路是:首先结合边的特征以及和边相连的两个节点的特征,得到 https://blog.csdn.net/Wmnyang/article/details/118048781

该范式包含这样三个步骤:

  1. 邻接节点信息变换
  2. 邻接节点信息聚合到中心节点
  3. 聚合信息变换

基于此范式,我们可以定义聚合邻接节点信息生成中心节点表征图神经网络。在PyG中,MessagePassing基类是所有基于消息传递范式的图神经网络的基类,它大大地方便了我们对图神经网络的构建。
 

【步骤】

  • 首先导入必要的包(from from torch_geometric.nn import MessagePassing)
  • 其次创建一个类,继承MessagePassing基类
class MessagePassing(aggr='add', flow='source_to_target', node_dim=0)

aggr: 定义要使用的聚合方案(“add”、“mean"或"max”)
flow: 定义消息传递的流向(“source_to_target"或"target_to_source”)
node_dim: 定义沿着哪个轴线传播
 

  • 然后初始化__init()__,定义forward()方法用于模型的表示
  • 接着可以覆写这几个函数:
  1. message()
  2. aggregate()(查看MessagePassing的源码,可以看到其aggregate函数的定义(在PyG中是通过scatter函数来实现上述过程)PyG教程(7):剖析邻域聚合-pudn.com
  3. message_and_aggregate()方法
  4. update()方法

 继承MessagePassing基类的GCNConv

数学定义以及PyG实现-GCNConv

  • 数学定义

GNN在生化任务上的工程实现学习笔记_第3张图片

  • PyG实现
import torch
from torch_geometric.nn import MessagePassing
from torch_geometric.utils import add_self_loops, degree


class GCNConv(MessagePassing):
    def __init__(self, in_channels, out_channels):
        super(GCNConv, self).__init__(aggr='add', flow='source_to_target')
        # "Add" aggregation (Step 5).
        # flow='source_to_target' 表示消息从源节点传播到目标节点
        self.lin = torch.nn.Linear(in_channels, out_channels)

#所有逻辑在forward()方法中实现
    def forward(self, x, edge_index):
        # x has shape [N, in_channels]
        # edge_index has shape [2, E]

        # Step 1: Add self-loops to the adjacency matrix.
        # 使用torch_geometric.utils.add_self_loops() 给边索引添加自循环边【对应1. 向邻接矩阵添加自环边】
        edge_index, _ = add_self_loops(edge_index, num_nodes=x.size(0))

        # Step 2: Linearly transform node feature matrix.
        # torch.nn.Linear 线性变换【对应2. 对节点表征做线性变换】
        x = self.lin(x)

        # Step 3: Compute normalization.【对应3. 计算归一化系数】
        row, col = edge_index
        deg = degree(col, x.size(0), dtype=x.dtype)
        deg_inv_sqrt = deg.pow(-0.5)
        norm = deg_inv_sqrt[row] * deg_inv_sqrt[col]

        # Step 4-5: Start propagating messages.
        # progagate包含了(先调用message(),再aggregate,再update)
        return self.propagate(edge_index, x=x, norm=norm)
        #MessagePassing.propagate(edge_index, size=None, **kwargs):
        # 开始传播消息的起始调用。它以edge_index(边的端点的索引)和flow(消息的流向)以及一些额外的数据为参数,size=(N,M)设置对称邻接矩阵的形状。     

    def message(self, x_j, norm):
        # x_j has shape [E, out_channels]
        # Step 4: Normalize node features.
        return norm.view(-1, 1) * x_j

from torch_geometric.datasets import Planetoid
 
dataset = Planetoid(root='dataset/Cora', name='Cora')
data = dataset[0]
print(data.x)#node_feature
print(data.edge_index)#边索引
 
net = GCNConv(data.num_features, 64)
h_nodes = net(data.x, data.edge_index)
print(h_nodes.shape)

输出结果:

GNN在生化任务上的工程实现学习笔记_第4张图片

 2. 节点/边表征以及图表征

  • node /edge level:

图节点预测或边预测任务中,首先需要生成节点表征(Node Representation)。我们使用图神经网络来生成节点表征,并通过基于监督学习的对图神经网络的训练,使得图神经网络学会产生高质量的节点表征。高质量的节点表征能够用于衡量节点的相似性,同时高质量的节点表征也是准确分类节点的前提

  • graph level:

图表征学习要求根据节点属性、边和边的属性(如果有的话)生成一个向量作为图的表征,基于图表征我们可以做图的预测.

基于图同构网络(Graph Isomorphism Network, GIN)的图表征网络是当前最经典的图表征学习网络,为了得到图表征首先需要做节点表征,然后做图读出。GIN中节点表征的计算遵循WL Test算法中节点标签的更新方法,因此它的上界是WL Test算法。在图读出中,我们对所有的节点表征(加权,如果用Attention的话)求和,这会造成节点分布信息的丢失

详情可参考:https://blog.csdn.net/LuoMin2523/article/details/118464411?spm=1001.2014.3001.5502https://blog.csdn.net/LuoMin2523/article/details/118464411?spm=1001.2014.3001.5502

⚛️⚛️⚛️⚛️⚛️⚛️ 

  • 基于图同构网络的图表征模块(GINGraphRepr Module)

此模块首先采用GINNodeEmbedding模块对图上每一个节点做节点嵌入(Node Embedding),得到节点表征;然后对节点表征做图池化得到图的表征;最后用一层线性变换对图表征转换为对图的预测

节点嵌入——节点表征——图池化——图的表征——线性变换

  • 基于图同构网络的节点嵌入模块(GINNodeEmbedding Module) 

输入到此节点嵌入模块的节点属性为类别型向量。

步骤:

1)嵌入。用AtomEncoder对输入向量做嵌入得到第0层节点表征

2)计算节点表征

从第1层开始到第num_layers层逐层计算节点表征。(每一层节点表征的计算都以上一层的节点表征h_list[layer]边edge_index边的属性edge_attr输入

注意事项:GINConv的层数越多,此节点嵌入模块的感受野(receptive field)越大,结点i的表征最远能捕获到结点i的距离为num_layers的邻接节点的信息。

- 输入的边属性(edge_attr为类别型),先将类别型边属性转换为边表征,GINConv模块遵循:“消息传递,消息聚合,消息更新”这一过程。

  • AtomEncoder和BondEncoder

节点(原子)和边(化学键)的属性都为离散值,属于不同的空间。

所以通过Embedding,将节点属性和边属性分别映射到一个新的空间,在这个新的空间中对节点和边进行消息聚合。

注:节点属性有多少维,就需要多少个嵌入函数(通过调用 torch.nn.Embedding(dim,emb_dim))可以实例化一个嵌入函数

其中dim为:被嵌入数据可能取值的数量;emb_dim:要映射到的空间的维度。

得到的嵌入函数,接收一个x(0

在forward()函数中,我们对不同属性值得到的不同嵌入向量进行了相加操作,实现了节点不同属性融合在一起(消息聚合)


【Zachary‘s karate club network数据集进行PyG小白入门实战 】 

数据集——数据集展示——GCN网络定义——输入特征展示——训练模型

1. 数据集

from torch_geometric.datasets import KarateClub

dataset = KarateClub()

print(f'Dataset:{dataset}:')
print('===================')
print(f'Number of graphs:{len(dataset)}')
print(f'Number of features:{dataset.num_features}')
print(f'Number of classes:{dataset.num_classes}')

'''
Dataset:KarateClub():
===================
Number of graphs:1 #只有一个图,对点做分类
Number of features:34 #每一个点有34个特征
Number of classes:4   #每个点做4分类
'''

dataset[0]
'''
Data(x=[34, 34], edge_index=[2, 156], y=[34], train_mask=[34])
'''

edge_index = dataset[0].edge_index
print(edge_index.t())

'''
tensor([[ 0,  1],
        [ 0,  2],
        [ 0,  3],
        [ 0,  4],
        [ 0,  5],
        [ 0,  6],
        [ 0,  7],
        [ 0,  8],
        [ 0, 10],
        [ 0, 11],
        [ 0, 12],
        [ 0, 13],
        [ 0, 17],
        [ 0, 19],
        [ 0, 21],
        [ 0, 31],
        [ 1,  0],
        [ 1,  2],
        [ 1,  3],
        [ 1,  7],
        [ 1, 13],
        [ 1, 17],
        [ 1, 19],
        [ 1, 21],
        [ 1, 30],
...
        [33, 29],
        [33, 30],
        [33, 31],
        [33, 32]])
'''

 图的表示用Data格式

  1. 其中上述的 x=[34,34],34*34(M×F——M:为样本的个数;F:每个样本的特征维度)
  2. edge_index=[2, 156] (edge_index:表示图的连接关系(start,end两个序列))

    GNN在生化任务上的工程实现学习笔记_第5张图片

    edge_index 表示是稀疏的,可以看做邻接矩阵,但并不是传统意义上的n*n的邻接矩阵(而是[2,边的个数])
  3. y=[34] 标签(34个node)
  4. train_mask=[34] 哪些点是有标签的,哪些点时无标签的(有的节点木有标签,用来表示哪些节点要计算损失)

torch_geometric.nn:是可以调用的一些层 

torch_geometric.data:是可以调用的一些数据(数据结构)

torch_geometric.datasets:是可以调用的一些数据集

torch_geometric.utils:是可以调用的一些基本处理的函数

2. 数据集展示(使用networkx可视化展示) 

import matplotlib.pyplot as plt
import networkx as nx

def visualize_graph(G,color):
    plt.figure(figsize=(7,7))
    plt.xticks([])
    plt.yticks([])
    nx.draw_networkx(G,pos=nx.spring_layout(G,seed=42),with_labels=False,node_color=color,cmap="Set2")
    plt.show()

from torch_geometric.utils import to_networkx

data = dataset[0]
G = to_networkx(data,to_undirected=True)
visualize_graph(G,color=data.y)

GNN在生化任务上的工程实现学习笔记_第6张图片

 很明显classes=4,即有4个不同的类别

3. Graph Neural Network网络定义

GCN layer(与上述 继承MessagePassing基类的GCNConv 略有不同)

import torch
from torch.nn import Linear
from torch_geometric.nn import GCNConv

class GCN(torch.nn.Module):
    def __init__(self):
        super().__init__()
        torch.manual_seed(1234)#设置随机种子是为了确保每次生成固定的随机数
        # 定义输入特征和输出特征即可

        self.conv1 = GCNConv(dataset.num_features,4)#dataset.num_features:34维向量,经过这一层得到4维向量
        self.conv2 = GCNConv(4,4)
        self.conv3 = GCNConv(4,2)

        #连接全连接
        self.classifier = Linear(2,dataset.num_classes)# 4个类别概率值

    def forward(self,x,edge_index):
        h = self.conv1(x,edge_index)#输入特征与邻接矩阵  x:图(有每个点的特征),edge_index(邻接矩阵)
        h = h.tanh()
        h = self.conv2(h,edge_index)
        h = h.tanh()
        h = self.conv3(h,edge_index)
        h = h.tanh()

        #分类层,即FC
        out = self.classifier(h)

        return out,h #out为输出结果,h为得到的中间结果【self.conv3 = GCNConv(4,2)】,即两维向量
model = GCN()
print(model)

'''
GCN(
  (conv1): GCNConv(34, 4)
  (conv2): GCNConv(4, 4)
  (conv3): GCNConv(4, 2)
  (classifier): Linear(in_features=2, out_features=4, bias=True)
)
'''

GNN在生化任务上的工程实现学习笔记_第7张图片

 4. 输出特征展示

def visualize_embedding(h,color,epoch=None,loss=None):
    plt.figure(figsize=(7,7))
    plt.xticks([])
    plt.yticks([])
    #detach(): 返回一个新的Tensor,但返回的结果是没有梯度的。cpu():把gpu上的数据转到cpu上。numpy():将tensor格式转为numpy
    h = h.detach().cpu().numpy()
    plt.scatter(h[:,0],h[:,1],s=140,c=color,cmap="Set2")
    if epoch is not None and loss is not None:
        plt.xlabel(f"Epoch:{epoch},Loss:{loss.item():.4f}",fontsize=16)
    plt.show()


# 展示输出的两维特征(还未训练前)
model = GCN()
_, h = model(data.x,data.edge_index)
print(f'Embedding Shape:{list(h.shape)}') #Embedding Shape:[34, 2]
visualize_embedding(h,color=data.y)

GNN在生化任务上的工程实现学习笔记_第8张图片

上图为随机初始化的两维向量

  5. 训练模型(Semi-supervised)

import time

model = GCN()
criterion = torch.nn.CrossEntropyLoss() #define loss criterion 分类损失函数
optimizer = torch.optim.Adam(model.parameters(),lr=0.01)#优化器

#train
def train(data):
    optimizer.zero_grad()#梯度清零
    out,h = model(data.x,data.edge_index)#h为两维向量主要是为了展示画图
    loss = criterion(out[data.train_mask],data.y[data.train_mask])#data.train_mask 即看train_mask为True的,即只看有标签的结点
    #semi-supervised:只关注有标签的那些
    loss.backward()#反向传播
    optimizer.step()#参数更新
    return loss,h

#迭代
for epoch in range(401):#1个epoch即1个图
    loss,h = train(data)
    #每10个epoch去画一个图
    if epoch%10 ==0:
        visualize_embedding(h,color=data.y,epoch=epoch,loss=loss)
        time.sleep(0.3) #让画停一下

GNN在生化任务上的工程实现学习笔记_第9张图片 向量聚堆的感觉,特征分类示意图 

【一些PyG的数据类型】

Data 类(PyG的数据结构)

GNN在生化任务上的工程实现学习笔记_第10张图片

一些data的方法和属性补充:

  • data.keys:返回属性名列表。
  • data['x']:返回属性名为'x'的值。
  • for key, item in data: ...:按照字典的方法返回data属性和对应值。
  • 'x' in data:判断某一属性是否在data中。
  • data.num_nodes:返回节点个数,相当于x.shape[0]
  • data.num_edges:返回边的条数,相当于edge_index.shape[1]
  • data.contains_isolated_nodes():是否存在孤立的节点。
  • data.contains_self_loops():是否存在自环。
  • data.is_directed():是否是有向图。
  • data.to(torch.device('cuda')):将数据对象转移到GPU。

Common Benchmark Datasets-PyG包含了很多公用的数据集,所有Planetoid数据集(Cora, Citeseer, Pubmed)。来自TUDataset | TUD Benchmark datasets的所有图分类数据集,和其简洁版,QM7和QM9数据集等等。

Batch类【详情可参考】

番外篇:PyG框架及Cora数据集简介 - 知乎由于GNN的输入是不规则的graph,所以在训练中无法方便地像image或text输入数据,该篇文章介绍PyG框架,介绍部分基本上来自于PyG的文档的前面两节。介绍Cora数据集是因为在该篇之后会单独再发一篇使用PyG框架及原生…https://zhuanlan.zhihu.com/p/78452993?from_voters_page=true

Mini-Batches(Batch类是继承Data的,是自动生成的)

神经网络通常以批量方式进行训练。 PyG 通过创建稀疏块对角邻接矩阵由 edge_index 定义)并在节点维度上连接特征和目标矩阵来实现小批量的并行化。 这种设计使得我们可以将不同规模的图放在同一个batch中

PyTorch Geometric中采用的将多个图封装成批的方式是:将小图作为连通组件(connected component)的形式合并,构建一个大图。于是小图的邻接矩阵存储在大图邻接矩阵的对角线上。大图的邻接矩阵、属性矩阵、预测目标矩阵分别为:

GNN在生化任务上的工程实现学习笔记_第11张图片

注意,邻接矩阵没有额外的内存开销,因为它们是以稀疏的方式保存的,只保留非零项,即边。

  • 通过torch_geometric.data.DataLoader类,多个小图被封装成一个大图。 torch_geometric.data.DataLoader是PyTorch的DataLoader的子类,它覆盖了collate()函数(该函数定义了一列表的样本是如何封装成批的)。因此,所有可以传递给PyTorch DataLoader的参数也可以传递给PyTorch Geometric的 DataLoader,例如,num_workers。
  • torch_geometric.data.Batch类继承自torch_geometric.data.Data多出了一个batch属性。batch属性为一个用于标记每一个来自于哪张图的列向量。

ENZYMES数据集(包含600个图,分为6类)为例做介绍

  • ENZYMES数据集

GNN在生化任务上的工程实现学习笔记_第12张图片

GNN在生化任务上的工程实现学习笔记_第13张图片

注:上图黄字说明——第一个graph包含有37个节点,每个节点特征维度为3,有 1682=84 条无向边,并且该graph的标签(类别)的维度为1。 

GNN在生化任务上的工程实现学习笔记_第14张图片

  •  Batch
#加载ENZYMES数据集

from torch_geometric.datasets import TUDataset
from torch_geometric.loader import DataLoader

dataset = TUDataset(root='/tmp/ENZYMES', name='ENZYMES', use_node_attr=True)
loader = DataLoader(dataset, batch_size=4, shuffle=True)

#获取一个batch
batch = loader.__iter__().next()
print(batch)

GNN在生化任务上的工程实现学习笔记_第15张图片

 batch属性说明

1. ptr属性

如上图所示batch['ptr']的结果为:tensor([ 0, 20, 62, 81, 130])

for i in range(batch.num_graphs): #num_graphs:4
    print(batch[i])
#batch['ptr']:tensor([  0,  20,  62,  81, 130])
'''
Data(edge_index=[2, 82], x=[20, 21], y=[1])——batch[0]就是[0:20] 20-0=20
Data(edge_index=[2, 176], x=[42, 21], y=[1])——batch[1]就是[20:62] 62-20=42 
Data(edge_index=[2, 78], x=[19, 21], y=[1])——batch[2]就是[62:81] 81-62=19 
Data(edge_index=[2, 188], x=[49, 21], y=[1])——batch[3]就是[81:130] 130-81=49 
'''

GNN在生化任务上的工程实现学习笔记_第16张图片

2. batch属性

#加载ENZYMES数据集
from torch_geometric.datasets import TUDataset
from torch_geometric.loader import DataLoader

dataset = TUDataset(root='/tmp/ENZYMES', name='ENZYMES', use_node_attr=True)
## 每次加载4个数据到内存中,loader = DataLoader(dataset, batch_size=4, shuffle=True)
loader = DataLoader(
    # 要加载的数据集
    dataset=dataset,
    # ENZYMES包含600个图
    # 每次加载4个
    batch_size=4,
    # 每次加入进来之后是否随机打乱数据(可以增加模型的泛化性)
    shuffle=True
)

batch = loader.__iter__().next()
print(batch)
print(batch['ptr'])
print(batch.batch)
print((batch.batch==0).sum())
print((batch.batch==1).sum())
# edge_index:连接的边
#x:整个batch的节点特征矩阵
#y:graph标签
#batch:列向量,用于指示每个节点属于batch中的第几个graph
#ptr: pointer record 第几个graph起始的指针位置
'''
DataBatch(edge_index=[2, 498], x=[124, 21], y=[4], batch=[124], ptr=[5])
tensor([  0,  27,  49, 108, 124])
tensor([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
        1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
        2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
        2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
        3, 3, 3, 3])
tensor(27) #第一个graph graph0有27个图
tensor(22) #第二个graph graph1有49-27=22个图
'''

为什么要获取一个batch?

GNN在生化任务上的工程实现学习笔记_第17张图片

【Reminder】

GNN在生化任务上的工程实现学习笔记_第18张图片

  • parser.py 中的batch_size是决定从比如ENZYMES数据集中抽多少个graph出来
  • batch[0]的graph都一样(其SMILES分子式也是一样的)

3. 生化任务的一些第三方包

分子和蛋白质是可以用图来表示的结构化实体。原子或残基作为节点,化学键或链作为边。

rdkit 官方文档

RDKit Cookbook — The RDKit 2022.03.1 documentationhttps://www.rdkit.org/docs/Cookbook.html

RDKit是一个专门处理化学分子数据的包,可以用来读取分子文件smiles,提取分子的各种特征。(比如读取toxcast.csv文件里面的smiles,然后将smiles转成RDKit的molecules)

  • 基于RDKit提取molecules的node和edge特征,最后得到一个Graph(这个Graph可以使用PyG来表示)

1. 如何画分子&一些分子操作方法的引入

  • Drawing Molecules

from rdkit import Chem
from rdkit.Chem.Draw import IPythonConsole
from rdkit.Chem import Draw

IPythonConsole.ipython_useSVG=False #如果想展示PNG请设置为FALSE
  • mol对象是RDKit的特殊的对象,专门用于保存化学分子的。
  • 下面两幅图展示了如何画一个分子结构式以及对有原子编号的分子结构式
  • 注:画的这个图是 a kinase inhibitor(激酶抑制剂)

GNN在生化任务上的工程实现学习笔记_第19张图片

GNN在生化任务上的工程实现学习笔记_第20张图片

【Code】 

#将分子中的原子都标记上原子编号

def mol_with_atom_index(mol):
    for atom in mol.GetAtoms():#返回原子atom对象组成的列表
        atom.SetAtomMapNum(atom.GetIdx())#返回原子编号
    return mol

#Chem.MolFromSmiles()输入的字符串是SMILES(用于表示分子的一种方法),得到的是mol对象

mol = Chem.MolFromSmiles("C1CC2=C3C(=CC=C2)C(=CN3C1)[C@H]4[C@@H](C(=O)NC4=O)C5=CNC6=CC=CC=C65")

# With atom index
mol_with_atom_index(mol)

添加原子索引的更简单方法是调整 IPythonConsole 属性。 这会产生与上面示例类似的图像,不同之处在于原子索引现在靠近原子,而不是原子位置。

GNN在生化任务上的工程实现学习笔记_第21张图片

【Code】  

from rdkit import Chem
from rdkit.Chem.Draw import IPythonConsole
from rdkit.Chem import Draw

IPythonConsole.drawOptions.addAtomIndices = True
IPythonConsole.molSize = 300,300

mol = Chem.MolFromSmiles("C1CC2=C3C(=CC=C2)C(=CN3C1)[C@H]4[C@@H](C(=O)NC4=O)C5=CNC6=CC=CC=C65")
mol
from rdkit import Chem

mol = Chem.MolFromSmiles("C1CC2=C3C(=CC=C2)C(=CN3C1)[C@H]4[C@@H](C(=O)NC4=O)C5=CNC6=CC=CC=C65")

atom0 = mol.GetAtomWithIdx(0)

atom0 #返回结果:

- 注:分子分为原子和键

  1. mol对象中获取原子的方法GetAtoms(),返回为一个list
  2. mol对象中获取所有键的方法GetBonds(),返回为一个list
  3. mol对象中根据原子编号获取键GetAtomWithIdx()
  4. mol对象中根据键的编号获取键GetBondWithIdx( )
  5. 如果导入的分子是有带有坐标信息的,可通过** mol.GetConformer().GetAtomPosition(n)** 获取坐标信息
  • 更多关于操作分子的方法,详见 基础教程 — RDKit 中文教程 2020.09 文档
分子操作
1. 原子信息

得到了atom对象,其常用的内置方法为:

  1. ‘GetAtomMapNum’: map id 原子smarts形式冒号后面的数字,如[N:4], map id 就是4。
  2. GetAtomicNum’: 获取原子对应的元素编号,如碳原子的AtomicNum就是6
  3. ‘GetBonds’: 该原子参与形成的键的列表。
  4. ‘GetFormalCharge’: 该原子的电荷
  5. GetChiralTag’:原子的手性信息
  6. GetDegree’: 原子的度,这里的度指的是这个原子参与形成键的数目
  7. GetIsAromatic’: 判断原子是否是芳香性原子
  8. GetIdx’: 获取原子的编号
  9. ‘GetNeighbors’: 获取相连的原子列表
  10. ‘GetSmarts’: 获取原子的Smarts形式
  11. ‘GetSymbol’:获取原子的元素符号
  12. IsInRing()判断原子是否在环上
  13. IsInRingSize(n): 判断原字是否在n-元环上
2. 键信息

得到键对象bond, 内置了大量的方法:

  1. GetBondType()获取键的类型
  2. ‘GetBeginAtom()’: 组成键的第一个原子
  3. GetBeginAtomIdx(): 键的第一个组成原子编号
  4. GetEndAtomIdx():键的第二个组成原子编号
  5. ‘GetEndAtom()’: 组成键的第二个原子
  6. IsInRing()’: 判断是否在环上
  7. ‘IsInRingSize(n)’:判断是否在n-元环上
  • Some Simple Calculation

计算原子电荷值 Gasteiger Charges

from rdkit import Chem
from rdkit.Chem import AllChem
from rdkit.Chem.Draw import IPythonConsole
IPythonConsole.molSize = 250,250

m = Chem.MolFromSmiles('c1ncncc1C(=O)[O-]')
# Gasteiger Charges:原子电荷
# 计算一个分子的Gasteiger电荷。 使用称为_GasteigerCharge的属性为每个Atom对象存储计算出的值。 可以通过将此值传递给相似度图来绘制电荷图。 
AllChem.ComputeGasteigerCharges(m)#计算各原子的Gasteiger Charges
m

AllChem.ComputeGasteigerCharges(m)

已经将m中每个原子的Gasteiger Charges已经计算出来了并保存在每一个原子_GasteigerCharge中;然后对原子逐个使用atom.SetProp(添加原子的性质,同步可以实现直接显示。

GNN在生化任务上的工程实现学习笔记_第22张图片

【Code】

#设置原子电荷的属性
for atom in m.GetAtoms():
    value = '%.2f'%(atom.GetDoubleProp('_GasteigerCharge'))
    atom.SetProp('atomNote',value) #设置原子的属性,设置完以后,会在mol中直接显示
    #这里要注意,不能直接使用atom.SetAtomMapNum(value),因为matom.SetAtomMapNum()用于添加原子编号,只能添加整数

#遍历每个原子并调用其属性值
for atom in m.GetAtoms():
    print(atom.GetProp('atomNote'))

2. 子结构搜索&分子指纹

  • 子结构的搜索可以使用SMARTS匹配符完成

Smiles 可以看成分子的1D形式,分子的平面结构可以看成分子的2D形式。

SMARTS 在子结构匹配、化学反应等方面发挥着重要作用。

(SMART编码是前面描述的SMILES语言的扩展,可以用来创建查询。可以将SMART模式看作类似于用于搜索文本的正则表达式(换一种说法就是,smart编码相当于对smiles编码的一种模糊搜索)) 常用的SMARTS知识:

  1. C c 大写小写C是不一样的,大写代表脂肪碳;小写代表芳香碳。
  2. 冒号后面的数字为Atom Map ID
  3. 羟基O的表示 []
  1. 判断是否有子结构m.HasSubstructMatch(patt)
  2. 获取第一个子结构对应的原子编号m.GetSubstructMatch(patt)——返回一个元组
  3. m.GetSubstructMatches(patt) 获取所有子结构的原子编号——返回元组表(内含有多个元组...)

以甲氧基苯为例,查找是否含有-OCH3,若有则显示其对应的原子编号。:

GNN在生化任务上的工程实现学习笔记_第23张图片

GNN在生化任务上的工程实现学习笔记_第24张图片

 【Code】

from rdkit import Chem
m = Chem.MolFromSmiles('c1ccccc1OC')
patt = Chem.MolFromSmarts('OC')
flag =m.HasSubstructMatch(patt)

if flag:
    print("molecu m contains group -OCH3")
    atomids = m.GetSubstructMatch(patt)
    print("matched atom id:",atomids)
else:
    print("molecu m don't contain group -OCH3")
  • 分子指纹(Molecular Fingerprint)

分子指纹就是一种分子的抽象表征,它将分子转化(编码)为一系列比特串(即比特向量,bit vector .),然后可以很容易地在分子之间进行比较,比较分子的相似性等。典型的流程是将提取分子的结构特征、然后哈希(Hashing)生成比特向量。

使用rdkit中的 from rdkit.Chem.Fingerprints import FingerprintMols

详情可参考:

化合物相似性搜索_RDKit | 基于分子指纹的分子相似性_weixin_39999859的博客-CSDN博客分子相似性: 相似性原理(similar property principle)指出,总体相似的分子应具有相似的生物活性。相似性评估 化合物的指纹对于使用计算机考虑化合物的相似性是必需的。已经提出了各种评估方法,但是最常用的评估方法称为“ Tanimoto系数 ”。使用以下等式从两个分子A和B的位阵列指纹计算Tanimoto系数:导入库import pandas as p...https://blog.csdn.net/weixin_39999859/article/details/112354718?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_baidulandingword~default-4-112354718-blog-97921745.pc_relevant_default&spm=1001.2101.3001.4242.3&utm_relevant_index=7


4. 引用

️一些视频【强烈安利】

  • 神经网络 (六)图神经网络 Graph Neural Network_哔哩哔哩_bilibili
  • 带你一天搞定图神经网络GNN!学不会up直接下跪!图神经网络快速入门教程(绝对通俗易懂!)(GNN/GCN入门实战(附课程源码)_哔哩哔哩_bilibili
  • 【从入门到精通】不愧是计算机博士唐宇迪带我半天就学会图神经网络PytorchGeometric实战_哔哩哔哩_bilibili
  • 【RDKit】【教程】Introduction to RDKit, Jan Jensen_哔哩哔哩_bilibili
  • RDkit:SMILES命名绘图_哔哩哔哩_bilibili

其他

  • Chem和AllChem的区别:

Chem : 负责基础常用的化学功能 ( 如 : 读写分子 , 子结构搜索 , 分子美化等 )
AllChem: 负责高级但不常用的化学功能 。

  • 安装踩坑解决方案:

- torch_sparse安装问题

  • 使用pip install torch_sparse有如下报错:

GNN在生化任务上的工程实现学习笔记_第25张图片

  • 解决方案:

装了torch_geometric之后装不上sparse的参考文章:torch-sparse安装教程_机器不学习我学习的博客-CSDN博客_torch_sparse

失败原因:

应该是库和库之间依赖的问题,所以torch版本要和geometric还有sparse等module都要对应起来!

torch_geometric、 torch_sparse、 torch_scatter、 torch_cluster是一个大佬根据根据pytorch版本和一些bug修复持续更新的,因此依赖关系比较严重,根据发布的版本应该对应起来进行安装使用。

  • https://github.com/rusty1s/pytorch_cluster/releases
  • Releases · pyg-team/pytorch_geometric · GitHub
  • https://github.com/rusty1s/pytorch_sparse/releases
  • https://github.com/rusty1s/pytorch_scatter/releases

1. python——import torch 查看torch版本和the CUDA version PyTorch

GNN在生化任务上的工程实现学习笔记_第26张图片

2. 按照这个Installation — pytorch_geometric documentation安装

安装命令为下图蓝色highlight所示

GNN在生化任务上的工程实现学习笔记_第27张图片

你可能感兴趣的:(GNN,人工智能,神经网络,pytorch)