PyTorch


0 Pytorch教程

1 安装相关安装包

1.1 windows安装NVIDA

1.2 Linux安装Anaconda

1.3 在conda环境中安装Pytorch

从官网选择对应的版本,复制下载指令即可,官网链接:

https://pytorch.org/get-started/locally/

1.3.1 安装旧版本pytorch

1.4 conda导出/导入环境

  • 不同系统迁移
conda activate env_name  # 切换到新的环境
conda env export > env_name.yaml  # 导出环境
conda env create -f env_name.yaml  # 导入环境
  • 相同系统迁移
conda list --explicit > requirements.txt
conda create --name python-course --file requirements.txt

原文链接:Conda 环境迁移

1.4.1 打包虚拟环境与迁移

conda install -c conda-forge conda-pack
  • 打包虚拟环境
conda pack -n my_env_name -o out_name.tar.gz
  • 复制压缩文件到新的电脑环境
  1. 进到conda的安装目录:/anaconda(或者miniconda)/envs/
  2. 在该名目录下创建文件夹
  3. 解压conda环境:tar -zxvf output.tar.gz -C /anaconda(或者miniconda)/envs/创建的文件夹/
  4. 执行如下命令激活环境:source env_name/bin/activate
  5. 使用conda env list查看虚拟环境

1.5 下载特殊安装包

1.5.1 下载tensorboardx

conda install -c conda-forge tensorboardx

1.5.2 下载PyG

  • 注意pyg不支持PyTorch 1.12,注意下载旧版本的PyTorch
conda install pyg -c pyg

1.5.3 下载sklearn

conda install scikit-learn

1.5.4 下载opencc(简体繁体转换)

pip install opencc-python-reimplemented

1.6 window添加conda环境变量

  • 添加路径到系统变量Path下:
  1. C:\xxx\xxx\Anaconda3\Scripts
  2. C:\xxx\xxx\Anaconda3\Library\bin

1.7 安装pytorch版本过低,可能是指定cuda版本过高

在安装pytorch之前先查看本地cuda版本,Linux可以通过“nvidia-smi”查看,可得知支持的最高版本的cuda,不能高于这个版本,否则会自动安装低版本pytorch。

1.8 conda创建、删除、激活、推出环境

# 创建环境
conda create -n py36 python=3.6 

# 删除环境
conda create -n py39 python=3.6

# 激活环境
conda activate py36

# 退出环境
conda deactivate

1.9 conda环境迁移

1.10 下载不了目标依赖包,添加 -c anaconda

  • 如果下载显示下载完成,但是实际上没有下载对应包,需要添加channel
conda install -c anaconda scikit-learn

2 常用指令

2.1 一般指令

#矩阵乘法
torch.mm(input, mat2, *, out=None) → Tensor
#矩阵转置
torch.t(input) → Tensor
tensor.t()
#生成单元矩阵,对角线为1,其余为0的矩阵
torch.eye(n, m=None, *, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False) → Tensor
#设置和运行 CUDA 操作
torch.cuda()
tensor.cuda()
#把tensor_1扩展成size的形状
tensor_1.expand(size)
#把tensor_1扩展成和tensor_2一样的形状
tensor_1.expand_as(tensor_2)
#修改tensor形状
#view不会改变自身数据,返回的新的tensor与源tensor共享内存,即更改其中一个,另外一个也会跟着改变。
tensor.view(2,3)
tensor.view(-1)  # 把tensor变为一维结构

2.2 查看GPU信息

torch.cuda.is_available()
#cuda是否可用;

torch.cuda.device_count()
#返回gpu数量;

torch.cuda.get_device_name(0)
#返回gpu名字,设备索引默认从0开始;

torch.cuda.current_device()
#返回当前设备索引;

2.3 保存/加载模型,torch.save()/load()

将优化器参数、损失值等一同保存下来,然后在恢复模型的时候连同其它参数一起恢复,示例如下:

