关于Pytorch和Pytorch Geometric(PyG)框架下重现GCN代码的理解

本文详细介绍了如何使用PyTorchGeometric(PyG)框架实现GraphConvolutionalNetworks(GCN),包括对MessagePassing的理解,Cora数据集的处理,以及GCN层和网络模型的具体代码分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

一. 理解MessagePassing

二. 关于数据集的处理(以Cora为例)

三. 重现GCN代码分析


最近学习了,Pytorch和Pytorch Geometric(PyG)框架下重现SEMI-SUPERVISED CLASSIFICATION WITH GRAPH CONVOLUTIONAL NETWORKS的代码,下面是关于Pytorch Geometric及代码的理解。

一. 理解MessagePassing

PyTorch Geometric provides the torch_geometric.nn.MessagePassing base class

1. Creating Message Passing Networks

将卷积运算符推广到不规则域通常表示为邻域聚合或消息传递方案:

       (1)

:代表节点i在第(k)层的特征

:代表(可选)代表边i,j的特征

其中:代表可微分置换不变函数,例如(求和、求平均值、求最大值),而γϕ为微分函数,例如MLP(多层感知器)

2. The “MessagePassing” Base Class

PyTorch Geometric provides the torch_geometric.nn.MessagePassing base class

定义卷积层的时候继承此基类,只需要定义函数ϕ,比如:message(),和γ函数,比如:update(),以及the aggregation scheme to use,比如:aggr='add', aggr='mean' or aggr='max'

 

3.Implementing the GCN Layer

The GCN layer is mathematically defined as

其中,邻域节点特征首先转化为权重矩阵Θ,被度归一化,然后加起来,这个公式,被分解为如下5步:

  1. Add self-loops to the adjacency matrix.
  2. Linearly transform node feature matrix.
  3. Normalize node features in ϕ.
  4. Sum up neighboring node features (“add” aggregation).
  5. Return new node embeddings in γ.

Steps 1-2 are typically computed before message passing takes place. Steps 3-5 can be easily processed using the (torch_geometric.nn.MessagePassing) base class. 

二. 关于数据集的处理(以Cora为例)

1.导入数据集

import os
import os.path as osp
from torch_geometric.datasets import Planetoid
import torch_geometric.transforms as T

dataset = 'Cora'
path = osp.join(osp.dirname(osp.realpath('__file__')), 'data', dataset)

##加载数据集
dataset = Planetoid(path, dataset, T.NormalizeFeatures())
data = dataset[0]

2. 测试节点特征已归一化

dataset[0]中包含了,数据集的所有信息,具体如下:

(1)data.x (节点特征,大小为2708*1433,tensor)

###测试####测试特征归一化  data.x已经归一化 
x = data.x
nonzero_x = torch.nonzero(x[0])
nonzero_x = torch.nonzero(x[1])
print(torch.nonzero(x[0]))
print(torch.nonzero(x[1]))
print(torch.nonzero(x[0]).shape)
print(torch.nonzero(x[1]).shape)
print(x[0,19])
print(x[1,19])

结果如下: 

tensor([[  19],
        [  81],
        [ 146],
        [ 315],
        [ 774],
        [ 877],
        [1194],
        [1247],
        [1274]])
tensor([[  19],
        [  88],
        [ 149],
        [ 212],
        [ 233],
        [ 332],
        [ 336],
        [ 359],
        [ 472],
        [ 507],
        [ 548],
        [ 687],
        [ 763],
        [ 808],
        [ 889],
        [1058],
        [1177],
        [1254],
        [1257],
        [1262],
        [1332],
        [1339],
        [1349]])
torch.Size([9, 1])
torch.Size([23, 1])
tensor(0.1111)
tensor(0.0435)

(2) 图结构及其他

print("dataset.num_features:",data)
print("dataset.num_features:",dataset.num_features)
#特征数:1433
print("dataset.num_features:",dataset.num_features)
#分类数:7
print("dataset.num_classes:",dataset.num_classes)

#查看张量X的小大([2708, 1433]),2708个节点,每个节点1433个特征
print("data.x.shape:",data.x.shape)
print("data.edge_index.shap:",data.edge_index.shape)
##edge_index 代表边([2, 10556]),有边的节点对
x, edge_index = data.x, data.edge_index
print("edge_index.shape:",edge_index.shape)

