【torch_geometric】图介绍及GCN基本用法(上)

在这里插入图片描述
官方API

一、基本介绍

1. 图的基本定义

  • 图由顶点(Vertex)以及连接顶点的边(Edge)构成。顶点表示研究的对象,边表示两个对象之间特定的关系。

  • 图可以表示为顶点和边的集合,记为G=(V, E),其中V是顶点集合,E是边集合。

  • 图又分为有向图和无向图;非加权图和加权图;连通图和非连通图(看是否有孤立的点)等。
    在这里插入图片描述

2. torch_geometric中的数据存储方式

下面通过调用现有的数据集,查看数据集内部的存储结构。

# 从datasets中获取KarateClub数据集(和之前的内容有点像)
# 其实GNN是基于torch编写的,很多代码和之前类似
from torch_geometric.datasets import KarateClub

dataset = KarateClub()

print(f"Dataset:{dataset}:")
print("***********************")
print(f"Number of Graphs:{len(dataset)}") # 有多少个点?
print(f"Number of  features:{dataset.num_features}") # 每个点的特征维度是多少?
print(f"Number of  classes:{dataset.num_classes}") # 有多少类?

输出结果
在这里插入图片描述
由此看到这个数据集里只包含一张图。下面继续:

data = dataset[0] # 获取第0个元素(第一个图)

print(data)

输出结果
在这里插入图片描述
这个图一共有34个点,每个点由34维向量表示,有78条边,每个点都有标签。(如果有的点没有标签train_mask会小于34)

# 查看顶点特征
print(data.x)

输出结果
在这里插入图片描述
注意全是tensor向量。

edge_index = data.edge_index
print(edge_index.t())

输出结果
在这里插入图片描述
edge_index表示图的连接关系,这里并不是NN的表示,而是2N的矩阵,其中每个小矩阵的第一个元素表示起点,第二个元素表示终点,起点指向终点。

  1. edge_index:表示图的连接关系
  2. node_features:每个点的特征
  3. node_labels:每个点的标签
  4. train_mask:有的节点没有标签(用来表示哪些节点需要计算损失)

3. torch_geometric.utils的可视化工具:networkx

from torch.geometric.utils import to_networkx
import matplotlib.pyplot as plt
import networkx as nx

#现在要可视化刚刚那张有34个顶点,78条边的图
G = to_networkx(data, to_undirected=True) #表示无向图
plt.figure(figsize=(7,7))
plt.xticks([])
plt.xticks([])
nx.draw_networkx(G, pos=nx.spring_layout(G, seed=42), with_labels=False, node_color=color, cmap='Set2')
plt.show()

输出结果如上图

4. Graph Neural Network网络定义

可以自行看官方文档

import torch
from torch.nn import Linear #线性层
from torch_geometric.nn import GCNConv #卷积层

这个过程非常简单,和之前定义MLP网络很像

class GCN(torch.nn.Module):
	def __init__(self):
		super().__init__()
		torch,manual_seed(1234)
		self.conv1 = GCNConv(dataset.num_features, 4)
		self.conv2 = GCNConv(4,4)
        self.conv3 = GCNConv(4,2) #最终是4类,但是这里输出2维向量,方便可视化
        self.classifier = Linear(2, dataset.num_classes)

	# 定义前向传播网络,我也不知道什么时候用tanh激活什么时候用relu
	def forward(self, x, edge_index): #注意别忘记输入边的信息,相当于邻接矩阵
        h = self.conv1(x,edge_index)
        h = h.tanh()
        h = self.conv2(h, edge_index)
        h = h.tanh()
        h = self.conv3(h, edge_index)
        h = h.tanh()

		# 分类层
        out = self.classifier(h)

		return out, h

model = GCN()
print(model) #打印model结构

输出结果
在这里插入图片描述
这里,我们首先可视化未训练之前的特征(epoch=0)

_, h = model(data.x, data.edge_index) #前面为什么加了“_”?
print(f'embedding_shape:{list(h.shape)}') #34个点,2维向量