model_save_path = os.path.join(model_save_dir, 'model.pt')
torch.save({
            'epoch': epoch,
            'model_state_dict': model.state_dict(),
            'optimizer_state_dict': optimizer.state_dict(),
            'loss': loss,
    		'metrics': metrics
            ...
            }, model_save_path)
  • 加载模型
checkpoint = torch.load(model_save_path)
model.load_state_dict(checkpoint['model_state_dict'])
optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
epoch = checkpoint['epoch']
loss = checkpoint['loss']
metrics = checkpoint['metrics']

2.4 模型加速,torch.backends.cudnn.benchmark=True

让程序在开始时花费一点额外时间,为整个网络的每个卷积层搜索最适合它的卷积实现算法,进而实现网络的加速。适用场景是网络结构固定(不是动态变化的),网络的输入形状(包括 batch size,图片大小,输入的通道)是不变的,其实也就是一般情况下都比较适用。反之,如果卷积层的设置一直变化,将会导致程序不停地做优化,反而会耗费更多的时间。

2.5 查看torch版本

import torch
print(torch.__version__)

3 重要函数

3.1 torch.svd(A)

#计算矩阵或矩阵批次的奇异值分解input,奇异值按降序返回
torch.svd(input, some=True, out=None) -> (Tensor, Tensor, Tensor)
U,S,V=torch.svd(A)#返回对形如 n×m的实矩阵 A 进行奇异值分解的结果,使得 A=USV^T。 U 为左奇异向量,形状为 n×n,S 为特征值矩阵,形状为 n×m ,V 为右奇异向量,形状为 m×m
  • 基于SVD的主成分分析代码
### PCA降维 ###
def PCA_svd(X, k):
    # X为输入数,n*m n维m个样本
    # k为目标维度数
    X_mean = torch.mean(X.double(), 1,True)
    A = X - X_mean.expand_as(X)  # 对所有样本中心化
    print('A',A.size())
    U,S,V = torch.svd(torch.t(A))  # 对矩阵A做特征值分解(奇异值分解),注意此处的A进行的转置
    print('V',V.size())
    return torch.mm(V[:k],A)  # 返回新的特征矩阵

3.2 torch.mean(input, dim, keepdim=false)

  • 输入( Tensor ) – 输入张量。
  • dim ( int or tuple of python:ints ) – 要减少的一个或多个维度。
  • keepdim ( bool ) – 输出张量是否dim保留。
>>>torch.mean(a, 1, True)
tensor([[-0.0163],
        [-0.5085],
        [-0.4599],
        [ 0.1807]])

3.3 unsqueeze()与squeeze()

unsqueeze(): 在指定位置增加一个维度

# a的size为(2,3)
a.unsqueeze(1) # a.unsqueeze(-2)
# a的size为(2,1,3)

squeeze():在指定位置删除一个长度为1的维度,注意长度一定要为1

# a的size为(1, 2,3)
a.squeeze(0) # 下标从0开始
# a的size为(2,3)

3.4 arange(start, end, step)

>>> torch.arange(5)
tensor([ 0,  1,  2,  3,  4])
>>> torch.arange(1, 4)
tensor([ 1,  2,  3])
>>> torch.arange(1, 2.5, 0.5)
tensor([ 1.0000,  1.5000,  2.0000])

3.5 tensor乘法广播机制

广播的执行过程:

1.如果维度个数不同,则在维度较少的左边补1,使得维度的个数相同。

2.各维度的维度大小不同时,如果有维度为1的,直接将该维拉伸至维度相同。

3.6 enisum

image-20210626003645988

image-20210626003925510

3.7 randint(low=0,high, size)

>>> torch.randint(3, 10, (2, 2))
tensor([[4, 5],
        [6, 7]])

3.8 torch.backends.cudnn.deterministic

torch.backends.cudnn.deterministic是啥?顾名思义,将这个 flag 置为True的话,每次返回的卷积算法将是确定的,即默认算法。如果配合上设置 Torch 的随机种子为固定值的话,应该可以保证每次运行网络的时候相同输入的输出是固定的