##Edge feature matrix with shape [num_edges, num_edge_features]

print("data.edge_attr:",data.edge_attr)

#输出结果,2708 每个节点的标签分类情况
print("data.y:",data.y)
print("data.y[data.train_mask]",data.y[data.train_mask])

##有向图还是无向图
print("data.is_undirected:",data.is_undirected())

###train_mask denotes against which nodes to train (140 nodes)
##val_mask denotes which nodes to use for validation, e.g., to perform early stopping (500 nodes)
##test_mask denotes against which nodes to test (1000 nodes)
print(data.train_mask.sum().item())
print(data.val_mask.sum().item())
print(data.test_mask.sum().item())

print("data.train_mask:",data.train_mask)

结果显示:

dataset.num_features: Data(edge_index=[2, 10556], test_mask=[2708], train_mask=[2708], val_mask=[2708], x=[2708, 1433], y=[2708])
dataset.num_features: 1433
dataset.num_features: 1433
dataset.num_classes: 7
data.x.shape: torch.Size([2708, 1433])
data.edge_index.shap: torch.Size([2, 10556])
edge_index.shape: torch.Size([2, 10556])
data.edge_attr: None
data.y: tensor([3, 4, 4,  ..., 3, 3, 3])
data.y[data.train_mask] tensor([3, 4, 4, 0, 3, 2, 0, 3, 3, 2, 0, 0, 4, 3, 3, 3, 2, 3, 1, 3, 5, 3, 4, 6,
        3, 3, 6, 3, 2, 4, 3, 6, 0, 4, 2, 0, 1, 5, 4, 4, 3, 6, 6, 4, 3, 3, 2, 5,
        3, 4, 5, 3, 0, 2, 1, 4, 6, 3, 2, 2, 0, 0, 0, 4, 2, 0, 4, 5, 2, 6, 5, 2,
        2, 2, 0, 4, 5, 6, 4, 0, 0, 0, 4, 2, 4, 1, 4, 6, 0, 4, 2, 4, 6, 6, 0, 0,
        6, 5, 0, 6, 0, 2, 1, 1, 1, 2, 6, 5, 6, 1, 2, 2, 1, 5, 5, 5, 6, 5, 6, 5,
        5, 1, 6, 6, 1, 5, 1, 6, 5, 5, 5, 1, 5, 1, 1, 1, 1, 1, 1, 1])
data.is_undirected: True
140
500
1000
data.train_mask: tensor([ True,  True,  True,  ..., False, False, False])

三. 重现GCN代码分析

1.定义卷积层

class GCNConv(MessagePassing):
    def __init__(self, in_channels, out_channels):
        super(GCNConv, self).__init__(aggr='add')  # "Add" aggregation.
        self.lin = torch.nn.Linear(in_channels, out_channels)

    def forward(self, x, edge_index):
        # x has shape [N, in_channels=1433]
        # edge_index has shape [2, E]

        # Step 1: Add self-loops to the adjacency matrix.
        ## x.size(0)=2708
        edge_index, _ = add_self_loops(edge_index, num_nodes=x.size(0))

        ##print("x = self.lin(x)***:",x,x.shape)
        ##2708*1433
        ##print(x)
        
        # Step 2: Linearly transform node feature matrix.2708*1433
        x = self.lin(x)

        ##2708*16
        ##print("x = self.lin(x):",x,x.shape)
        
        # Step 3-5: Start propagating messages.
        return self.propagate(edge_index, size=(x.size(0), x.size(0)), x=x)

    def message(self, x_j, edge_index, size):
        # x_j has shape [E, out_channels]

        # Step 3: Normalize node features.
        row, col = edge_index
        
        #计算每个结点的度
        deg = degree(row, size[0], dtype=x_j.dtype)
        deg_inv_sqrt = deg.pow(-0.5)
        
        ##
        norm = deg_inv_sqrt[row] * deg_inv_sqrt[col]
        

        ##1*(10556+2708=13264) * (10556+2708=13264)*out_channels = 1*out_channels
        return norm.view(-1, 1) * x_j

    def update(self, aggr_out):
        # aggr_out has shape [N=2708, out_channels]

        # Step 5: Return new node embeddings.
        return aggr_out

