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参数设置数据下载的位置。通过索引可以访问每一个数据。

from torch_geometric.datasets import TUDataset
dataset = TUDataset(root='/tmp/ENZYMES', name='ENZYMES')
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。

参数

class NeighborSampler(torch.utils.data.DataLoader):def __init__(self, edge_index: Union[Tensor, SparseTensor],sizes: List[int], node_idx: Optional[Tensor] = None,num_nodes: Optional[int] = None, return_e_id: bool = True,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个节点进行采样。

train_loader = NeighborSampler(data.edge_index, node_idx=data.train_mask,sizes=[25, 10], batch_size=1024, shuffle=True,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上。

def train(epoch):model.train()total_loss = total_correct = 0for batch_size, n_id, adjs in train_loader:# `adjs` holds a list of `(edge_index, e_id, size)` tuples.adjs = [adj.to(device) for adj in adjs]optimizer.zero_grad()out = model(x[n_id], adjs)loss = F.nll_loss(out, y[n_id[:batch_size]])loss.backward()optimizer.step()total_loss += float(loss)total_correct += int(out.argmax(dim=-1).eq(y[n_id[:batch_size]]).sum())loss = total_loss / len(train_loader)approx_acc = total_correct / int(data.train_mask.sum())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图的卷积。

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

 NeighborSampler工作原理&具体实例

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

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

 

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

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

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

from torch_geometric.data import NeighborSampler
loader = NeighborSampler(edge_index=data.edge_index, sizes=[2], node_idx=torch.tensor([2]), batch_size=1)
next(iter(loader))
>>> (1,tensor([2, 3, 1]),EdgeIndex(edge_index=tensor([[1, 2],[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

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

 batch_size = [2, 2]

from torch_geometric.data import NeighborSampler
loader = NeighborSampler(edge_index=data.edge_index, sizes=[2, 2], node_idx=torch.tensor([2]), batch_size=1)
next(iter(loader))
>>> (1,tensor([2, 4, 1, 3, 0]),[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)),EdgeIndex(edge_index=tensor([[1, 2],[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
import torch
import torch.nn.functional as F
# 导入GCN层、GraphSAGE层和GAT层
from torch_geometric.nn import GCNConv, SAGEConv, GATConv
from torch_geometric.datasets import Planetoidclass GCN_NET(torch.nn.Module):def __init__(self, features, hidden, classes):super(GCN_NET, self).__init__()self.conv1 = GCNConv(features, hidden)  # shape(输入的节点特征维度 * 中间隐藏层的维度)self.conv2 = GCNConv(hidden, classes)  # shaape(中间隐藏层的维度 * 节点类别)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)  # dropout层,防止过拟合x = self.conv2(x, edge_index)  # 第二层卷积层# 将经过两层卷积得到的特征输入log_softmax函数得到概率分布return F.log_softmax(x, dim=1)
full edge index train方式:

参考:

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

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

4. PyG 实现的 GNN models

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

参考:

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

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

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

import torch
from torch_geometric.nn import MessagePassing
from torch_geometric.utils import add_self_loops, degreeclass 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]# edge_index has shape [2, E]# Step 1: Add self-loops to the adjacency matrix.edge_index, _ = add_self_loops(edge_index, num_nodes=x.size(0))# Step 2: Linearly transform node feature matrix.x = self.lin(x)# 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_indexdeg = degree(row, size[0], dtype=x_j.dtype)deg_inv_sqrt = deg.pow(-0.5)norm = deg_inv_sqrt[row] * deg_inv_sqrt[col]return norm.view(-1, 1) * x_jdef update(self, aggr_out):# aggr_out has shape [N, out_channels]# Step 5: Return new node embeddings.return aggr_out

4.2 Graph Transformer with mini-batch edge_index

mport torch
import torch.nn as nn
import torch.nn.functional as F
from torch_geometric.nn import GATConv, GCNConv, TransformerConv  # PyG封装好的GATConv函数
from torch_geometric.nn.inits import glorot, ones, zerosfrom torch.nn import Linear, BatchNorm1d, Sequential, ModuleList, ReLU, Dropout, ELUclass GraphFormer(nn.Module):'''adopt this module when using mini-batch'''def __init__(self, in_dim, hid_dim, out_dim, heads) -> None:  # in_dim, 302; hid_dim, 128; out_dim, 64, heads, 4super(GraphFormer, self).__init__()self.GraphFormer1 = TransformerConv(in_channels=in_dim, out_channels=hid_dim, heads=heads, dropout=0.5)  # 这只是__init__函数声明变量self.GraphFormer2 = TransformerConv(in_channels=hid_dim * heads, out_channels=hid_dim, dropout=0.5)  # 隐藏层维度,输出维度64self.GraphFormer3 = TransformerConv(in_channels=hid_dim, out_channels=out_dim, dropout=0.5)  # 隐藏层维度,输出维度64self.layers = ModuleList([self.GraphFormer1, self.GraphFormer2, self.GraphFormer3])self.norm = BatchNorm1d(heads * hid_dim)  # 将num_features那一维进行归一化,防止梯度扩散def forward(self, x, adjs, device):  # 这里的x是指batch node feature embedding, adjs是指RL_samplers 采样的batch node 子图 edgefor i, (edge_index, _, size) in enumerate(adjs):  # adjs list包含了从第L层到第1层采样的结果,adjs中子图是从大到小的。adjs(edge_index,e_id,size), edge_index是子图中的边# x: Tensor, edge_index: Tensorx, edge_index = x.to(device), edge_index.to(device)  # x: (2703, 302); (2, 53005); -> x: (1418, 512); (2, 2329)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个nodex = self.layers[i]((x, x_target), edge_index)  # 这里调用的是forward函数, layers[1] output (1418, 512) out_dim * heads; layers[2] output (100, 64)if i == 0:x = self.norm(x)  # 归一化操作,防止梯度散射x = F.elu(x)  # 非线性激活函数elux = F.dropout(x, training=self.training)del edge_indexreturn 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

  • torch_geometric.nn — pytorch_geometric documentation
  • 图神经网络 PyTorch Geometric 入门教程 - 知乎
  • pytorch geometric教程四 利用NeighorSampler实现节点维度的mini-batch + GraphSAGE样例-CSDN博客最好的一篇PyG blog
  • 使用Pytorch Geometric实现GCN、GraphSAGE和GAT - 知乎
  • 图神经网络之神器——PyTorch Geometric 上手 & 实战 - 知乎
  • GCN Pytorch实现(GCN、GraphSAGE、GAT) - 知乎

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/94098.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【2023年11月第四版教材】第17章《干系人管理》(合集篇)

第17章《干系人管理》(合集篇) 1 章节内容2 管理基础3 管理过程3.1 管理的过程★★★ (22上44)3.2 管理ITTO汇总★★★ 4 过程1-识别干系人4.1 数据收集★★★4.3数据分析4.4 权力利益方格4.5 数据表现:干系人映射分析…

记录UNIAPP打包苹果iOS·APP

用到生成的四个文件:1-1.CSR证书文件、2-2.CER证书文件、3-3.PP文件【证书Profiles文件】、4-4.P12文件【证书私钥】 1. 生成CSR证书文件: 2. 操作苹果后台:Sign In - Applehttps://developer.apple.com/account/resources/certificates/list

使用CrawlSpider爬取全站数据。

CrawpSpider和Spider的区别 CrawlSpider使用基于规则的方式来定义如何跟踪链接和提取数据。它支持定义规则来自动跟踪链接,并可以根据链接的特征来确定如何爬取和提取数据。CrawlSpider可以对多个页面进行同样的操作,所以可以爬取全站的数据。CrawlSpid…

PHP图片文件管理功能系统源码

文件图库管理单PHP源码直接解压就能用,单文件,indexm.php文件可以重新命名,上传到需要访问的目录中, 可以查看目录以及各个文件,图片等和下载及修改管理服务。 源码下载:https://download.csdn.net/downloa…

零基础Linux_11(进程)进程程序替换+实现简单的shell

目录 1. 进程程序替换 1.1 程序替换原理 1.2 execl 接口 1.3 execv execlp execvp 1.4 exec 调各种程序 1.5 execle 接口 2. 实现简单的shell 2.1 打印提示和获取输入 2.2 拆开输入的命令和选项 2.3 创建进程和程序替换执行命令 2.4 内建命令实现路径切换 2.5 my…

win10 ip设置

百度安全验证

第82步 时间序列建模实战:LightGBM回归建模

基于WIN10的64位系统演示 一、写在前面 这一期,我们介绍LightGBM回归。 同样,这里使用这个数据: 《PLoS One》2015年一篇题目为《Comparison of Two Hybrid Models for Forecasting the Incidence of Hemorrhagic Fever with Renal Syndr…

2023年【熔化焊接与热切割】考试资料及熔化焊接与热切割复审模拟考试

题库来源:安全生产模拟考试一点通公众号小程序 2023年熔化焊接与热切割考试资料为正在备考熔化焊接与热切割操作证的学员准备的理论考试专题,每个月更新的熔化焊接与热切割复审模拟考试祝您顺利通过熔化焊接与热切割考试。 1、【单选题】 湿法水下焊接时…

力扣 -- 322. 零钱兑换(完全背包问题)

参考代码&#xff1a; 未优化代码&#xff1a; class Solution { public:int coinChange(vector<int>& coins, int amount) {int n coins.size();const int INF 0x3f3f3f3f;//多开一行&#xff0c;多开一列vector<vector<int>> dp(n 1, vector<i…

华硕X555YI, Win11下无法调节屏幕亮度

翻出一个旧电脑华硕X555YI&#xff0c;装Win11玩&#xff0c;已经估计到会有一些问题。 果然&#xff0c;装完之后&#xff0c;发现屏幕无法调节亮度。试了网上的一些方法&#xff0c;比如修改注册表等&#xff0c;无效。 估计是显卡比较老&#xff0c;哪里没兼容。然后用驱动…

vue3简易文字验证码

大神勿喷&#xff0c;简易版本&#xff0c;demo中可以用一下。 需要几个文字自己codelen 赋值 灵活点直接父组件传过去&#xff0c;可以自己改造 首先创建一个生成数字的js **mathcode.js**function MathCode(num){let str "寻寻觅觅冷冷清清凄凄惨惨戚戚乍暖还寒时候…

【C++进阶(七)】仿函数深度剖析模板进阶讲解

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:C从入门到精通⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习C   &#x1f51d;&#x1f51d; 模板进阶 1. 前言2. 仿函数的概念3. 仿函数的实…

kubectl命令举例

为了使读者能够快速掌握kubectl命令的使用方法&#xff0c;下面对常用的命令进行介绍。 1. kubectl create命令 此命令通过文件或者标准输入创建一个资源对象&#xff0c;支持YAML或者JSON格式的配置文件。例如&#xff0c;如果用户创建了一个Nginx的YAML配置文件&#xff0c…

22.app.js的全局数据共享

app.js中定义的全局变量适合 不修改且仅在js中使用的变量 目录 1 全局变量 2 修改全局变量 3 app.js中的变量不能直接在wxml中渲染 4 全局方法 1 全局变量 比如我现在想定义一个全局的变量something&#xff0c;直接在APP中写就行了 之后你可以在任何一个页面中&…

代码随想录算法训练营第五十六天 | 动态规划 part 14 | 1143.最长公共子序列、1035.不相交的线、53. 最大子序和(dp)

目录 1143.最长公共子序列思路代码 1035.不相交的线思路代码 53. 最大子序和&#xff08;dp&#xff09;思路代码 1143.最长公共子序列 Leetcode 思路 本题和718. 最长重复子数组 区别在于这里不要求是连续的了&#xff0c;但要有相对顺序&#xff0c;即&#xff1a;“ace” …

软件设计模式系列之二十四——模板方法模式

在软件设计领域&#xff0c;设计模式是一组被反复使用、多次实践验证的经典问题解决方案。其中&#xff0c;模板方法模式是一种行为型设计模式&#xff0c;用于定义一个算法的骨架&#xff0c;将算法中的某些步骤延迟到子类中实现&#xff0c;从而使子类可以重新定义算法的某些…

计算机网络(五):运输层

参考引用 计算机网络微课堂-湖科大教书匠计算机网络&#xff08;第7版&#xff09;-谢希仁 1. 运输层概述 之前所介绍的计算机网络体系结构中的物理层、数据链路层以及网络层它们共同解决了将主机通过异构网络互联起来所面临的问题&#xff0c;实现了主机到主机的通信&#xff…

AtCoder Beginner Contest 233 (A-Ex)

A.根据题意模拟即可 B.根据题意模拟即可 C.直接用map 进行dp即可 D.用前缀和进行模拟&#xff0c;用map统计前缀和&#xff0c;每次计算当前前缀和-k的个数就是以当前点为右端点答案。 E - Σ[k0..10^100]floor(X&#xff0f;10^k) (atcoder.jp) &#xff08;1&#xff09;…

Java 多输入框查询需求实现

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; 多输入框查询 需求分析 任意一个输入框&#xff0c;输入内容点击搜索都可以精准搜索到对应的内容 代码实现 Controller接口编写 PostMapping("merchant/manage&…

分享Arduino环境下加速下载 第三方库或芯片包

Content 问题描述问题解决 问题描述 众所周知&#xff0c;由于网络的问题&#xff0c;导致Arduino里面的包下载速度非常慢&#xff0c;甚至下了非常久&#xff0c;最后也还是出现下载失败的情况。 有的人打开了加速器&#xff0c;但是也依旧是速度非常慢&#xff0c;为什么呢…