# 设置随机数种子
np.random.seed(1)
torch.manual_seed(1)
torch.cuda.manual_seed_all(1)
torch.backends.cudnn.deterministic = True  # 保证每次结果一样

3.9 torch.optim

torch.optim是一个实现各种优化算法的包,实质就是更新权重。

  • 如何使用优化器

    要使用,torch.optim您必须构造一个优化器对象,该对象将保存当前状态并根据计算出的梯度更新参数。

  • 构建它
    要构造一个ptimizer你必须给它一个包含Variable要优化的参数(都应该是s)的迭代。然后,您可以指定特定于优化器的选项,例如学习率、权重衰减等。示

3.10 tensor拒绝计算整数的平均数,需要用到double()函数

torch.mean(X.double(), 0)

3.11 _可以作为占位符接收函数不需要的返回值

4 nn的相关使用

4.1 item()

# item()返回单个值(标量)
loss.item() # 获得loss的值

4.2 detach()

# detach()阻断反向传播,返回值仍为tensor
gpu_info.detach()  #返回tensor,仍在gpu上

4.3 cpu()

# cpu()将变量放在cpu上,仍为tensor
gpu_info.cpu()
gpu_info.cpu().detach()

4.4 设置提前停止

# 设置提前停止,patience代表忍受该指标多少个step不变化,当忍无可忍时,调整学习率。注,可以不是连续的n次。
early_stopping = EarlyStopping(patience=self.args.patience, verbose=True)

4.5 checkpoints

保存和加载用于推理或恢复训练的通用检查点模型有助于从上次停止的地方开始。保存一般检查点时,您必须保存的不仅仅是模型的 state_dict。

path = os.path.join(self.args.checkpoints, setting)

4.6 AMP

自动混合精度训练,预示着Tensor的dtype类型会自动变化,也就是框架按需自动调整tensor的dtype(其实不是完全自动,有些地方还是需要手工干预)

# 在训练最开始之前实例化一个GradScaler对象
scaler = GradScaler()

for epoch in epochs:
    for input, target in data:
        optimizer.zero_grad()

        # 前向过程(model + loss)开启 autocast
        with autocast():
            output = model(input)
            loss = loss_fn(output, target)

        # Scales loss. 为了梯度放大.
        scaler.scale(loss).backward()

        # scaler.step() 首先把梯度的值unscale回来.
        # 如果梯度的值不是 infs 或者 NaNs, 那么调用optimizer.step()来更新权重,
        # 否则,忽略step调用,从而保证权重不更新(不被破坏)
        scaler.step(optimizer)

        # 准备着,看是否要增大scaler
        scaler.update()

4.7 permute()

**Tensor.permute(a,b,c,d, …)**:permute函数可以对任意高维矩阵进行转置/换位,但没有 torch.permute() 这个调用方式, 只能 Tensor.permute():

>>> torch.randn(2,3,4,5).permute(3,2,0,1).shape
torch.Size([5, 4, 2, 3])

4.8 transpose(input, dim0, dim1)

转置/换位tensor中指定两个维度dim0 和 dim1的数据

4.9 nn.Conv1d()

卷积操作起源于图像处理,图像处理的数据一般为[batch_size, num_channel, height, width]

因此,通道数需要放在第二个维度。

# 定义
self.tokenConv = nn.Conv1d(in_channels=c_in, out_channels=d_model,kernel_size=3, padding=padding, padding_mode='circular')
# 使用
# x的结构为 [batch_size, len, dims]
# 此处需要获取feed_de的序列,因而先转置,以实现对len序列进行一维卷积,最后恢复原始数据维度排布
x = self.tokenConv(x.permute(0, 2, 1)).transpose(1, 2)

4.10 nn.init.kaiming_normal_

# 使用正态分布数据初始化模型参数
nn.init.kaiming_normal_(m.weight, mode='fan_in', nonlinearity='leaky_relu')

