学习目的
学习使用gnn进行节点分类的基本工作流程,即预测图中节点的类别。
关于GCN节点分类的综述
在图数据上最流行和广泛采用的任务之一是节点分类,其中模型需要预测每个节点的真实类别。
在图神经网络之前,许多被提出的方法要么单独使用连通性(如DeepWalk或node2vec),要么简单地结合连通性和节点自身的特征。相比之下,gnn通过结合局部邻域的连通性和特征提供了获得节点表示的机会。Kipf等人将节点分类问题表述为半监督节点分类任务的例子。图神经网络(GNN)仅借助一小部分标记节点就能准确预测其他节点的类别。笔记将展示如何在Cora数据集上仅使用少量标签构建半监督节点分类的GNN,一个以论文为节点,引用为边的引文网络。任务是预测给定论文的类别。每个论文节点包含一个单词计数向量作为其特征,标准化后它们的总和为1。
导包
import os
os.environ["DGLBACKEND"] = "pytorch"
import dgl
import dgl.data
import torch
import torch.nn as nn
import torch.nn.functional as F
from dgl.nn import GraphConv
os.environ[“DGLBACKEND”] = "pytorch"该行代码十分重要,因为它指定了DGL所运行的后端环境。
加载Cora数据集
dataset = dgl.data.CoraGraphDataset()
print(f"Number of categories: {dataset.num_classes}")
一个DGL数据集对象可以包含一个或多个图。本教程中使用的Cora数据集只包含一个图。
查看节点和边
"""
一个DGL数据集对象可以包含一个或多个图。本教程中使用的Cora数据集只包含一个图。
"""
g = dataset[0]print("Node features")
print(g.ndata)
print("Edge features")
print(g.edata)
定义图卷积网络(GCN)
# 定义图卷积网络(GCN)
class GCN(nn.Module):def __init__(self, in_feats, h_feats, num_classes):super(GCN, self).__init__()self.conv1 = GraphConv(in_feats, h_feats)self.conv2 = GraphConv(h_feats, num_classes)def forward(self, g, in_feat):h = self.conv1(g, in_feat)h = F.relu(h)h = self.conv2(g, h)return h
创建给定维度的模型
model = GCN(g.ndata["feat"].shape[1], 16, dataset.num_classes)
模型训练
使用DGL库训练网络类似于神经网络的训练,对于神经网络的训练步骤可以参考本博客:【转载】Pytorch模型实现的四部曲
# 模型训练
def train(g, model):optimizer = torch.optim.Adam(model.parameters(), lr=0.01)best_val_acc = 0best_test_acc = 0features = g.ndata["feat"]labels = g.ndata["label"]train_mask = g.ndata["train_mask"]val_mask = g.ndata["val_mask"]test_mask = g.ndata["test_mask"]for epoch in range(100):# 前向传播logits = model(g, features)# 计算预测值pred = logits.argmax(1)# 计算损失# 只计算训练集中节点的损失loss = F.cross_entropy(logits[train_mask], labels[train_mask])# Compute accuracy on training/validation/testtrain_acc = (pred[train_mask] == labels[train_mask]).float().mean()val_acc = (pred[val_mask] == labels[val_mask]).float().mean()test_acc = (pred[test_mask] == labels[test_mask]).float().mean()# Save the best validation accuracy and the corresponding test accuracy.if best_val_acc < val_acc:best_val_acc = val_accbest_test_acc = test_acc# Backwardoptimizer.zero_grad()loss.backward()optimizer.step()if e % 5 == 0:print(f"In epoch {e}, loss: {loss:.3f}, val acc: {val_acc:.3f} (best {best_val_acc:.3f}), test acc: {test_acc:.3f} (best {best_test_acc:.3f})")
在cuda上训练GCN
g = g.to('cuda') #在cuda上训练GCN
model = GCN(g.ndata['feat'].shape[1], 16, dataset.num_classes).to('cuda')
train(g, model)
省流版:完整代码
"""学习使用gnn进行节点分类的基本工作流程,即预测图中节点的类别。"""
import os
os.environ["DGLBACKEND"] = "pytorch"
import dgl
import dgl.data
import torch
import torch.nn as nn
import torch.nn.functional as F
from dgl.nn import GraphConv
"""GNN节点分类综述
在图数据上最流行和广泛采用的任务之一是节点分类,其中模型需要预测每个节点的真实类别。
在图神经网络之前,许多被提出的方法要么单独使用连通性(如DeepWalk或node2vec),
要么简单地结合连通性和节点自身的特征。相比之下,gnn通过结合局部邻域的连通性和特征提供了获得节点表示的机会。
Kipf等人将节点分类问题表述为半监督节点分类任务的例子。图神经网络(GNN)
仅借助一小部分标记节点就能准确预测其他节点的类别。本教程将展示如何在Cora数据集上仅使用少量标签构建半监督节点分类的GNN,一个以论文为节点,
引用为边的引文网络。任务是预测给定论文的类别。每个论文节点包含一个单词计数向量作为其特征,
标准化后它们的总和为1
"""# 加载Cora数据集¶
dataset = dgl.data.CoraGraphDataset()
print(f"Number of categories: {dataset.num_classes}")"""
一个DGL数据集对象可以包含一个或多个图。本教程中使用的Cora数据集只包含一个图。
"""
g = dataset[0]print("Node features")
print(g.ndata)
print("Edge features")
print(g.edata)# 定义图卷积网络(GCN)
class GCN(nn.Module):def __init__(self, in_feats, h_feats, num_classes):super(GCN, self).__init__()self.conv1 = GraphConv(in_feats, h_feats)self.conv2 = GraphConv(h_feats, num_classes)def forward(self, g, in_feat):h = self.conv1(g, in_feat)h = F.relu(h)h = self.conv2(g, h)return h# 创建具有给定维度的模型
model = GCN(g.ndata["feat"].shape[1], 16, dataset.num_classes)# 模型训练
def train(g, model):optimizer = torch.optim.Adam(model.parameters(), lr=0.01)best_val_acc = 0best_test_acc = 0features = g.ndata["feat"]labels = g.ndata["label"]train_mask = g.ndata["train_mask"]val_mask = g.ndata["val_mask"]test_mask = g.ndata["test_mask"]for epoch in range(100):# 前向传播logits = model(g, features)# 计算预测值pred = logits.argmax(1)# 计算损失# 只计算训练集中节点的损失loss = F.cross_entropy(logits[train_mask], labels[train_mask])# Compute accuracy on training/validation/testtrain_acc = (pred[train_mask] == labels[train_mask]).float().mean()val_acc = (pred[val_mask] == labels[val_mask]).float().mean()test_acc = (pred[test_mask] == labels[test_mask]).float().mean()# Save the best validation accuracy and the corresponding test accuracy.if best_val_acc < val_acc:best_val_acc = val_accbest_test_acc = test_acc# Backwardoptimizer.zero_grad()loss.backward()optimizer.step()if e % 5 == 0:print(f"In epoch {e}, loss: {loss:.3f}, val acc: {val_acc:.3f} (best {best_val_acc:.3f}), test acc: {test_acc:.3f} (best {best_test_acc:.3f})")
g = g.to('cuda') #在cuda上训练GCN
model = GCN(g.ndata['feat'].shape[1], 16, dataset.num_classes).to('cuda')
train(g, model)