【Graph Net】【专题系列】四、GAT代码实战

【Graph Net】【专题系列】四、GAT代码实战

目录

一、简介

二、代码

三、模型结构、结果分析

四、展望


一、简介

        GAT(Graph Attention Network)在图结构的基础上,加上了注意力这个东东,这期我不装了,图NNer不搞PyG,感觉就是对不起巨人们的肩膀!此处吹爆PyG!

        本文目标就是基于PyG,写个GAT的Demo来实现Cora图分类,这里Cora的读取我自己用sklearn和文本读取,也算是我自己的一点点贡献,方便后者用自己的数据集做二次开发。话不多说,直接上代码。 图的相关代码可见仓库:GitHub - mapstory6788/Graph-Networks

二、代码

import os
import time
import random
import torch
import torch.nn.functional as F
from torch_geometric.nn import GATConv
from torch_geometric.data import Data
import numpy as np
import pandas as pd
import scipy.sparse as sp
from sklearn.preprocessing import LabelEncoder

#配置项
class configs():
    def __init__(self):
        # Data
        self.data_path = r'./data/cora'
        self.save_model_dir = './'

        self.model_name = r'GAT'
        self.seed = 2023

        self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
        self.epoch = 500
        self.in_features = 1433  #core ~ feature:1433
        self.hidden_features = 16  # 隐层数量
        self.output_features = 8  # core~paper-point~ 8类

        self.learning_rate = 0.01
        self.dropout = 0.5

        self.istrain = True
        self.istest = True

cfg = configs()

def seed_everything(seed=2023):
    random.seed(seed)
    os.environ['PYTHONHASHSEED']=str(seed)
    np.random.seed(seed)
    torch.manual_seed(seed)

seed_everything(seed = cfg.seed)

# 读取Cora数据集 return geometric Data格式
def index_to_mask(index, size):
    mask = np.zeros(size, dtype=bool)
    mask[index] = True
    return mask

def load_cora_data(data_path = cfg.data_path):

    content_df = pd.read_csv(os.path.join(data_path,"cora.content"), delimiter="\t", header=None)
    content_df.set_index(0, inplace=True)
    index = content_df.index.tolist()
    features = sp.csr_matrix(content_df.values[:,:-1], dtype=np.float32)

    # 处理标签
    labels = content_df.values[:,-1]
    class_encoder = LabelEncoder()
    labels = class_encoder.fit_transform(labels)

    # 读取引用关系
    cites_df = pd.read_csv(os.path.join(data_path,"cora.cites"), delimiter="\t", header=None)
    cites_df[0] = cites_df[0].astype(str)
    cites_df[1] = cites_df[1].astype(str)
    cites = [tuple(x) for x in cites_df.values]
    edges = [(index.index(int(cite[0])), index.index(int(cite[1]))) for cite in cites]
    edges = np.array(edges).T

    # 构造Data对象
    data = Data(x=torch.from_numpy(np.array(features.todense())),
                edge_index=torch.LongTensor(edges),
                y=torch.from_numpy(labels))

    idx_train = range(140)
    idx_val = range(200, 500)
    idx_test = range(500, 1500)

    data.train_mask = index_to_mask(idx_train, size=labels.shape[0])
    data.val_mask = index_to_mask(idx_val, size=labels.shape[0])
    data.test_mask = index_to_mask(idx_test, size=labels.shape[0])

    return data

class GAT(torch.nn.Module):
    def __init__(self, in_channels, out_channels, heads=8, dropout=cfg.dropout, bias=True):
        super(GAT, self).__init__()

        self.conv1 = GATConv(in_channels, out_channels, heads=heads, concat=True, dropout=dropout, bias=bias)
        self.conv2 = GATConv(heads * out_channels, out_channels, heads=heads, concat=False, dropout=dropout, bias=bias)

    def forward(self, data):
        x, edge_index = data.x, data.edge_index
        x = F.dropout(x, p=0.6, training=self.training)
        x = F.elu(self.conv1(x, edge_index))
        x = F.dropout(x, p=0.6, training=self.training)
        x = self.conv2(x, edge_index)
        return F.log_softmax(x, dim=1)

