学习目的

学习使用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 = 0
    best_test_acc = 0

    features = 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/test
        train_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_acc
            best_test_acc = test_acc

        # Backward
        optimizer.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 = 0
    best_test_acc = 0

    features = 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/test
        train_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_acc
            best_test_acc = test_acc

        # Backward
        optimizer.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)
11-14 05:30