当前位置: 首页 > article >正文

【模型学习之路】PyG的使用+基于点的任务

这一篇是关于PyG的基本使用

目录

前言

PyG的数据结构

演示

图的可视化

基于点的任务

任务分析

MLP

GCN


前言

对图结构感兴趣的朋友可以学一下常用的有关图结构的库:networkx详细介绍 `networkx` 库,探讨它的基本功能、如何创建图、操作图以及其常用参数。-CSDN博客

PyG零基础的朋友可以看一下这个视频的11~14集1-PyTorch Geometric工具包安装与配置方法_哔哩哔哩_bilibili

PyG的数据结构

演示

我们用一个简单的数据集作为演示。

import matplotlib.pyplot as plt
from torch_geometric.datasets import KarateClub
dataset = KarateClub()  # 这是一个数据集,里面只有一个图
len(dataset)

# output
1

简单介绍一下每个数据的维度信息。

data = dataset[0]
print(data)

# output
Data(x=[34, 34], edge_index=[2, 156], y=[34], train_mask=[34])
  • x: 节点特征 [m, f] (m: 节点数,f: 特征数)。

  • edge_index: 边的索引 [2, e] (e: 边数)。可以看作是e条边,两两相连,然后转置了。

  • y: 标签 [m] (m: 节点数)。自然可以做成多输出的,那么维度就会是[m, n_tasks]

  • mask: [m] 一个相对玄学一点的东西,之后在不同场景中介绍

图的可视化

可以利用networks做可视化

import networkx as nx
from matplotlib import pyplot as plt
from torch_geometric.utils import to_networkx
from visualize import visualize_graph

def visualize_graph(G, color):
    plt.figure(figsize=(5, 5))
    plt.xticks([])
    plt.yticks([])
    nx.draw_networkx(G, pos=nx.spring_layout(G, seed=42), node_color=color,
                        with_labels=False, node_size=100, cmap='Set2')

G = to_networkx(data)
visualize_graph(G, color=data.y)

在PyG中,邻接矩阵edge_index是按照稀疏矩阵的方式存的,我们可以把转化为我们平时之前用的密集矩阵。

# 转化为密集型的邻接矩阵
G_adj = nx.to_numpy_array(G)
print(G_adj)

# output
[[0. 1. 1. ... 1. 0. 0.]
 [1. 0. 1. ... 0. 0. 0.]
 [1. 1. 0. ... 0. 1. 0.]
 ...
 [1. 0. 0. ... 0. 1. 1.]
 [0. 0. 1. ... 1. 0. 1.]
 [0. 0. 0. ... 1. 1. 0.]]

顺便画个热力图

import seaborn as sns
sns.heatmap(G_adj, cmap='Blues')

顺便把邻接矩阵和度矩阵都画一下

import numpy as np
adj = G_adj
d = np.diag(np.sum(adj, axis=1))
adj = adj + np.eye(adj.shape[0])
d = d + np.eye(adj.shape[0])

plt.figure(figsize=(10, 4))
plt.subplot(121)
sns.heatmap(adj, cmap='Blues')
plt.subplot(122)
sns.heatmap(d, cmap='Blues')
plt.show()

基于点的任务

任务分析

先像前言中教程一样,先拿到数据集,然后做一些分析。

import torch
from torch_geometric.datasets import Planetoid
from torch_geometric.transforms import NormalizeFeatures

dataset = Planetoid(root='data/Planetoid', name='Cora', transform=NormalizeFeatures())

print(f'Number of graphs: {len(dataset)}')
print(f'Number of features: {dataset.num_features}')
print(f'Number of classes: {dataset.num_classes}')

#output
Number of graphs: 1
Number of features: 1433
Number of classes: 7


# 就一张图,直接取出来便是
data = dataset[0]
print(data)
print(f'Number of nodes: {data.num_nodes}')
print(f'Number of edges: {data.num_edges}')
print(f'Average node degree: {data.num_edges / data.num_nodes:.2f}')
print(f'Training node label ratio: {data.train_mask.sum().item() / data.num_nodes:.2f}')
print(f'Has isolated nodes: {data.has_isolated_nodes()}')
print(f'Has self-loops: {data.has_self_loops()}')
print(f'Is undirected: {data.is_undirected()}')

#output
Data(x=[2708, 1433], edge_index=[2, 10556], y=[2708], train_mask=[2708], val_mask=[2708], test_mask=[2708])
Number of nodes: 2708
Number of edges: 10556
Average node degree: 3.90
Training node label ratio: 0.05
Has isolated nodes: False
Has self-loops: False
Is undirected: True

上面的很多指标意思都很明显,就不过多解释了。

重点是,基于点的任务到底要干什么?

基于点的任务,往往会约束在同一张图中。这张图有很多节点,如果它们有标签值,就可以划分到训练集(train_mask为True的点)、验证集(valid_mask为True的点)和测试集(test_mask为True的点),用于模型的训练与预测。

我们的目标是,对于一些没有标签值的点,我们就用训练好的模型去预测它们。

所以,其实这个任务非常加了邻接矩阵的MLP。我们将两者对比一下。

MLP

模型定义很简单,这是一个7分类问题。

import torch
import torch.nn as nn