4.11 nn.embedding()

一个简单的查找表,用于存储固定字典和大小的embedding。

参数:

  • **num_ embedding ** ( int ) –embedding字典的大小
  • embedding _dim ( int ) – 每个embedding向量的大小
  • padding_idx ( int , optional ) – 如果指定,则条目padding_idx不影响梯度;因此,嵌入向量 atpadding_idx在训练期间不会更新,即它保持为固定的“垫”。对于新构造的Embedding,嵌入向量 atpadding_idx将默认为全零,但可以更新为另一个值以用作填充向量。
  • max_norm ( float , optional ) – 如果给定,则范数大于的每个嵌入向量都max_norm 被重新归一化为范数max_norm
  • norm_type ( float , optional ) – 要为max_norm选项计算的 p 范数的 p 。默认2.
  • scale_grad_by_freq ( boolean , optional ) – 如果给定,这将按小批量中单词频率的倒数来缩放梯度。默认False.
  • sparse ( bool , optional ) – 如果True,梯度 wrtweight矩阵将是一个稀疏张量。有关稀疏梯度的更多详细信息,请参阅注释。

变量:

~Embedding.weight (Tensor) – 可学习权重模块的大小

4.12 nn.parameter.Parameter()

一种被视为模块参数的tensor。

参数:

  • data( Tensor ) –参数张量。
  • requires_grad ( bool , optional ) – 如果参数需要梯度。

4.13 self.register_buffer(‘pe’, pe)

1.定义parameter和buffer都只需要传入Tensor即可。也不需要将其转成gpu,这是因为,当网络进行.cuda时候,会自动将里面的层的参数,buffer等转换成相应的GPU上。

2.self.register_buffer可以将tensor注册成buffer,在forward中使用self.mybuffer,而不是self.mybuffer_tmp

3.网络存储时也会将buffer存下,当网络load模型时,会将存储的模型的buffer也进行赋值。

4.buffer的更新在forward中,optim.step只能更新nn.parameter类型的参数。

4.14 dropout

在训练期间,使用来自伯努利分布的样本,在概率为p下随机将输入tensor的某些元素归零。每个通道将在每次前转呼叫中独立归零。

一种用于正则化和防止神经元协同适应的有效技术,用于抑制过拟合。

# 定义
self.dropout = nn.Dropout(p=dropout)
# 使用
self.dropout(x)

4.15 embedding()

词嵌入在 pytorch 中非常简单,只需要调用 torch.nn.Embedding(m, n) 就可以了,m 表示单词的总数目,n 表示词嵌入的维度,其实词嵌入就相当于是一个大矩阵,矩阵的每一行表示一个单词.

import torch
from torch import nn
from torch.autograd import Variable
# 定义词嵌入
embeds = nn.Embedding(2, 5) # 2 个单词,维度 5
# 得到词嵌入矩阵
embeds.weight
Parameter containing:
-1.3426  0.7316 -0.2437  0.4925 -0.0191
-0.8326  0.3367  0.2135  0.5059  0.8326
[torch.FloatTensor of size 2x5]

我们通过 weight 得到了整个词嵌入的矩阵,注意,

  1. 这个矩阵是一个可以改变的 parameter,在网络的训练中会不断更新;
  2. 同时词嵌入的数值可以直接进行修改,比如我们可以读入一个预训练好的词嵌入等等。

通俗来说,如果是不读入预训练的词嵌入,nn.embedding()相当于把one-hot编码转换为低维向量,这个向量就称为嵌入,就是仅仅根据one-hot编码信息得出,包含的是顺序/位置信息,以方便用表示每个元素(one-hot维度太大)。

4.16 自然指数为底的对数,.log()

4.17 获取Parameter的tesnor,Parameter.data

5 torch.tensor

5.1 转置 tensor.t()

5.2 基于列向量生成对角矩阵 torch.diag_embed(tensor)

