前面讲解了图卷积的基本理论知识( 链接.),接下来讲解基于PyG库的图卷积的节点分类。
需要说明的是,我是用的是win10系统。
目录
- 一、环境搭建
- 1、PyG库简介
- 2、安装Anaconda并配置虚拟环境
- 3、安装PyTorch和PyG
- (1)安装PyTorch
- (2)安装PyGeometric
- 二、节点分类实战
- 1、GCN模型: Y = A ^ W X Y=\hat AWX Y=A^WX
- 2、GAT模型
PyG的全称是 PyTorch Geometric,是一款基于 PyTorch 的几何深度学习框架,可以简单方便的实现图神经网络,也就是说,PyG里面封装了各种方法,比如常见的各种图卷积,直接调用即可。当然在学习PyG之前,要对PyTorch有一定的了解。
PyG官方文档: https://pytorch-geometric.readthedocs.io/en/latest/notes/installation.html.
项目链接: https://github.com/rusty1s/pytorch_geometric.
官网下载Anaconda: https://www.anaconda.com/products/individual.
然后根据教程: https://zhuanlan.zhihu.com/p/61639212.安装即可。
注意,需要添加环境变量,下面的这两点是Linxu上的添加环境变量:
export PATH=/home/你的用户名/anaconda3/bin:$PATH
再更新一下环境变量:
source ~/.bashrc
安装完Anaconda之后,我们添加一些设置,为以后安装别的包方便。
首先打开Anaconda Prompt。
(1)添加清华镜像包管理:
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/
添加之后,在安装包的时候将地址展示出来,方便我们查看:
conda config --set show_channel_urls yes
如果不想用添加的源,使用下面的命令删除就行了。
conda config --remove-key channels
(2)conda配置虚拟环境
我们为每个项目创建一个虚拟环境,虽然麻烦,但是这样比较清晰,也方便管理,会减少一些不必要的问题。
创建环境:(这个项目使用的是python3.6)
conda create -n env_name python=3.x
删除环境:
conda remove -n env_name --all
激活环境:
activate env_name # 这个是windows下的
source activate env_name # 这个是linux下的
退出环境:
deactivate env_name # 这个是windows下的
source deactivate env_name # 这个是linux下的
在安装之前,需要看看对应的版本(去前面给的项目链接看)。
PyTorch 1.5.0对应如下:
PyTorch 1.4.0对应如下:
这个项目使用如下版本:
PyTorch=1.4.0、torchvision=0.5.0、cudatoolkit=10.1、cudnn=7.6.3
(cudnn基于GPU的加速库,也就是有了它才能在GPU上加速)
然后去PyTorch官网,复制安装命令,如下所示:
需要说明的是 -c pytorch 的意思就是说不用我们设置的清华镜像,而是去pytorch官网下载。
由于要指定版本,所以最终使用的命令如下,需要说明的是需要进入前面创建的虚拟环境安装。
conda install pytorch=1.4 torchvision=0.5 cudatoolkit=10.1
然后测试是否安装成功:
输入 python
输入 import torch,没有报错说明安装成功
输入 print(torch.version),查看pytorch的版本。
到这里,pytorch和 torchvision,以及要使用显卡的包cudatoolkit,就安装成功了。
注意:如果不用显卡,用cpu跑则不用安装cudatoolkit。
首先去PyGeometric的官网: https://pytorch-geometric.readthedocs.io/en/latest/notes/installation.html.
1、确保至少安装了PyTorch 1.4.0:
python -c "import torch; print(torch.__version__)"
>>> 1.4.0
2、PyTorch安装的CUDA版本:
python -c "import torch; print(torch.version.cuda)"
>>> 10.1
3、安装相关软件包:
pip install torch-scatter==latest+cu101 -f https://pytorch-geometric.com/whl/torch-1.4.0.html
pip install torch-sparse==latest+cu101 -f https://pytorch-geometric.com/whl/torch-1.4.0.html
pip install torch-cluster==latest+cu101 -f https://pytorch-geometric.com/whl/torch-1.4.0.html
pip install torch-spline-conv==latest+cu101 -f https://pytorch-geometric.com/whl/torch-1.4.0.html
pip install torch-geometric
如果是AMD的显卡,显然不能使用cuda,那么只能使用cpu跑了,用CPU时,要注意:pytorch,还有上面的这几个相关依赖包都要是CPU版本的。
4、测试是否安装成功:
输入 python
输入 import torch
输入 import torch_geometric
如果都没有报错,那就是成功了。
我安装完遇到了错误:import torch_geometric,然后报错:OSError: [WinError 127] 找不到指定的模块,我把错误写下来, 链接.
最好先把PyG的官方文档看一遍, PyG官方文档.
还要基本会用PyTorch, PyTorch官方文档.
解释在代码的注释中。
注意,总体要按四步来看:
第一步:准备数据
第二步:定义模型(网络的定义,以及输入、隐藏、输出层数)
第三步:定义损失函数和优化器
第四步:训练+测试(把数据灌入里面)
下面就使用最简单的GCN模型。
import os
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch_geometric.datasets import Planetoid # PyG处理好的一些数据,如"Cora", "CiteSeer" and "PubMed" ,用Planetoid这个类调用即可
import torch_geometric.nn as pyg_nn
# 第一步:准备数据,load Cora dataset
def get_data(folder="node_classify/cora", data_name="cora"):
"""
:param folder:保存数据集的根目录。
:param data_name:数据集的名称
:return:返回的是一个对象,就是PyG文档里的Data对象,它有一些属性,如 data.x、data.edge_index等
"""
dataset = Planetoid(root=folder, name=data_name)
return dataset
# 第二步:定义模型,create the graph cnn model
class GraphCNN(nn.Module):
def __init__(self, in_c, hid_c, out_c):
super(GraphCNN, self).__init__() # 表示子类GraphCNN继承了父类nn.Module的所有属性和方法.
# 下面这个就是前面讲的GCN,参数只有输入和输出,定义了两层的GCN.
self.conv1 = pyg_nn.GCNConv(in_channels=in_c, out_channels=hid_c)
self.conv2 = pyg_nn.GCNConv(in_channels=hid_c, out_channels=out_c)
def forward(self, data):
# data.x data.edge_index
x = data.x # [N, C], C为特征的维度
edge_index = data.edge_index # [2, E], E为边的数量
hid = self.conv1(x=x, edge_index=edge_index) # [N, D], N是节点数量,D是第一层输出的隐藏层的维度
hid = F.relu(hid)
out = self.conv2(x=hid, edge_index=edge_index) # [N, out_c], out_c就是定义的输出,比如分成几类就是几,这里是7
out = F.log_softmax(out, dim=1) # [N, out_c],表示输出
return out
# todo list
class YouOwnGCN(nn.Module): # 这个不用理会,如果之后想用别的图卷积实现,可以自己在这里写,然后调用
pass
def main():
os.environ["CUDA_VISIBLE_DEVICES"] = "0" # 配置GPU
cora_dataset = get_data()
# todo list
# 这个是自己写的网络的实例化
my_net = GraphCNN(in_c=cora_dataset.num_node_features, hid_c=13, out_c=cora_dataset.num_classes)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu") # 检查设备
my_net = my_net.to(device) # 模型送入设备
data = cora_dataset[0].to(device) # 数据送入设备,也就是一张图
# 第三步:定义损失函数和优化器
optimizer = torch.optim.Adam(my_net.parameters(), lr=1e-3) # 优化器
# 第四步:训练+测试
# model train,这个train就是说归一化等可以重复使用,而设置成eval则就不行了,表示测试
my_net.train()
for epoch in range(200):
optimizer.zero_grad() # 每次缓存之后清零,不然梯度会累加
output = my_net(data) # 预测结果
loss = F.nll_loss(output[data.train_mask], data.y[data.train_mask]) # 意思就是只取训练集
loss.backward()
print("epoch:", epoch + 1, loss.item())
optimizer.step() # 优化器
# model test
my_net.eval()
_, prediction = my_net(data).max(dim=1)
target = data.y
test_correct = prediction[data.test_mask].eq(target[data.test_mask]).sum().item()
test_number = data.test_mask.sum().item()
print("Accuracy of Test Samples:{}%".format(100*test_correct/test_number))
if __name__ == "__main__":
main()
这个和上面的大部分代码都是一样的,不一样的只是定义模型使用的是GAT。
import os
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch_geometric.datasets import Planetoid
import torch_geometric.transforms as T
import torch_geometric.nn as pyg_nn
# load Cora dataset
def get_data(folder="node_classify/cora", data_name="cora"):
dataset = Planetoid(root=folder, name=data_name, pre_transform=T.KNNGraph(k=6), transform=T.NormalizeFeatures())
return dataset
# create the graph cnn model
class GraphCNN(nn.Module):
def __init__(self, in_c, hid_c, out_c):
super(GraphCNN, self).__init__() # 表示子类GraphCNN继承了父类nn.Module的所有属性和方法
self.conv1 = pyg_nn.GATConv(in_channels=in_c, out_channels=hid_c, dropout=0.6)
self.conv2 = pyg_nn.GATConv(in_channels=hid_c, out_channels=out_c, dropout=0.6, heads=1, concat=True)
def forward(self, data):
# data.x data.edge_index
x = data.x # [N, C], C为特征的维度
edge_index = data.edge_index # [2, E], E为边的数量
x = F.dropout(x, p=0.6, training=self.training)
hid = self.conv1(x=x, edge_index=edge_index) # [N, D], N是节点数量,D是第一层输出的隐藏层的维度
hid = F.relu(hid)
out = self.conv2(x=hid, edge_index=edge_index) # [N, out_c], out_c就是定义的输出,比如分成几类就是几,这里是7
out = F.log_softmax(out, dim=1) # [N, out_c],表示输出
return out
def main():
os.environ["CUDA_VISIBLE_DEVICES"] = "0" # 配置GPU
cora_dataset = get_data()
my_net = GraphCNN(in_c=cora_dataset.num_node_features, hid_c=8, out_c=cora_dataset.num_classes)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu") # 检查设备
my_net = my_net.to(device) # 模型送入设备
data = cora_dataset[0].to(device) # 数据送入设备,也就是一张图
optimizer = torch.optim.Adam(my_net.parameters(), lr=5e-3) # 优化器
# model train,这个train就是说归一化等可以重复使用,而设置成eval则就不行了,表示测试
my_net.train()
for epoch in range(200):
optimizer.zero_grad() # 每次缓存之后清零,不然梯度会累加
output = my_net(data) # 预测结果
loss = F.nll_loss(output[data.train_mask], data.y[data.train_mask]) # 意思就是只取训练集
loss.backward()
print("epoch", epoch + 1, loss.item())
optimizer.step() # 优化器
# model test
my_net.eval()
_, prediction = my_net(data).max(dim=1)
target = data.y
test_correct = prediction[data.test_mask].eq(target[data.test_mask]).sum().item()
test_number = data.test_mask.sum().item()
print("Accuracy of Test Samples:{}%".format(100*test_correct/test_number))
if __name__ == "__main__":
main()