• GNN PyG~torch_geometric 学习理解


    目录

    1. PyG Introduction

    2. PyG Installation

    2.1 PyG 安装常见错误及原因

    2.2 PyG 具体安装步骤

    3. torch_geometric packages

    torch_geometric.data.Data

    Dataset 与 DataLoader

    Dropout、BatchNorm

    3. torch_geometric: 理解edge_index

    3.1 理解 mini-batch edge index for GNN models

    torch_geometric.loader.NeighborSampler

    GraphSAGE model base on mini-batch edge_index

    3.2 理解 full node embedding + full edge index

    3.2.1 GCN

    full edge index train方式:

    full edge index test

     3.2.2 GraphSAGE

    3.2.3 GAT

    4. PyG 实现的 GNN models

    4.1  基于MessagePassing + propagate 构建的GCN模型

    4.2 Graph Transformer with mini-batch edge_index

    10. Torch-geometric errors

    torch_geometric报错(一): "找不到指定的模块"

    Reference​​​​​​​​​​​​​


    1. PyG Introduction

    GNN, Graph Neural Network,也称为Graph Machine Learning

    PyG 就是PyTorch Geometric package, 它集成了经典的和最新的GNN algorithms, such as GCN、GraphSAGE、GAT、Graph Transformer, etc. 

    它可以让GNN研究人员安心做一个“调包侠”,而不用自己费心费力去实现和debug,而且自己写的GNN model还可能是错的,你哭不哭?

    2. PyG Installation

    这里涉及的PyG安装包括: torch_cluster、torch_scatter、torch_sparse、torch_spline_conv和torch_geometric

    2.1 PyG 安装常见错误及原因

    不兼容问题、版本冲突问题

    OSError: /home/zh1995/anaconda3/lib/python3.6/site-packages/torch_sparse/_version_cuda.so: undefined symbol: _ZN3c106detail23torchInternalAssertFailEPKcS2_jS2_S2_

    这是因为安装了多个pytorch或多个CUDA版本的原因。通过anaconda-navigator安装pytorch 时它将自动安装cpu和gpu两个版本,因此产生错误。

    不要通过来安装Torch,而是通过pip从以下网址下载特定版本的pytorch: Installation — pytorch_geometric documentation

    2.2 PyG 具体安装步骤

    参考:     python安装torch-geometric包出现的错误:OSError: torch_sparse/_version_cuda.so: undefined symbol:_oserror: /home/ubuntu/anaconda3/lib/python3.10/sit-CSDN博客

    安装完torch geometric,import torch_geometric然后报错:OSError: [WinError 127] 找不到指定的模块-CSDN博客

    PyG 2.3 requires that at least PyTorch 1.12 is installed. 

    所以,torch版本最好≥1.12.0

    1. 卸载原有的包: pip uninstall 相应的依赖包,如torch、torch-scatter、torch-sparse。

    2. 查询系统兼容的torch和CUDA版本。

    torch指的是cpu版本;torch-cuda是GPU版本。如果需要在服务器上跑代码,就需要安装torch-cuda版本。 torch对应的cuda版本查询地址:Previous PyTorch Versions | PyTorch

    3. 确定好torch-cuda版本后,就需要确定兼容的PyG版本了。

    torch_cluster、torch_sparse、torch_scatter等版本查询地址:https://data.pyg.org/whl/index.html

     4. 一定要用pip安装!!!安装指令和顺序如下:

    需要指定package version和torch-cuda version!

    比如:基于torch-1.12.0+cuda102版本安装PyG packages

    pip install torch-scatter==2.0.9 -f https://pytorch-geometric.com/whl/torch-1.12.0+cu102.html

    安装其他的包时,需要替换对应的package name and version。

    5. 最后安装torch-geometric package

    它不用指定版本

    pip install torch-geometric -f https://pytorch-geometric.com/whl/torch-1.12.0+cu102.html

    3. torch_geometric packages

    参考:

    图神经网络 PyTorch Geometric 入门教程 - 知乎

    torch_geometric.data.Data

    节点和节点之间的边构成了图。所以在 PyG 中,如果你要构建图,那么需要两个要素:节点和边。PyG 提供了torch_geometric.data.Data (下面简称Data) 用于构建图,包括 5 个属性,每一个属性都不是必须的,可以为空。

    • x: 用于存储每个节点的特征,形状是[num_nodes, num_node_features]
    • edge_index: 用于存储节点之间的边,形状是 [2, num_edges]
    • pos: 存储节点的坐标,形状是[num_nodes, num_dimensions]
    • y: 存储样本标签。如果是每个节点都有标签,那么形状是[num_nodes, *];如果是整张图只有一个标签,那么形状是[1, *]
    • edge_attr: 存储边的特征。形状是[num_edges, num_edge_features]

    Dataset 与 DataLoader

    PyG 的 Dataset继承自torch.utils.data.Dataset,自带了很多图数据集,我们以TUDataset为例,通过以下代码就可以加载数据集,root参数设置数据下载的位置。通过索引可以访问每一个数据。

    1. from torch_geometric.datasets import TUDataset
    2. dataset = TUDataset(root='/tmp/ENZYMES', name='ENZYMES')
    3. data = dataset[0]

     在一个图中,由edge_indexedge_attr可以决定所有节点的邻接矩阵。PyG 通过创建稀疏的对角邻接矩阵,并在节点维度中连接特征矩阵和 label 矩阵,实现了在 mini-batch 的并行化。PyG 允许在一个 mini-batch 中的每个Data (图) 使用不同数量的节点和边。​​​​​​​​​​​​​​

    Dropout、BatchNorm

     PyG实现GAT,使用封装好的GATConv函数。

    • model.train()和model.eval()分别定义模型的训练模型和测试模式,主要对Dropout层和BatchNorm产生影响。
    • Dropout: 训练过程中,为防止模型过拟合,增加其泛化性,会随机屏蔽掉一些神经元,相当于每次经过不同的神经元,最终得到不同的模型。测试模式时,所有神经元共同作用,类似于boosting。
    • BatchNorm: 训练过程中,模型每处理一次minibatch数据,BN层根据一个minibatch来计算mean和std后做归一化处理。测试时,BN层会利用训练时得到的参数来处理测试数据。如果不设置model.eval(),输入单个数据,模型会报错。

    3. torch_geometric: 理解edge_index

    参考: 

    pytorch geometric教程四 利用NeighorSampler实现节点维度的mini-batch + GraphSAGE样例-CSDN博客

    3.1 理解 mini-batch edge index for GNN models

    PyG的官方文档中有mini-batchAdvanced mini-batching两部分内容,但实现的都是图维度的mini-batch。如何像GraphSAGE paper中对minibatch的节点进行邻居采样并训练模型,使得大规模全连接图的GNNs模型训练成为可能,PyG是通过torch_geometric.loader.NeighborSampler实现的(早一点版本是torch_geometric.data.NeighborSampler)。
    需要注明的一点是,这篇文章中虽然举了SAGEConv的代码样例,但只要卷积层支持bipartite图,大家举一反三,就可以与NeighborSampler结合使用,实现节点维度的mini-batch模型训练与推断

    torch_geometric.loader.NeighborSampler
    from torch_geometric.loader import NeighborSampler, RandomNodeSampler

    A data loader that performs neighbor sampling as introduced in the "Inductive Representation Learning on Large Graphs" paper. 它允许对GNNs mini-batch training进行neighbor采样,并训练模型,使得大规模全连接的GNNs模型训练成为可能。

    核心想法

    NeighborSampler的核心想法是,给定mini-batch的节点和图卷积的层数L,以及每一层需要采样的邻居数目sizes,依次从第一层到第L层,对每一层进行邻居采样并返回一个bipartite子图。sizes是一个L长度的list,包含每一层需要采样的邻居个数。以下是主要逻辑的归纳:
    For i in L:

    • 1层使用初始minibatch的节点进行邻居采样,返回采样结果。
    • i (i>0)层,使用上层采样中涉及到的所有节点进行邻居采样,返回采样结果。   

    i层采样完成后,返回结果(batch_size, n_id, adjs),其中batch_size就是mini-batch的节点数目,n_id是包含所有在L层卷积中遇到的节点的list,且target节点在n_id前几位。adjs是一个list,包含了第L层到第1层采样的结果,所以adjs中的子图是从大到小的。每一层采样返回的结果具体形式为(edge_index, e_id, size)。其中edge_index是采样得到的bipartite子图中source节点到target节点的边。e_id是edge_index的边在原始大图中的IDs, the index of node index,就是bipartite子图的shape。

    以下是一个2层采样的示意图,注意在第2层采样的时候,使用了第1层中涉及到的所有节点,包括出发点
    在这里插入图片描述

    返回结果 of NeighborSampler:

    • batch_size
    • n_id: L层采样中遇到的所有的节点的list,其中target节点在list最前端。
    • adjs: 第L层到第1层采样结果的list
      • edge_index: 采样得到的bipartite子图中source节点到target节点的边。
      • e_id: edge_index的边在原始大图中的IDs
      • size: bipartite子图中的shape。

    参数

    1. class NeighborSampler(torch.utils.data.DataLoader):
    2. def __init__(self, edge_index: Union[Tensor, SparseTensor],
    3. sizes: List[int], node_idx: Optional[Tensor] = None,
    4. num_nodes: Optional[int] = None, return_e_id: bool = True,
    5. transform: Callable = None, **kwargs):
    • edge_index (Tensor or SparseTensor):图的边信息,可以是Tensor,也可以是SparseTensor。
    • sizes ([int]):note that 这是个list!!每一层需要采样的邻居数目,如果是-1的话,选取所有的邻居
    • node_idx (LongTensor, optional):提供需要被采样节点的信息,比如模型训练的时候,只给出数据集train中的节点。在预测的时候,使用None,考虑所有的节点。
    • num_nodes: Optional[int] = None:图中节点的数目,可选参数。
    • return_e_id: bool = True:当设为False的时候,不会返回partite子图的边在原图中的IDs。
    • transform
    • **kwargs:NeighborSampler是torch.utils.data.DataLoader的子类,所以父类DataLoader的参数NeighborSampler都可以使用,比如:batch_size, shuffle, num_workers。
    GraphSAGE model base on mini-batch edge_index

    模型训练
    train_loader
    首先代码里定义了模型训练时的数据加载器train_loader。 node_idx=data.train_mask指定只对训练集的节点进行邻居采样,sizes=[25, 10]指明了这是一个两层的卷积,第一层卷积采样邻居数目25,第二层卷积采样邻居数目10。batch_size=1024指定了mini-batch的节点数目,每次只对1024个节点进行采样。

    1. train_loader = NeighborSampler(data.edge_index, node_idx=data.train_mask,
    2. sizes=[25, 10], batch_size=1024, shuffle=True,
    3. num_workers=12)

     train
    train_loader每次返回一个batch_size节点邻居采样的结果,其形式是(batch_size, n_id, adjs),其中n_id是采样过程中涉及的所有节点的id,也是adjs中涉及的所有节点,因此x[n_id]是所有相关节点的特征。而且x[n_id]相当于做了一次映射,x[n_id]中第i行就是adjs中i节点的特征(关于这一点,会在后面NeighborSampler工作原理部分详述)。adjs是包含了所有bipartite子图边信息的list。所以model(x[n_id], adjs)传入了所有bipartite子图的节点特征和边信息。
    另外NeighborSampler是在CPU中完成的,所以返回的结果都在CPU上。如果用GPU训练模型,要记得将loader的结果放到GPU上。

    1. def train(epoch):
    2. model.train()
    3. total_loss = total_correct = 0
    4. for batch_size, n_id, adjs in train_loader:
    5. # `adjs` holds a list of `(edge_index, e_id, size)` tuples.
    6. adjs = [adj.to(device) for adj in adjs]
    7. optimizer.zero_grad()
    8. out = model(x[n_id], adjs)
    9. loss = F.nll_loss(out, y[n_id[:batch_size]])
    10. loss.backward()
    11. optimizer.step()
    12. total_loss += float(loss)
    13. total_correct += int(out.argmax(dim=-1).eq(y[n_id[:batch_size]]).sum())
    14. loss = total_loss / len(train_loader)
    15. approx_acc = total_correct / int(data.train_mask.sum())
    16. return loss, approx_acc

    model中的forward()函数
    forward函数依次实现了从第L层到第1层采样得到的bipartite子图的卷积。
    adjs包含L层邻居采样的bipartite子图:(edge_index, e_id, size)。在上一个教程中讲过了,SAGEConv是支持bipartite图的。对bipartite图进行卷积时,输入的x是一个tuple: (x_source, x_target)。上面提到过,n_id是包含所有在L层卷积中遇到的节点的list,且target节点在n_id前几位。而bipartite图的size是(num_of_source_nodes, num_of_target_nodes),因此对每一层的bipartite图都有x_target = x[:size[1]] 。所以 self.convs[i]((x, x_target), edge_index)实现了对一层bipartite图的卷积。

    1. class SAGE(torch.nn.Module):
    2. def __init__(self, in_channels, hidden_channels, out_channels, num_layers):
    3. super(SAGE, self).__init__()
    4. self.num_layers = num_layers
    5. ...
    6. def forward(self, x, adjs):
    7. for i, (edge_index, _, size) in enumerate(adjs):
    8. x_target = x[:size[1]] # Target nodes are always placed first.
    9. x = self.convs[i]((x, x_target), edge_index)
    10. if i != self.num_layers - 1:
    11. x = F.relu(x)
    12. x = F.dropout(x, p=0.5, training=self.training)
    13. return x.log_softmax(dim=-1)

     NeighborSampler工作原理&具体实例

    上文只是简略讲了NeighborSampler的工作原理,这里用几个实例,让大家更清楚地理解其中的细节。
    首先用networkx建以下一张图:

    1. import networkx as nx
    2. graph = nx.Graph()
    3. graph.add_edges_from([(0,1), (1,2), (1,3), (2,3), (3,4), (4,2)])
    4. nx.draw_kamada_kawai(graph, with_labels=True)

     

     将其转换成PyG中的Data格式。

    1. from torch_geometric.data.data import Data
    2. from torch_geometric.utils import from_networkx
    3. data = from_networkx(graph)
    4. data.edge_index
    5. >>> tensor([[0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4],
    6. [1, 0, 2, 3, 1, 3, 4, 1, 2, 4, 2, 3]])

     batch_size =1 ,采样邻居数小于邻居数

    1. from torch_geometric.data import NeighborSampler
    2. loader = NeighborSampler(edge_index=data.edge_index, sizes=[2], node_idx=torch.tensor([2]), batch_size=1)
    3. next(iter(loader))
    4. >>> (1,
    5. tensor([2, 3, 1]),
    6. EdgeIndex(edge_index=tensor([[1, 2],
    7. [0, 0]]), e_id=tensor([8, 2]), size=(3, 1)))

    以上代码对2号节点取两个邻居。n_id: tensor([2, 3, 1])是遇到的所有节点,target节点在最前面,是2号节点,3, 1是采样到的邻居。edge_index=tensor([[1,2], [0,0]])是采样得到的bipartite子图。n_id中的index对应edge_index中的数值。edge_index[1]中是target节点,bipartite子图是从target节点开始计数的,所以n_id里面永远是target节点在前几位。另外size[0]是source节点的数目,size[1]是target节点的数目,所以n_id[size[1]]可以获取target节点在原图中的id。看以下示意图:

    知道了bipartite子图中的节点对应原图哪个节点后,还可以将bipartite子图中的边对应到原图中的边。看以下示意图: 

    [3, 2]和边[1, 2]分别是原图中的第8,第2条边,和返回的e_id相同。

    batch_size =1 ,采样邻居数大于邻居数

    以下结果可以看出,当采样邻居数大于邻居数的时候,NeighborSampler不会对邻居进行随机填充,如果打算sample 4个,但neighbor nodes只有3个,那就取这三个,不会用0填充。这是因为在源码中,作者将采样的replace设置成了False

    1. from torch_geometric.data import NeighborSampler
    2. loader = NeighborSampler(edge_index=data.edge_index, sizes=[4], node_idx=torch.tensor([2]), batch_size=1)
    3. next(iter(loader))
    4. >>>(1,
    5. tensor([2, 4, 1, 3]),
    6. EdgeIndex(edge_index=tensor([[1, 2, 3],
    7. [0, 0, 0]]), e_id=tensor([10, 2, 8]), size=(4, 1)))

     batch_size = [2, 2]

    1. from torch_geometric.data import NeighborSampler
    2. loader = NeighborSampler(edge_index=data.edge_index, sizes=[2, 2], node_idx=torch.tensor([2]), batch_size=1)
    3. next(iter(loader))
    4. >>> (1,
    5. tensor([2, 4, 1, 3, 0]),
    6. [EdgeIndex(edge_index=tensor([[2, 3, 0, 3, 0, 4],
    7. [0, 0, 1, 1, 2, 2]]), e_id=tensor([2, 8, 6, 9, 4, 0]), size=(5, 3)),
    8. EdgeIndex(edge_index=tensor([[1, 2],
    9. [0, 0]]), e_id=tensor([10, 2]), size=(3, 1))])

    其中EdgeIndex(edge_index=tensor([[1, 2], [0, 0]]), e_id=tensor([10, 2]), size=(3, 1))是第一层采样得到的bipartite图, EdgeIndex(edge_index=tensor([[2, 3, 0, 3, 0, 4], [0, 0, 1, 1, 2, 2]]), e_id=tensor([2, 8, 6, 9, 4, 0]), size=(5, 3))是第二层采样得到的bipartite图。
    可以看出来,第二层采样是建立在第一层采样的基础上的,第一层采样bipartite中所有的节点[0, 1, 2] (在原图中对应[2, 4, 1])作为第二层采样的出发点。所以edge_index[1]有[0, 0, 1, 1, 2, 2],这是将0, 1, 2作为target节点采样两个邻居的结果。

    3.2 理解 full node embedding + full edge index

    参考:

    使用Pytorch Geometric实现GCN、GraphSAGE和GAT - 知乎

    https://github.com/DGraphXinye/2022_finvcup_baseline/tree/master/models

    3.2.1 GCN
    1. import torch
    2. import torch.nn.functional as F
    3. # 导入GCN层、GraphSAGE层和GAT层
    4. from torch_geometric.nn import GCNConv, SAGEConv, GATConv
    5. from torch_geometric.datasets import Planetoid
    6. class GCN_NET(torch.nn.Module):
    7. def __init__(self, features, hidden, classes):
    8. super(GCN_NET, self).__init__()
    9. self.conv1 = GCNConv(features, hidden) # shape(输入的节点特征维度 * 中间隐藏层的维度)
    10. self.conv2 = GCNConv(hidden, classes) # shaape(中间隐藏层的维度 * 节点类别)
    11. def forward(self, data):
    12. # 加载节点特征和邻接关系
    13. x, edge_index = data.x, data.edge_index
    14. # 传入卷积层
    15. x = self.conv1(x, edge_index)
    16. x = F.relu(x) # 激活函数
    17. x = F.dropout(x, training=self.training) # dropout层,防止过拟合
    18. x = self.conv2(x, edge_index) # 第二层卷积层
    19. # 将经过两层卷积得到的特征输入log_softmax函数得到概率分布
    20. return F.log_softmax(x, dim=1)
    full edge index train方式:

    参考:

             GCN Pytorch实现(GCN、GraphSAGE、GAT) - 知乎

    1. device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    2. model = GCN().to(device)
    3. data = dataset[0].to(device)
    4. optimizer = torch.optim.Adam(model.parameters(), lr=0.01, weight_decay=5e-4)
    5. model.train()
    6. for epoch in range(200):
    7. optimizer.zero_grad()
    8. out = model(data)
    9. loss = F.nll_loss(out[data.train_mask], data.y[data.train_mask])
    10. loss.backward()
    11. optimizer.step()
    full edge index test
    1. model.eval()
    2. pred = model(data).argmax(dim=1)
    3. correct = (pred[data.test_mask] == data.y[data.test_mask]).sum()
    4. acc = int(correct) / int(data.test_mask.sum())
    5. print(f'Accuracy: {acc:.4f}')
    6. >>> Accuracy: 0.8150
     3.2.2 GraphSAGE
    1. import torch
    2. import torch.nn.functional as F
    3. # 导入GCN层、GraphSAGE层和GAT层
    4. from torch_geometric.nn import GCNConv, SAGEConv, GATConv
    5. from torch_geometric.datasets import Planetoid
    6. class GraphSAGE_NET(torch.nn.Module):
    7. def __init__(self, feature, hidden, classes):
    8. super(GraphSAGE_NET, self).__init__()
    9. self.sage1 = SAGEConv(feature, hidden) # 定义两层GraphSAGE层
    10. self.sage2 = SAGEConv(hidden, classes)
    11. def forward(self, data):
    12. x, edge_index = data.x, data.edge_index
    13. x = self.sage1(x, edge_index)
    14. x = F.relu(x)
    15. x = F.dropout(x, training=self.training)
    16. x = self.sage2(x, edge_index)
    17. return F.log_softmax(x, dim=1)
    3.2.3 GAT
    1. import torch
    2. import torch.nn.functional as F
    3. # 导入GCN层、GraphSAGE层和GAT层
    4. from torch_geometric.nn import GCNConv, SAGEConv, GATConv
    5. from torch_geometric.datasets import Planetoid
    6. class GAT_NET(torch.nn.Module):
    7. def __init__(self, features, hidden, classes, heads=4):
    8. super(GAT_NET, self).__init__()
    9. self.gat1 = GATConv(features, hidden, heads=4) # 定义GAT层,使用多头注意力机制
    10. self.gat2 = GATConv(hidden*heads, classes) # 因为多头注意力是将向量拼接,所以维度乘以头数。
    11. def forward(self, data):
    12. x, edge_index = data.x, data.edge_index
    13. x = self.gat1(x, edge_index)
    14. x = F.relu(x)
    15. x = F.dropout(x, training=self.training)
    16. x = self.gat2(x, edge_index)
    17. return F.log_softmax(x, dim=1)

    4. PyG 实现的 GNN models

    4.1  基于MessagePassing + propagate 构建的GCN模型

    参考:

            图神经网络之神器——PyTorch Geometric 上手 & 实战 - 知乎

    MessagePassing本质上是自己写模型,而不是调用现成的包!

    self.propagete机制是自动调用message函数!

    1. import torch
    2. from torch_geometric.nn import MessagePassing
    3. from torch_geometric.utils import add_self_loops, degree
    4. class GCNConv(MessagePassing):
    5. def __init__(self, in_channels, out_channels):
    6. super(GCNConv, self).__init__(aggr='add') # "Add" aggregation.
    7. self.lin = torch.nn.Linear(in_channels, out_channels)
    8. def forward(self, x, edge_index):
    9. # x has shape [N, in_channels]
    10. # edge_index has shape [2, E]
    11. # Step 1: Add self-loops to the adjacency matrix.
    12. edge_index, _ = add_self_loops(edge_index, num_nodes=x.size(0))
    13. # Step 2: Linearly transform node feature matrix.
    14. x = self.lin(x)
    15. # Step 3-5: Start propagating messages.
    16. return self.propagate(edge_index, size=(x.size(0), x.size(0)), x=x)
    17. def message(self, x_j, edge_index, size):
    18. # x_j has shape [E, out_channels]
    19. # Step 3: Normalize node features.
    20. row, col = edge_index
    21. deg = degree(row, size[0], dtype=x_j.dtype)
    22. deg_inv_sqrt = deg.pow(-0.5)
    23. norm = deg_inv_sqrt[row] * deg_inv_sqrt[col]
    24. return norm.view(-1, 1) * x_j
    25. def update(self, aggr_out):
    26. # aggr_out has shape [N, out_channels]
    27. # Step 5: Return new node embeddings.
    28. return aggr_out

    4.2 Graph Transformer with mini-batch edge_index

    1. mport torch
    2. import torch.nn as nn
    3. import torch.nn.functional as F
    4. from torch_geometric.nn import GATConv, GCNConv, TransformerConv # PyG封装好的GATConv函数
    5. from torch_geometric.nn.inits import glorot, ones, zeros
    6. from torch.nn import Linear, BatchNorm1d, Sequential, ModuleList, ReLU, Dropout, ELU
    7. class GraphFormer(nn.Module):
    8. '''
    9. adopt this module when using mini-batch
    10. '''
    11. def __init__(self, in_dim, hid_dim, out_dim, heads) -> None: # in_dim, 302; hid_dim, 128; out_dim, 64, heads, 4
    12. super(GraphFormer, self).__init__()
    13. self.GraphFormer1 = TransformerConv(in_channels=in_dim, out_channels=hid_dim, heads=heads, dropout=0.5) # 这只是__init__函数声明变量
    14. self.GraphFormer2 = TransformerConv(in_channels=hid_dim * heads, out_channels=hid_dim, dropout=0.5) # 隐藏层维度,输出维度64
    15. self.GraphFormer3 = TransformerConv(in_channels=hid_dim, out_channels=out_dim, dropout=0.5) # 隐藏层维度,输出维度64
    16. self.layers = ModuleList([self.GraphFormer1, self.GraphFormer2, self.GraphFormer3])
    17. self.norm = BatchNorm1d(heads * hid_dim) # 将num_features那一维进行归一化,防止梯度扩散
    18. def forward(self, x, adjs, device): # 这里的x是指batch node feature embedding, adjs是指RL_samplers 采样的batch node 子图 edge
    19. for i, (edge_index, _, size) in enumerate(adjs): # adjs list包含了从第L层到第1层采样的结果,adjs中子图是从大到小的。adjs(edge_index,e_id,size), edge_index是子图中的边
    20. # x: Tensor, edge_index: Tensor
    21. x, edge_index = x.to(device), edge_index.to(device) # x: (2703, 302); (2, 53005); -> x: (1418, 512); (2, 2329)
    22. x_target = x[:size[1]] # (1418, 302); (100, 512) Target nodes are always placed first; size是子图shape, shape[1]即子图 node number; x[:size[1], : ]即取出前n个node
    23. x = self.layers[i]((x, x_target), edge_index) # 这里调用的是forward函数, layers[1] output (1418, 512) out_dim * heads; layers[2] output (100, 64)
    24. if i == 0:
    25. x = self.norm(x) # 归一化操作,防止梯度散射
    26. x = F.elu(x) # 非线性激活函数elu
    27. x = F.dropout(x, training=self.training)
    28. del edge_index
    29. return x

    10. Torch-geometric errors

    torch_geometric报错(一): "找不到指定的模块"

    problem: 兼容性错误

    solution: 重装package

    pip install torch-scatter==2.0.9 -f https://pytorch-geometric.com/whl/torch-1.10.0+cpu.html

    Reference

  • 相关阅读:
    WebMail:网页注册成功发送邮件
    pat考完了
    Vue项目中ESLint配置(VScode)
    Python系列之:本地定时推送文件到指定服务器目录下
    tensorboard报错解决:No dashboards are active for the current data set
    C语言扫雷游戏完整实现(下)
    Java基础——运算符表达式
    Redis面试题总结
    谷歌AlphaFold近日宣称预测出地球上几乎所有蛋白质结构
    小白学编程(CSS):呼吸灯
  • 原文地址:https://blog.csdn.net/qq_33419476/article/details/133467260