深度学习(36)—— 图神经网络GNN(1)
这个系列的所有代码我都会放在git上,欢迎造访
文章目录
- 深度学习(36)—— 图神经网络GNN(1)
- 1. 基础知识
- 2.使用场景
- 3. 图卷积神经网络GCN
- (1)基本思想
- 4. GNN基本框架——pytorch_geometric
- (1)数据
- (2)可视化
- (3)网络定义
- (4)训练模型(semi-supervised)
1. 基础知识
-
GNN考虑的事当前的点和周围点之间的关系
-
邻接矩阵是对称的稀疏矩阵,表示图中各个点之间的关系
-
图神经网络的输入是每个节点的特征和邻接矩阵
-
文本数据可以用图的形式表示吗?
文本数据也可以表示图的形式,邻接矩阵表示连接关系 -
邻接矩阵中并不是一个N* N的矩阵,而是一个source,target的2* N的矩阵
-
信息传递神经网络:
每个点的特征如何更新??
——考虑他们的邻居,更新的方式可以自己设置:最大,最小,平均,求和等 -
GNN可以有多层,图的结构不发生改变,即当前点所连接的点不发生改变(邻接矩阵不发生变化)【卷积中存在感受野的概念,在GNN中同样存在,GNN的感受野也随着层数的增大变大】
-
GNN输出的特征可以干什么?
- 各个节点的特征组合,对图分类【graph级别任务】
- 对各个节点分类【node级别任务】
- 对边分类【edge级别任务】
利用图结构得到特征,最终做什么自定义!
2.使用场景
为什么CV和NLP中不用GNN?
因为图像和文本的数据格式很固定,传统神经网络格式是固定的,输入的东西格式是固定的- 化学、医疗
- 分子、原子结构
- 药物靶点
- 道路交通,动态流量预测
- 社交网络——研究人
GNN输入格式比较随意,是不规则的数据结构, 主要用于输入数据不规则的时候
3. 图卷积神经网络GCN
图卷积和卷积完全不同
- GCN不是单纯的有监督学习,多数是半监督,有的点是没有标签的,在计算损失的时候只考虑有标签的点。针对数据量少的情况也可以训练
(1)基本思想
- 网络层次:第一层对于每个点都要做更新,最后输出每个点对应的特征向量【一般不会做特别深层的】
- 图中的基本组成:G(原图)A(邻接)D(度)F(特征)
- 度矩阵的倒数* 邻接矩阵 *度矩阵的倒数——>得到新的邻接矩阵【左乘对行做归一化,右乘对列做归一化】
- 两到三层即可,太多效果不佳
4. GNN基本框架——pytorch_geometric
它实现了各种GNN的方法
注意:安装过程中不要pip install,会失败!根据自己的device和python版本去下载scatter,pattern等四个依赖,先安装他们然后再pip install torch_geometric==2.0
这里记得是2.0版本否则会出现 TypeError: Expected ‘Iterator‘ as the return annotation for __iter__
of SMILESParser, but found ty
献上github地址:这里
下面是一个demo
(1)数据
这里使用的是和这个package提供的数据,具体参考:club
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}')
data = dataset[0] # Get the first graph object.
在torch_geometric中图用Data的格式,Data的对象:可以在文档中详细了解
其中的属性
- edge_index:表示图的连接关系(start,end两个序列)
- node features:每个点的特征
- node labels:每个点的标签
- train_mask:有的节点没有标签(用来表示哪些节点要计算损失)
(2)可视化
from torch_geometric.utils import to_networkx
G = to_networkx(data, to_undirected=True)
visualize_graph(G, color=data.y)
(3)网络定义
GCN layer的定义:
可以在官网的文档做详细了解
卷积层就有很多了:
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) # 只需定义好输入特征和输出特征即可
self.conv2 = GCNConv(4, 4)
self.conv3 = GCNConv(4, 2)
self.classifier = Linear(2, dataset.num_classes)
def forward(self, x, edge_index):
h = self.conv1(x, edge_index) # 输入特征与邻接矩阵(注意格式,上面那种)
h = h.tanh()
h = self.conv2(h, edge_index)
h = h.tanh()
h = self.conv3(h, edge_index)
h = h.tanh()
# 分类层
out = self.classifier(h)
return out, h
model = GCN()
print(model)
_, h = model(data.x, data.edge_index)
print(f'Embedding shape: {list(h.shape)}')# 输出最后分类前的中间特征shape
visualize_embedding(h, color=data.y)
这时很分散
(4)训练模型(semi-supervised)
import time
model = GCN()
criterion = torch.nn.CrossEntropyLoss() # Define loss criterion.
optimizer = torch.optim.Adam(model.parameters(), lr=0.01) # Define optimizer.
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]) # semi-supervised
loss.backward()
optimizer.step()
return loss, h
for epoch in range(401):
loss, h = train(data)
if epoch % 10 == 0:
visualize_embedding(h, color=data.y, epoch=epoch, loss=loss)
time.sleep(0.3)
然后就可以看到一系列图,看点的变化情况了