5.3 求范数 torch.norm()

import torch
import torch.tensor as tensor
 
a = torch.ones((2,3))  #建立tensor
a2 = torch.norm(a)      #默认求2范数
a1 = torch.norm(a,p=1)  #指定求1范数

5.4 判断是否有nan,torch.isnan(tensor)

torch.isnan(tensor)  # 返回bool类型的tensor
torch.any(tensor)  # 判断tensor中是否有True值,返回True or False

5.5 有条件替换,torch.where(condition, x, y)

torch.where(condition, x, y)  # condition是BoolTensor,x是满足condition位置的值,y是不满足condition位置的值
  • 替换所有nan值
a = torch.Tensor([[1, 2, np.nan], [2, np.nan, 4], [3, 4, 5]])

a = torch.where(torch.isnan(a), torch.full_like(a, 0), a)

5.6 返回同一值的同size的tensor,torch.full_like(tensor)

torch.full_like(input, fill_value) → Tensor

5.7 比较tensor元素,torch.eq()相关

torch.eq()  #  逐元素比较,=
torch.equal()  # 整体比较,=
torch.ge()  # 逐元素比较,>=
torch.gt()  # 逐元素比较,>
torch.le()  # 逐元素比较,<=
torch.lt()  # 逐元素比较,<
torch.ne()  # 逐元素比较,!=

5.8 复制tensor,repeat(*size)

  • 注意不同于numpy.repeat(),numpy.repeat()逐元素复制,torch.repeat()可以赋值扩展到新的size
x = torch.tensor([1, 2, 3])
x.repeat(4, 2)
### 结果 ###
tensor([[ 1,  2,  3,  1,  2,  3],
        [ 1,  2,  3,  1,  2,  3],
        [ 1,  2,  3,  1,  2,  3],
        [ 1,  2,  3,  1,  2,  3]])

x.repeat(4, 2, 1).size()  # 1代表维持第3个维度的大小不变
# torch.Size([4, 2, 3])

numpy.array([1,2,3]).repeat(2)
# array([1, 1, 2, 2, 3, 3])

5.8 在已有维度上拼接tensor,torch.cat(inputs, dim)

  1. 对于需要拼接的tensor,维度数量必须相同,指定拼接的维度的尺寸可以不同,但是其它维度的尺寸必须相同。
  2. inputs可以是tensor也可以是list
x = tensor([[-0.1997, -0.6900,  0.7039],
        [ 0.0268, -1.0140, -2.9764]])
>>> torch.cat((x, x, x), 0)	# 在 0 维(纵向)进行拼接
tensor([[-0.1997, -0.6900,  0.7039],
        [ 0.0268, -1.0140, -2.9764],
        [-0.1997, -0.6900,  0.7039],
        [ 0.0268, -1.0140, -2.9764],
        [-0.1997, -0.6900,  0.7039],
        [ 0.0268, -1.0140, -2.9764]])

5.9 在新维度上拼接tensor,torch.stack(inputs, dim)

把相同形状的tensor合并,并根据提供的维度序列在相应位置插入新的维度,函数会根据位置来排列数据。

>>> x1 = torch.randn(2, 3)
>>> x2 = torch.randn(2, 3)
>>> torch.stack((x1, x2), 0).size()	# 在 0 维插入一个维度,进行区分拼接
torch.Size([2, 2, 3])
>>> torch.stack((x1, x2), 1).size()	# 在 1 维插入一个维度,进行组合拼接
torch.Size([2, 2, 3])

5.10 在指定位置插入维度1,torch.unsqueeze(input, dim)

x = torch.Tensor([1, 2, 3, 4])
torch.unsqueeze(x, 1)
# tensor([[1.],
#         [2.],
#         [3.],
#         [4.]])

5.11 去除大小为1的维度,torch.squeeze(input, dim)

m = torch.zeros(2, 1, 2, 1, 2)
print(m.size())  # torch.Size([2, 1, 2, 1, 2])
n = torch.squeeze(m)
print(n.size())  # torch.Size([2, 2, 2])