h = h.detach().numpy()
plt.scatter(h[:,0], h[:,1], s=140, c=data.y,cmap='Set2')
if epoch is not None and loss is not None:
	plt.xlabel(f"Epoch:{epoch}, loss:{loss.item():.4f}",fontsize=16)
    plt.show()

输出结果
embedding_shape:[34, 2]

在这里插入图片描述
下面看看训练模型之后特征是什么样子的

import time

model = GCN()
critation = torch.nn.CrossEntropyLoss() #交叉熵损失
optimizer = torch.optim.Adam(model.parameters(), lr=0.01) @Adam优化器

def train(data):
    optimizer.zero_grad() #梯度清零
    out, h = model(data.x, data.edge_index) #带分类层输出和不带分类层
    loss = critation(out[data.train_mask], data.y[data.train_mask]) #半监督
    loss.backward()
    optimizer.step()
    return loss, h

for epoch in range(401):
    loss, h = train(data)
    if epoch % 10 == 0:
        visualize_embedding(h, color=data.y, epoch=epoch, loss=loss)
        time.sleep(0.3)

在这里插入图片描述


二、节点分类任务

图嘛,节点和边可以生成好多个任务,比如给边推断节点类型,给点和边补全未知边,等等。现在,介绍一个最简单的节点分类任务。

目标:
对比MLP模型 和GCN模型的效果(在已有数据集上)

1. 数据集加载

查看数据基本信息

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

#下载数据集
dataset = Planetoid(root='data/Plantoid', name='Cora', transform=NormalizeFeatures())

print()
print(f'Dataset:{dataset}')
print("***********************************")
print(f'Number of fgraphs:{len(dataset)}')
print(f'Number of features:{dataset.num_features}')
print(f'Number of classes:{dataset.num_classes}')

data = dataset[0]

print()
print(data)
print('****************************************')

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'Number of training nodes:{data.train_mask.sum()}')
print(f'training node label rate:{int(data.train_mask.sum()) / data.num_nodes:.2f}')

输出结果
在这里插入图片描述
PS:这里不懂的小伙伴可以看第一章节对数据集存储结构的介绍部分↑
下面要先定义一个可视化函数,方便后面进行可视化

# TSNE可视化(降维)
%matplotlib inline
import matplotlib.pyplot as plt
from sklearn.manifold import TSNE

def visualize(h, color):
    z = TSNE(n_components=2). fit_transform(h.detach().cpu().numpy())
    
    plt.figure(figsize=(10,10))
    plt.xticks([])
    plt.yticks([])
    
    plt.scatter(z[:, 0], z[:, 1], s=70, c=color, cmap='Set2')
    plt.show

2. MLP模型

思考:传统的神经网络模型怎么对它进行分类?试着自己写一写代码

class MLP(torch.nn.Module):
	def __init__(self, hidden_channels):
		super().__init__()
		torch.manual_seed(12345)
        self.lin1 = Linear(dataset.num_features, hidden_channels)
        self.lin2 = Linear(hidden_channels, dataset.num_classes)

	def forward(self, x):
        x = self.lin1(x)
        x = x.relu()
        x = F.dropout(x, p=0.5, training=self.training)
        x = self.lin2(x)
        return x
        
model = MLP(hidden_channels=16)
print(model)

输出网络结构
在这里插入图片描述

model = MLP(hidden_channels=16) #为什么是16我也不清楚
critation = torch.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 = critation(out[data.train_mask], data.y[data.train_mask])
    loss.backward()
    optimizer.step()
    return loss #返回的是训练损失

def test():
    model.eval()
    out = model(data.x)
    pred = out.argmax(dim=1)
    test_correct = pred[data.test_mask] == data.y[data.test_mask]
    test_acc = int(test_correct.sum()) / int(data.test_mask.sum())
    return test_acc #返回的是测试准确率

for epoch in range(1, 201):
    loss = train()
    print(f"Epoch:{epoch},Loss:{loss:.4f}")

输出训练损失
在这里插入图片描述
调用训练好的model,查看测试准确率