2.定义网络模型

class Net(torch.nn.Module):
    #torch.nn.Module 是所有神经网络单元的基类
    def __init__(self):
        super(Net, self).__init__()###复制并使用Net的父类的初始化方法,即先运行nn.Module的初始化函数
        self.conv1 = GCNConv(dataset.num_node_features, 16)
        self.conv2 = GCNConv(16, dataset.num_classes)

    def forward(self):
        x, edge_index = data.x, data.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.设置GPU,并定义优化器

##############################设置GPU、定义优化器#############################
device = torch.device('cpu')
#device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model, data = Net().to(device), data.to(device)
optimizer = torch.optim.Adam(model.parameters(), lr=0.01, weight_decay=5e-4)

4.定义训练函数

##############################定义训练函数#############################
def train():
    model.train()
     # 在反向传播之前,先将梯度归0
    optimizer.zero_grad()
    # 将误差反向传播
    F.nll_loss(model()[data.train_mask], data.y[data.train_mask]).backward()
    # 更新参数
    optimizer.step()

5.定义测试函数

##############################定义测试函数#############################
def test():
    model.eval()
    logits, accs = model(), []
    for _, mask in data('train_mask', 'val_mask', 'test_mask'):
        pred = logits[mask].max(1)[1]
        acc = pred.eq(data.y[mask]).sum().item() / mask.sum().item()
        accs.append(acc)
    return accs

6.训练并测试模型

##############################训练并测试函数#############################
best_val_acc = test_acc = 0
for epoch in range(1, 201):
    train()
    train_acc, val_acc, tmp_test_acc = test()
    if val_acc > best_val_acc:
        best_val_acc = val_acc
        test_acc = tmp_test_acc
        
    #打印有哪些参与训练的参数
    for name, param in model.named_parameters():
        if param.requires_grad:
            print(name)
            
    log = 'Epoch: {:03d}, Train: {:.4f}, Val: {:.4f}, Test: {:.4f}'
    print(log.format(epoch, train_acc, best_val_acc, test_acc))

时间仓促,后续补充更新