5.12 查找某个值,torch.nonzero(a==10).squeeze()

5.13 将tensor最高维度转换为list,其他维度维持tensor格式

list(Tensor)

5.14 重构tensor维度大小,view

torch.view(a, b)  # a * b
torch.view(a, -1)  # a * n/a, n代表元素个数
torch.view(-1)  # n, 一维结构

5.15 求均值,mean(input, dim, keepdim=False)

torch.mean(input, dim, keepdim=False, *, dtype=None, out=None)
  • keepdim (bool) – 是否保持原来的维度
>>> a = torch.randn(4, 4)
>>> a
tensor([[-0.3841,  0.6320,  0.4254, -0.7384],
        [-0.9644,  1.0131, -0.6549, -1.4279],
        [-0.2951, -1.3350, -0.7694,  0.5600],
        [ 1.0842, -0.9580,  0.3623,  0.2343]])
>>> torch.mean(a, 1)
tensor([-0.0163, -0.5085, -0.4599,  0.1807])
>>> torch.mean(a, 1, True)
tensor([[-0.0163],
        [-0.5085],
        [-0.4599],
        [ 0.1807]])

5.16 生成一个顺序tensor,torch.arange(n)

5.17 取对角元素,torch.diag()

  • torch.diag()可以取tensor对角元素

5.18 一维tensor转为对角矩阵,torch.diag_embed()

通过利用torch.diag()和torch.diag_embed()可以令对角元素为0

a = a - torch.diag_embed(a.diag())

5.19 mm和mul的区别,mm是矩阵乘法,mul是对位相乘(不求和)

5.20 张量点乘,tensordot

5.21 any

5.21.1 any(-1)

a
Out[25]: 
tensor([[[False,  True,  True, False],
         [False, False,  True,  True],
         [False,  True, False,  True]],
        [[False, False,  True, False],
         [False,  True, False, False],
         [False, False, False, False]]])
a.any(-1)
Out[26]: 
tensor([[ True,  True,  True],
        [ True,  True, False]])
b[a.any(-1)==0] =0
b
Out[28]: 
tensor([[[1., 1., 1., 1., 1.],
         [1., 1., 1., 1., 1.],
         [1., 1., 1., 1., 1.]],
        [[1., 1., 1., 1., 1.],
         [1., 1., 1., 1., 1.],
         [0., 0., 0., 0., 0.]]])

6 PyG-torch.geometirc

图神经网络神器

6.1 入门教程

6.2 构建输入

PyG的特征输入有三种格式:Dataset,List[Data],List[HeteroData]

6.2.1 构建Dataset

  • 创建一次读入内存的数据

构建torch_geometric.data.InMemoryDataset,需要重写(区分重载和重写)四个函数:
(1)torch_geometric.data.InMemoryDataset.raw_file_names()
存放raw_dir目录下所有数据文件名的字符串列表,用于下载时的检查过程(正如之前的文章提到的,数据集下载的时候会检测是否已经存在,避免重复下载,也就是如何避免自动下载的httperror的解决方案)。
(2)torch_geometric.data.InMemoryDataset.processed_file_names()
和(1)类似,存放processed_dir目录下的文件名的列表,用于检测是否已经存在(不会二次处理)。
(3)torch_geometric.data.InMemoryDataset.download()
下载数据到raw_dir目录下。
(4)torch_geometric.data.InMemoryDataset.process()
对raw_dir下的数据进行处理并存储到processed_dir目录下。
因此,可以发现关键在于第四个函数的实现,函数内首先需要读取原始数据并创建一个torch_geometric.data.Data对象的列表,并存储到processed_dir目录下面。直接存储和使用这个python-list时间代价很高,所以在存储之前调用torch_geometric.data.InMemoryDataset.collate()函数将列表转换为一个torch_geometric.data.Data对象。处理后的数据被整合到了一个数据对象中(作为返回值),同时返回一个slices字典来获取到这个数据对象中单个数据,所以总结下来process过程一共分四步:

  1. 加载数据创建列表
  2. 进行各种处理过程
  3. 调用collate()函数
  4. 存储本地

