文章目录
- 0 提出背景
- 1 网络结构
- 2 GNN算法
- 2.1 算法描述
- 2.2 举个栗子
- 3 GNN本质
- 4 应用领域
- 5 代码案例
- 5.1 PyG的下载
- 5.2 常用数据集介绍
- 5.3 one demo
0 提出背景
经典的深度神经网络适用于 欧几里得数据(Euclidean data),比如我们常常用卷积神经网络来处理图像这种2维矩阵数据,用循环神经网络来处于文本这种1维向量数据。
but 现实世界中常常有许多 非欧几里得数据(non-Euclidean data),如:社交网络、蛋白质网络、推荐系统、食物链等一些 图(Graph) 类型数据。它们没有传统意义上的上下左右前后之分,难以用方方正正的矩阵或向量来表示。
为了解决对非欧几里得数据的学习问题,图神经网络应运而生。
1 网络结构
GNN以图数据作为输入,经过多层图卷积、激活、正则等各种操作,输出下游各种任务的预测结果。
常见的下游任务有:
- 节点分类: 预测某一节点的类型
- 边预测: 预测两个节点之间是否存在边、存在什么类型的边
- 社区预测: 识别密集连接的节点所形成的簇(子图)
- 网络相似性: 两个子图是否相似
2 GNN算法
2.1 算法描述
Main函数:
FORWARD函数:
BACKWARD(x,w)
2.2 举个栗子
举个例子来简单说明上述算法:
-
初始化节点特征: 上图中节点特征向量(1,1,1,1,1)、 (2,2,2,2,2) 等,可以是提取到的,也可以是标签。如果没有标签也没有特征,就随机初始化一个特征向量,做为可训练的变量,参与后续的训练。
-
执行聚合计算: 将邻居的信息结合到自己身上,作为自己特征的补充。
以A节点为例,邻居信息N = a∗(2,2,2,2,2) + b∗(3,3,3,3,3) + c∗(4,4,4,4,4),其中a,b,c是边的权重,假如b对a很重要,则a的值就可以设置的高一些,假如c对a不是很重要,则c的值就可以设置的低一些。
-
执行参数更新: A节点的总特征,就是自己的特征 加上α倍的邻居信息N,再乘权重W,再经过一个激活函数σ,最终得到的是经过一层GNN操作之后A的最终信息。
以A节点为例,A的信息=σ (W((1,1,1,1,1)+α∗N))
其中,σ为激活函数 (relu,sigmoid等),W是模型需要训练的参数。 -
重复上述操作: 第一次聚合后,A中有它的邻居B,C,D的信息;B中有A,C的信息;C中有A,B,D,E的信息;D中有A,C的信息;E中有C的信息。第二次聚合以此类推。GNN层数越多,GNN的“感受野”越大,每个点考虑其他点的信息越多,考虑越全面。
以A节点为例,此时A聚合C的时候,由于C中有上一层聚合得到的E的信息,这时A获得了二阶邻居E的特征。
3 GNN本质
归根到底,GNN就是一个提取特征的方法。
通过重复的聚合更新运算,我们能够得到每个节点的嵌入表达,也就是节点最终的特征向量。
4 应用领域
1. 知识图谱
- 问答系统
- 推理
2. 推荐系统
- 个性化推荐
- 协同过滤
3. 生物学、化学
- 蛋白质结构预测
- 化学分子结构预测
4. 交通与物流
- 交通流量预测
- 路径规划
- 物流调度
5 代码案例
5.1 PyG的下载
不建议pip install torch_geometric直接下载,后续可能会出一些问题…
可以先把一些文件下载到本地,再执行pip install +本地地址,链接如下:
pyg-team/pytorch_geometric: Graph Neural Network Library for PyTorch (github.com)
比如我的torch版本2.3.1 python版本3.9,那我就需要下载以下几个文件:
对上面每个文件执行以下命令,只举一例说明:
最后:
到这里,PyG的安装就ok了。
5.2 常用数据集介绍
torch_geometric.datasets — pytorch_geometric 文档 (pytorch-geometric.readthedocs.io)
本次demo使用第一个数据集KarateClub
5.3 one demo
%matplotlib inline
import torch
import networkx as nx
import matplotlib.pyplot as plt
from torch_geometric.datasets import KarateClub
from torch_geometric.utils import to_networkx
from torch.nn import Linear
from torch_geometric.nn import GCNConv
import timedef visualize_graph(G,color):plt.figure(figsize=(8,8))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()def visualize_embedding(h, color, epoch=None, loss=None):plt.figure(figsize=(8,8))plt.xticks([])plt.yticks([])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()dataset = KarateClub()
print(f'Dataset:{dataset}')
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] # 获得第一个图
# x=[节点数,节点特征维度];
# edge_index=[2,边数];
# y=[边数,边特征维度];
# train_mask=[节点数],是一个布尔型张量,指示哪些节点参与训练。
print(data) G = to_networkx(data, to_undirected=True)
visualize_graph(G, color=data.y)# 一个简单的图神经网络定义
class GCN(torch.nn.Module):def __init__(self):super().__init__()torch.manual_seed(66)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, hmodel = GCN()
print(model)_,h = model(data.x, data.edge_index)
print(f'Embedding shape:{list(h.shape)}')
visualize_embedding(h, color=data.y)criterion = torch.nn.CrossEntropyLoss() # 损失函数
optimizer = torch.optim.Adam(model.parameters(), lr=0.01) # 优化器def train(data):optimizer.zero_grad()out, h = model(data.x, data.edge_index)loss = criterion(out[data.train_mask], data.y[data.train_mask])loss.backward()optimizer.step()return loss, hfor epoch in range(501):loss, h = train(data)if epoch%10 == 0:visualize_embedding(h, color =data.y, epoch=epoch, loss=loss)time.sleep(0.3)
可视化图结构:
可视化图嵌入结构:
训练过程:
epoch0
…
epoch500
结论:随着学习过程的进行,网络逐渐优化节点的嵌入表示,损失不断减小,分类更加准确。