对于GCN(Graph convolution networks)的浅显理解

传统的网状结构

初学深度学习时候,常见的经典结构比如CNN、RNN
对于CNN来说,他的核心在于它的kernel,kernel是一个个小窗口,在图片上平移,通过卷积的方式来提取特征。这里的关键在于图片结构上的平移不变性:一个小窗口无论移动到图片的哪一个位置,其内部的结构都是一模一样的,因此CNN可以实现参数共享。这就是CNN的精髓所在。
对于RNN来说,它的对象是自然语言这样的序列信息,是一个一维的结构,RNN就是专门针对这些序列的结构而设计的,通过各种门的操作,使得序列前后的信息互相影响,从而很好地捕捉序列的特征。
但是他们处理的对象都是属于欧式空间的数据。然而对于一些不规则的结构,例如图形结构(拓扑结构)、树形结构,比如社交网络、化学分子结构、知识图谱等等,这样图的结构一般来说是十分不规则的,可以认为是无限维的一种数据,所以它没有平移不变性。每一个节点的周围结构可能都是独一无二的,这种结构的数据,就让传统的CNN、RNN瞬间失效。就没有很好的效果了。

GCN结构

假设我们手头有一批图数据,其中有N个节点(node),每个节点都有自己的特征,我们设这些节点的特征组成一个N×D维的矩阵X,然后各个节点之间的关系也会形成一个N×N维的矩阵A,也称为邻接矩阵(adjacency matrix)。X和A便是我们模型的输入。
GCN也是一个神经网络层,它的层与层之间的传播方式是:
在这里插入图片描述
这个公式中:
A波浪=A+I,I是单位矩阵
D波浪是A波浪的度矩阵(degree matrix),公式为
H是每一层的特征,对于输入层的话,H就是X
σ是非线性激活函数

实例展示

# -*- coding: utf-8 -*-
# 引入库函数
import dgl
import torch
import torch.optim as optim
import matplotlib.pyplot as plt
import torch.nn.functional as F
import torch.nn as nn
import dgl.function as fn
# device设备设定先使用cuda再用cpu
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

class GCNLayer(nn.Module):
    def __init__(self, in_feats, out_feats, bias=True):
        super(GCNLayer, self).__init__()
        self.weight = nn.Parameter(torch.Tensor(in_feats, out_feats))
        if bias:
            self.bias = nn.Parameter(torch.zeros(out_feats))
        else:
            self.bias = None
        self.reset_parameter()
    def reset_parameter(self):
        nn.init.xavier_uniform_(self.weight)
    def forward(self, g, h):
        with g.local_scope():
            h = torch.matmul(h, self.weight)
            g.ndata['h'] = h * g.ndata['norm']
            g.update_all(message_func=fn.copy_u('h', 'm'),
                         reduce_func=fn.sum('m', 'h'))
            h = g.ndata['h']
            h = h * g.ndata['norm']
            if self.bias is not None:
                h = h + self.bias
            return h

# GCN模型
class GCNModel(nn.Module):
    def __init__(self, in_feats, h_feats, num_classes, bias=True):
        super(GCNModel, self).__init__()
        self.conv1 = GCNLayer(in_feats, h_feats, bias)
        self.conv2 = GCNLayer(h_feats, num_classes, bias)

    def forward(self, g, in_feat):
        h = self.conv1(g, in_feat)
        h = F.relu(h)
        h = self.conv2(g, h)
        return h

# 画图功能
def drawPlot(heights, fname, ylabel, legends=None):
    # fname:save file name
    plt.figure(figsize=(6, 5))  # 设置画布大小
    x = [i for i in range(1, len(heights[0]) + 1)]
    # 绘制训练集和测试集上的loss变化曲线子图
    plt.xlabel("epoch")
    plt.ylabel(ylabel)
    for i in range(len(heights)):
        plt.plot(x, heights[i])
    if legends:
        plt.legend(legends)
    plt.savefig("images/{}".format(fname))
    plt.show()

# 训练函数
def train(g, model, optimizer, loss_fn, epochs):
    best_val_acc, best_test_acc = 0, 0
    # 得到数据集的特征和标签
    features, labels = g.ndata['feat'], g.ndata['label']
    train_mask, val_mask, test_mask = g.ndata['train_mask'], g.ndata[
        'val_mask'], g.ndata['test_mask']
    train_acc_list, val_acc_list, test_acc_list = [], [], []
    for e in range(epochs):
        logits = model(g, features)  # (N, label_nums)
        pred = logits.argmax(1)
        loss = loss_fn(logits[train_mask], labels[train_mask])
        # cal trian acc
        train_acc = (pred[train_mask] == labels[train_mask]).float().mean()
        # cal val acc
        val_acc = (pred[val_mask] == labels[val_mask]).float().mean()
        # cal test acc
        test_acc = (pred[test_mask] == labels[test_mask]).float().mean()

        train_acc_list.append(train_acc.item())
        val_acc_list.append(val_acc.item())
        test_acc_list.append(test_acc.item())

        # save result based on valid dataset
        if best_val_acc < val_acc:
            best_val_acc = val_acc
            best_test_acc = test_acc

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        if (e + 1) % 10 == 0:
            print(
                'Epoch {}, loss: {:.3f}, train acc: {:.3f}, val acc: {:.3f} (best {:.3f}), test acc: {:.3f} (best {:.3f})'
                .format(e + 1, loss, train_acc, val_acc, best_val_acc,
                        test_acc, best_test_acc))
    drawPlot([train_acc_list, val_acc_list, test_acc_list], "accuracy.png",
             "Acc", ["train", "val", "test"])


if __name__ == "__main__":
    # 设置超参数
    epochs = 200 # 训练轮次
    hidden_size = 32 # 隐藏层结点个数
    lr = 0.01 # 学习率
    weight_decay = 5e-4 # 下降权重
    # 加载数据集
    dataset = dgl.data.CoraGraphDataset(raw_dir="../Datasets/DGL/")
    g = dataset[0] # graph图状结构 有2708个结点 10556条边
    # 删除和添加自闭环
    g = dgl.remove_self_loop(g)
    g = dgl.add_self_loop(g)
    # degs保存的是图的邻接矩阵
    degs = g.out_degrees().float()
    # 开根号
    norm = torch.pow(degs, -0.5)
    # 把两点不相连的位置都填成0
    norm[torch.isinf(norm)] = 0
    g.ndata['norm'] = norm.unsqueeze(1)
    # 定义模型
    model = GCNModel(in_feats=g.ndata['feat'].shape[1],
                     h_feats=hidden_size,
                     num_classes=dataset.num_classes)
    # 将数据都部署到gpu或者cpu上
    if torch.cuda.is_available():
        g = g.to(device)
        model = model.to(device)
    # 优化器
    optimizer = optim.Adam(model.parameters(),
                           lr=lr,
                           weight_decay=weight_decay)
    # 损失函数
    loss_fn = nn.CrossEntropyLoss()
    # 训练
    train(g, model, optimizer, loss_fn, epochs)

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值