最后在数据类的构造函数中加载数据集并赋值给self.data和self.slices

import torch
from torch_geometric.data import InMemoryDataset

class MyDataset(InMemoryDataset):
    def __init__(self, root, transform=None, pre_transform=None):
        # 数据的下载和处理过程在父类中调用实现
        super(MyDataset, self).__init__(root, transform, pre_transform)
        # 加载数据
        self.data, self.slices = torch.load(self.processed_paths[0])

    # 将函数修饰为类属性
    @property
    def raw_file_names(self):
        return ['file_1', 'file_2']

    @property
    def processed_file_names(self):
        return ['data.pt']

    def download(self):
        # download to self.raw_dir
        pass

    def process(self):
        data_list = [...]

        if self.pre_filter is not None:
            data_list = [data for data in data_list if self.pre_filter(data)]

        if self.pre_filter is not None:
            data_list = [self.pre_transform(data) for data in data_list]

        data, slices = self.collate(data_list)
        # 这里的save方式以及路径需要对应构造函数中的load操作
        torch.save((data, slices), self.processed_paths[0])
  • 创建大规模数据

大数据集一般不会直接加载到内存中,这里构建数据集的时候需要继承父类torch_geometric.data.Dataset。在上面构建数据集时,重写了四个函数,此处还需要多实现两个函数:
(1)torch_geometric.data.Dataset.len()
返回数据集的文件个数。
(2)torch_geometric.data.Dataset.get()
实现对单个数据(图数据集的话一般是单个图)的加载逻辑。

import os.path as osp
import torch # 这里就不能用InMemoryDataset了
from torch_geometric.data import Dataset

class MyDataset(Dataset):
    # 默认预处理函数的参数都是None
    def __init__(self, root, transform=None, pre_transform=None):
        super(MyDataset, self).__init__(root, transform, pre_transform)
    @property
    def raw_file_names(self):
        return ['file_1', 'file_2']
    
    @property
    def processed_file_names(self):
        # 一次无法加载所有数据,所以对数据进行了分解
        return ['data1.pt', 'data2.pt', 'data3.pt']
    
    def download(self):
        # Download to raw_dir
        pass
    
	def process(self):
        i = 0
        # 遍历每一个文件路径
        for raw_path in self.raw_paths:
            data = Data(...)
            if self.pre_filter is not None and not self.pre_filter(data):
                continue
            if self.pre_transform is not None:
                data = self.pre_transform(data)
            torch.save(data, osp.join(self.processed_dir, 'data_{}.pt'.format(i)))
            i += 1
            
	def len(self):
        return len(self.processed_file_names)
    
	def get(self, idx):
        data = torch.load(osp.join(self.processed_dir, 'data{}.pt',format(idx)))
        return data

6.2.2 构建List[Data]

Data的结构由节点特征x与边索引edge_index构成,生成多个Data组成List即可,下面的示例包含使用batch处理。

graph_data_list = [Data(x=x, edge_index=self.graph_edge_index) for x in feature_input]
graph_dataloader = DataLoader(graph_data_list, batch_size=self.batch_size, shuffle=False) 
for graph_batch_data in graph_dataloader:
    GATConv(graph_batch_data.x, graph_batch_data.edge_index)

6.2.3 构建异质图List[HeteroData]

6.3 源码解释

6.3.1 GATConv源码解释

6.3.2 GCNConv源码解释

Debug

OSError: /lib64/libm.so.6: version `GLIBC_2.27‘ not found (required by /home/MSI/anaconda3/envs/py37

将torch-spline-conv卸载后问题就解决了pip uninstall torch-spline-conv


文章作者: fdChen
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 fdChen !
评论
  目录
加载中...