class myGAT_run():
    def train(self):
        t = time.time()
        dataset = load_cora_data()
        model = GAT(dataset.num_features, cfg.output_features).to(cfg.device)
        data = dataset
        optimizer = torch.optim.Adam(model.parameters(), lr=cfg.learning_rate, weight_decay=5e-4)

        model.train()
        for epoch in range(cfg.epoch):
            optimizer.zero_grad()
            output = model(data)
            preds = output.max(dim=1)[1]
            loss_train = F.nll_loss(output[data.train_mask], data.y[data.train_mask].long())
            correct = preds[data.train_mask].eq(data.y[data.train_mask]).sum().item()
            acc_train = correct / int(data.train_mask.sum())
            loss_train.backward()
            optimizer.step()
            loss_val = F.nll_loss(output[data.val_mask], data.y[data.val_mask].long())
            correct = preds[data.val_mask].eq(data.y[data.val_mask]).sum().item()
            acc_val = correct / int(data.val_mask.sum())
            print('Epoch: {:04d}'.format(epoch + 1),
                  'loss_train: {:.4f}'.format(loss_train.item()),
                  'acc_train: {:.4f}'.format(acc_train),
                  'loss_val: {:.4f}'.format(loss_val.item()),
                  'acc_val: {:.4f}'.format(acc_val),
                  'time: {:.4f}s'.format(time.time() - t))
        torch.save(model, os.path.join(cfg.save_model_dir, 'latest.pth'))  # 模型保存

    def infer(self):
        #Create Test Processing
        dataset = load_cora_data()
        data = dataset
        model_path = os.path.join(cfg.save_model_dir, 'latest.pth')
        model = torch.load(model_path, map_location=torch.device(cfg.device))
        model.eval()
        output = model(data)
        params = sum(p.numel() for p in model.parameters())
        preds = output.max(dim=1)[1]
        loss_test = F.nll_loss(output[data.test_mask], data.y[data.test_mask].long())
        correct = preds[data.test_mask].eq(data.y[data.test_mask]).sum().item()
        acc_test = correct / int(data.test_mask.sum())
        print("Test set results:",
              "loss= {:.4f}".format(loss_test.item()),
              "accuracy= {:.4f}".format(acc_test),
              'params={:.4f}k'.format(params/1024))

if __name__ == '__main__':
    mygraph = myGAT_run()
    if cfg.istrain == True:
        mygraph.train()
    if cfg.istest == True:
        mygraph.infer()

三、模型结构、结果分析

Layer (type)Output ShapeParam #
Linear-1[-1, 64]91712
SumAggregation-2[-1, 8, 8]  0
GATConv-3[-1, 64]64
Linear-4[-1, 64]4096
SumAggregation-5[-1, 8, 8]0
GATConv-6  [-1, 8]8

Total params: 95,880≈93.88Kb,模型结构还是比较精简的。
在Cora数据集上,训练epoch=500,accuracy= 0.7590

四、展望

后面文章考虑从代码的角度,来研究下Graph Embedding,且与GNNs的联系。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是使用PyTorch实现GAT代码示例: ``` python import torch import torch.nn as nn import torch.nn.functional as F class GATLayer(nn.Module): def __init__(self, in_dim, out_dim): super(GATLayer, self).__init__() self.in_dim = in_dim self.out_dim = out_dim self.W = nn.Parameter(torch.zeros(size=(in_dim, out_dim))) self.a = nn.Parameter(torch.zeros(size=(2*out_dim, 1))) nn.init.xavier_uniform_(self.W.data, gain=1.414) nn.init.xavier_uniform_(self.a.data, gain=1.414) def forward(self, h, adj): Wh = torch.mm(h, self.W) a_input = self._prepare_attentional_mechanism_input(Wh) e = F.leaky_relu(torch.matmul(a_input, self.a).squeeze(2)) zero_vec = -9e15*torch.ones_like(e) attention = torch.where(adj > 0, e, zero_vec) attention = F.softmax(attention, dim=1) h_prime = torch.matmul(attention, Wh) return h_prime def _prepare_attentional_mechanism_input(self, Wh): N = Wh.size()[0] Wh_repeated_in_chunks = Wh.repeat_interleave(N, dim=0) Wh_repeated_alternating = Wh.repeat(N, 1) all_combinations_matrix = torch.cat([Wh_repeated_in_chunks, Wh_repeated_alternating], dim=1) return all_combinations_matrix.view(N, N, 2*self.out_dim) class GAT(nn.Module): def __init__(self, n_feat, n_hid, n_class, dropout, alpha, n_heads): super(GAT, self).__init__() self.dropout = dropout self.attentions = [GATLayer(n_feat, n_hid) for _ in range(n_heads)] for i, attention in enumerate(self.attentions): self.add_module('attention_{}'.format(i), attention) self.out_att = GATLayer(n_hid*n_heads, n_class) self.alpha = alpha def forward(self, x, adj): x = F.dropout(x, self.dropout, training=self.training) x = torch.cat([att(x, adj) for att in self.attentions], dim=1) x = F.dropout(x, self.dropout, training=self.training) x = F.elu(self.out_att(x, adj)) return F.log_softmax(x, dim=1) ``` 在此示例中,我们实现了一个包含多头注意力机制的GAT模型。其中,GATLayer是GAT的核心组件,每个GATLayer都包含一个注意力头。在GAT模型中,我们将多个注意力头的输出连接在一起,再通过一个输出层进行分类。在forward函数中,我们首先对输入进行dropout,然后通过多个GATLayer进行特征提取,最后通过输出层进行分类并使用log_softmax进行预测。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BoostingIsm

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值