test_acc = test()
print(f"Test Accuracy:{test_acc:.4f}")

3. GCN模型

这里只需要把全连接层替换成GCN层

from torch_geometric.nn import GCNConv

class GCN(torch.nn.Module):
    def __init__(self, hidden_channels):
        super().__init__()
        torch.manual_seed(1234567)
        self.conv1 = GCNConv(dataset.num_features, hidden_channels)
        self.conv2 = GCNConv(hidden_channels,dataset.num_classes)
        
    # 为了公平,这里也用了两层,也用了relu激活函数
    def forward(self, x, edge_index):
        x = self.conv1(x,edge_index)
        x = x.relu()
        x = F.dropout(x, p=0.5, training=self.training)
        x = self.conv2(x, edge_index)
        return x
    
model = GCN(hidden_channels=16)
print(model)

输出网络结构
在这里插入图片描述
先可视化未训练的模型,由于输出是7维向量,所以降维成2维进行展示

model = GCN(hidden_channels=16)
model.eval()

out = model(data.x, data.edge_index)
visualize(out, color = data.y)

在这里插入图片描述
训练模型

model = GCN(hidden_channels=16)
critation = torch.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 = critation(out[data.train_mask], data.y[data.train_mask])
    loss.backward()
    optimizer.step()
    return loss

def test():
    model.eval()
    out = model(data.x, data.edge_index)
    pred = out.argmax(dim=1)
    test_correct = pred[data.test_mask] == data.y[data.test_mask]
    test_acc = int(test_correct.sum()) / int(data.test_mask.sum())
    return test_acc

for epoch in range(1,101):
	loss = train()
	print(f"Epoch:{epoch},Loss:{loss:.4f}")

输出训练损失
在这里插入图片描述
计算测试准确率

test_acc = test()
print(f"Test Accuracy:{test_acc:.4f}")

训练后的可视化展示

model.eval()

out = model(data.x, data.edge_index)
visualize(out, color=data.y)

在这里插入图片描述

疑问:为什么这里没有用线性层做全连接呢?

参考:

《深入浅出图神经网络》GNN原理解析
官方PyG的API文档

  • 21
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
GCN (Graph Convolutional Networks) 是一种用于数据的深度学习模型,而 torch_geometric 是一个基于 PyTorch神经网络库,提供了实现 GCN 的工具和函数。 要使用 torch_geometric 实现 GCN,首先需要安装 torch_geometric 库。可以通过以下命令使用 pip 进行安装: ``` pip install torch_geometric ``` 安装完成后,可以按照以下步骤实现 GCN: 1. 导入所需的库和模块: ```python import torch import torch.nn.functional as F from torch_geometric.nn import GCNConv ``` 2. 创建 GCN 模型类: ```python class GCN(torch.nn.Module): def __init__(self, num_features, num_classes): super(GCN, self).__init__() self.conv1 = GCNConv(num_features, 16) # 输入特征数为 num_features,输出特征数为 16 self.conv2 = GCNConv(16, num_classes) # 输入特征数为 16,输出特征数为 num_classes def forward(self, x, edge_index): x = self.conv1(x, edge_index) x = F.relu(x) x = F.dropout(x, training=self.training) x = self.conv2(x, edge_index) return F.log_softmax(x, dim=1) ``` 3. 创建数据集和加载数据: ```python from torch_geometric.datasets import Planetoid dataset = Planetoid(root='/tmp/Cora', name='Cora') data = dataset[0] ``` 4. 创建模型实例和优化器: ```python model = GCN(dataset.num_features, dataset.num_classes) optimizer = torch.optim.Adam(model.parameters(), lr=0.01, weight_decay=5e-4) ``` 5. 训练模型: ```python def train(): model.train() optimizer.zero_grad() output = model(data.x, data.edge_index) loss = F.nll_loss(output[data.train_mask], data.y[data.train_mask]) loss.backward() optimizer.step() for epoch in range(200): train() ``` 这是一个简单的 GCN 实现示例,可以根据具体需求进行修改和扩展。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值