<think>好的,我现在需要帮助用户了解使用PyTorch GeometricPyG)库。首先,用户可能对PyG的基本概念用途不太清楚,所以应该先介绍PyG是什么,以及它在图神经网络中的作用。需要提到PyG是基于PyTorch的,方便处理图结构数据,这对处理社交网络、分子结构等非欧几里得数据很有用。 接下来,用户可能需要安装指南。根据提供的引用[3],安装PyG需要先正确安装PyTorch,尤其是CUDA版本。这里需要分步骤说明,比如创建conda环境、安装PyTorch,然后再安装PyG的相关依赖,比如torch-scatter、torch-sparse等。要强调依赖安装的顺序可能遇到的问题,比如版本兼容性,可以参考引用[2]提到的官网检查步骤。 然后,用户可能需要一些基本用法的示例。比如如何加载数据集,构建数据对象,定义图神经网络模型,训练测试流程。需要引用[1]中的torch_geometric.datasets来展示如何载入标准数据集,如Cora。同时,展示一个简单的GCN模型代码示例,说明如何继承MessagePassing类,定义消息传递更新函数。 用户可能还关心文档资源,这里需要提到官方文档的重要性,引用[1]中的文档链接,并推荐教程社区资源,如GitHub仓库论坛。此外,可以补充一些常见问题,比如处理自定义数据集的方法,或者如何调试模型。 最后,根据用户问题生成的相关问题需要考虑他们可能的后续需求,比如安装问题、模型定义、数据集处理应用场景。这些问题需要覆盖用户在使用PyG过程中可能遇到的各个阶段,帮助他们进一步深入学习。 在回答过程中,要确保所有数学公式代码块符合格式要求,比如行内公式用$...$,独立公式用$$...$$,代码块用三个反引号包裹。同时,引用要自然地插入到相关内容末尾,如引用[1]、[2]、[3]分别对应文档、安装检查conda安装步骤。 检查是否有遗漏的部分,比如是否涵盖了主要功能、安装步骤、基本用法、文档资源常见问题。确保语言简洁明了,结构清晰,帮助用户一步步解决问题。同时,注意不要使用Markdown格式,保持回答的自然流畅。</think>### PyTorch GeometricPyG)库的使用与文档指南 PyTorch GeometricPyG)是基于PyTorch的扩展库,专门用于处理图结构数据(如社交网络、分子结构、推荐系统等)的深度学习任务[^1]。以下是关键使用步骤文档资源: --- ### 1. **安装PyTorch Geometric** 在安装PyG前,需确保已正确安装PyTorch(建议通过conda管理环境): ```bash # 创建conda环境(示例) conda create -n pyg_env python=3.9 conda activate pyg_env # 安装PyTorch(根据CUDA版本选择命令,此处以CUDA 11.3为例) conda install pytorch torchvision torchaudio cudatoolkit=11.3 -c pytorch # 安装PyG依赖库 pip install torch-scatter torch-sparse torch-cluster torch-spline-conv -f https://data.pyg.org/whl/torch-1.10.0+cu113.html pip install torch-geometric ``` 安装后可通过`import torch_geometric`验证是否成功[^3]。 --- ### 2. **核心功能与基本用法** #### (1) **加载数据集** PyG内置多种图数据集(如Cora、PubMed),通过`torch_geometric.datasets`加载: ```python from torch_geometric.datasets import Planetoid dataset = Planetoid(root='/tmp/Cora', name='Cora') data = dataset[0] # 获取第一个图数据 print(f"节点数: {data.num_nodes}, 边数: {data.num_edges}, 特征维度: {data.num_features}") ``` 输出示例:`节点数: 2708, 边数: 10556, 特征维度: 1433`[^1]。 #### (2) **定义图神经网络(GNN)** PyG提供`MessagePassing`类实现图卷积操作。以下是一个简单的GCN层示例: ```python import torch from torch_geometric.nn import MessagePassing class GCNConv(MessagePassing): def __init__(self, in_channels, out_channels): super().__init__(aggr='add') # 聚合方式为求 self.lin = torch.nn.Linear(in_channels, out_channels) def forward(self, x, edge_index): # x: 节点特征矩阵 [num_nodes, in_channels] x = self.lin(x) return self.propagate(edge_index, x=x) def message(self, x_j): # x_j: 邻居节点特征 [num_edges, out_channels] return x_j ``` #### (3) **训练模型** 完整训练流程示例(以节点分类任务为例): ```python import torch.nn.functional as F from torch_geometric.nn import GCNConv class GCN(torch.nn.Module): def __init__(self): super().__init__() self.conv1 = GCNConv(dataset.num_features, 16) self.conv2 = GCNConv(16, dataset.num_classes) def forward(self, data): x, edge_index = data.x, data.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) model = GCN() optimizer = torch.optim.Adam(model.parameters(), lr=0.01) def train(): model.train() optimizer.zero_grad() out = model(data) loss = F.nll_loss(out[data.train_mask], data.y[data.train_mask]) loss.backward() optimizer.step() return loss.item() ``` --- ### 3. **文档与资源** - **官方文档**:详细API说明教程见[PyG文档](https://pytorch-geometric.readthedocs.io/)。 - **GitHub仓库**:源码与示例代码参考[PyG GitHub](https://github.com/pyg-team/pytorch_geometric)。 - **社区支持**:问题讨论可访问[PyG论坛](https://discuss.pytorch.geometric.live/)。 --- ### 常见问题 - **Q**: 如何处理自定义图数据? **A**: 使用`torch_geometric.data.Data`类构建,需定义节点特征、边索引等属性: ```python from torch_geometric.data import Data edge_index = torch.tensor([[0, 1], [1, 2]], dtype=torch.long) x = torch.tensor([[1], [2], [3]], dtype=torch.float) data = Data(x=x, edge_index=edge_index.t().contiguous()) ``` - **Q**: 如何调试GPU兼容性问题? **A**: 运行`torch.cuda.is_available()`检查CUDA是否可用,确保PyTorchPyG版本匹配[^2]。 ---
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值