class MLP(torch.nn.Module):
    def __init__(self):
        super(MLP, self).__init__()
        torch.manual_seed(666)
        self.fc = nn.Sequential(
            nn.Linear(1433, 256),
            nn.ReLU(),
            nn.Linear(256, 64),
            nn.ReLU(),
            nn.Linear(64, 7)
        )
        
    def forward(self, x):
        return self.fc(x)

训练的时候,注意一下细节:

1. 这个数据集用train_mask和test_mask来划分训练集和测试集。在training时,只用在train_mask为True的上面计算损失即可。同样在计算testing的acc时,也只需要用到test_mask为True的即可。
2. x的shape为[2708, 1433],这里整个训练采用的是最传统的方式,即整个数据集不划分batch(或者说整个数据集就是一个batch)

model = MLP()
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.01, weight_decay=5e-4)

def train():
    model.train()
    optimizer.zero_grad()
    out = model(data.x)
    loss = criterion(out[data.train_mask], data.y[data.train_mask])
    loss.backward()
    optimizer.step()
    return loss.item()

def test():
    model.eval()
    out = model(data.x)
    pred = out.argmax(dim=1)  # [2708, 7] -> [2708]
    acc = pred[data.test_mask].eq(data.y[data.test_mask]).sum().item() / data.test_mask.sum().item()
    return acc

mlp_loss_lst = []
mlp_acc_lst = []
for epoch in range(1, 201):
    """
    input: [2708, 1433]
    output: [2708, 7]
    """
    loss = train()
    acc = test()
    mlp_loss_lst.append(loss)
    mlp_acc_lst.append(acc)

GCN

先来看看模型。

PyG将图神经网络里面各种经典的架构基本都有实现,这里的GCNConv直接调库,原理的话我们上一个专栏详细说过了:【模型学习之路】手写+分析GAT_手写gat-CSDN博客

仔细观察,GCNConv在维度上的表现简直就跟nn.Linear一模一样!

当然,内部自然要复杂的多。此外,它在调用时还要有edge_index,格式和前面的data.edge_index是统一的。

import torch
import torch.nn.functional as F
from torch_geometric.nn import GCNConv

class GCN(torch.nn.Module):
    def __init__(self):
        super(GCN, self).__init__()
        torch.manual_seed(666)
        self.conv1 = GCNConv(1433, 16)
        self.conv2 = GCNConv(16, 7)
        
    def forward(self, x, edge_index):
        x = self.conv1(x, edge_index)  # [2708, 1433] -> [2708, 16]
        x = F.relu(x)
        x = F.dropout(x, training=self.training)
        x = self.conv2(x, edge_index)  # [2708, 16] -> [2708, 7]
        return x

训练过程大差不差。

model = GCN()
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.01, weight_decay=5e-4)

def train():
    model.train()
    optimizer.zero_grad()
    out = model(data.x, data.edge_index)
    loss = criterion(out[data.train_mask], data.y[data.train_mask])
    loss.backward()
    optimizer.step()
    return loss.item()

def test():
    model.eval()
    out = model(data.x, data.edge_index)
    pred = out.argmax(dim=1)  # [2708, 7] -> [2708]
    acc = pred[data.test_mask].eq(data.y[data.test_mask]).sum().item() / data.test_mask.sum().item()
    return acc

gcn_loss_lst = []
gcn_acc_lst = []
for epoch in range(1, 201):
    """
    input: [2708, 1433]
    output: [2708, 7]
    """
    loss = train()
    acc = test()
    gcn_loss_lst.append(loss)
    gcn_acc_lst.append(acc)

两者的对比也是挺明显的:

plt.figure(figsize=(10, 5))
plt.plot(mlp_acc_lst, label='MLP')
plt.plot(gcn_acc_lst, label='GCN')
plt.ylim(0, 1)
plt.legend()
plt.show()


http://www.kler.cn/a/412021.html

相关文章:

  • 【机器学习】如何使用Python的Scikit-learn库实现机器学习模型,并对数据进行预处理和特征缩放以提高模型性能?
  • Vue前端开发2.3.5 条件渲染指令
  • 网络安全风险评估
  • 摄像头原始数据读取——V4L2(mmap模式,V4L2_MEMORY_MMAP)
  • Spring Boot 3.4 正式发布,结构化日志!
  • 旋转磁体产生的场 - 实验视频资源下载
  • Mybatis---MyBatis映射文件SQL深入、多表查询
  • Amazon AWS公司介绍
  • docker部署的服务器数据备份
  • 16.迭代器模式设计思想
  • Python学习指南 + 谷歌浏览器如何安装插件
  • 【通俗理解】神经网络中步长缩小的奥秘:优化算法与卷积操作的影响
  • 研0找实习【学nlp】14--BERT理解
  • 【C语言】指针与数组的例题详解:深入分析与高级用法
  • C/C++绘制爱心
  • 【论文阅读】WGSR
  • 紫光档案管理系统 mergeFile SQL注入漏洞复现
  • MySQL闪回恢复:轻松应对数据误删,数据安全有保障
  • 16:00面试,16:08就出来了,问的问题有点变态。。。
  • 实时数据开发 | 一文理解Flink窗口机制
  • 算法学习笔记(十):位运算、数论等
  • Java多态的优势和弊端
  • 入门岛-L0G1000
  • 【H2O2|全栈】JS进阶知识(十)ES6(6)
  • golang版本管理工具:scoop使用
  • Docker 技术:构建高效容器化环境的关键