首页 > 技术文章 > PyTorch API

megachen 2021-06-01 10:58 原文

PyTorch数据类型

  • Torch.FloatTensor
  • Torch.IntTensor
  • Torch.ByteTensor
  • Torch.CharTensor
  • Torch.LongTensor
  • tensor的维度为(1, ), 用来表示标量, 对应到tensor.size()/tensor.shape中是tensor.Size([num]), 如果tensor是这种情况, 可以直接使用tensor.item()返回对应的python内置的浮点数

   

PyTorch创建Tensor

  • Torch.tensor(data)
  • Torch.FloatTensor(dim1, dim2, …): 生成(dim1, dim2, …)维度的随机数tensor
  • Torch.Tensor(dim1, dim2, …): 和 torch.FloatTensor 一样, torch.set_default_tensor_type(torch.XxxTensor)指定默认类型
  • Torch.randn(dim1, dim2, …): 生成(dim1, dim2, …)维度的高斯随机数
  • Torch.normal(u, sigma): 高斯分布,但是指定了均值和方差, 具体使用需要看API文档
  • Torch.randint(low, high, size): 生成size维度的数据, 每个数据在[low, high)之间
  • Torch.rand(dim1, dim2, …): 生成(dim1, dim2, …)维度的均匀随机数
  • Torch.fill(size, value): 生成size维度的数据,每个元素的值为value
  • Torch.arange(start, end, step)
  • Torch.linspace(start, end, steps表示分成几份)
  • Torch.ones(dim1, dim2, …)
  • Torch.zeros(dim1, dim2, …)
  • Torch.eye(dim1, dim2, …)
  • Torch.randperm(len): [0, len) 之间的数随机排序
  • Torch.masked_select(mask): mask 中为1的选出来, 放在一维的tensor

   

维度

  • tensor.shape == tensor.size(), torch.shapetorch.size()返回的类型为torch.Size(), list(torch.Size类型的数据)可以转为python内置的list
    • tensor.shape[0] == tensor.size(0)
  • Tensor.dim() 返回 tensor.shape 的长度
  • Tensor.numel(): 返回tensor.shape中所有维度的乘积

   

维度变换

  • Torch.view() == torch.reshape(),常用于将Size([32, 512, 32, 32])变为Size([32, 512 * 32 * 32])等,工作原理是先将tensor展开成一维,再按照参数reshape,只修改表达方式,不修改内容
  • Sequeeze(dim): 如果没有参数,则将维度为1的都挤压掉,否则仅仅挤压dim(如果dim对应的size1);unsequeeze(dim)squeezeunsqueeze为维度元组增加1或者减少1的操作,比如将Size([512])扩展为Size(1, 512, 1, 1])
  • Expand()repeat(): 原理类似,但是接口不一样,目的都是对Size([1, 512, 1, 1])中为1的进行扩张, 比如要将Size([1, 512, 1, 1])变为Size([32, 512, 32, 32])就需要expand()或者repeat(), tensor.expand(32, 512, 32, 32)或者使用tensor.repeat(32, 1, 32, 32)
  • 广播:ab先维度按照右对齐,再expand/repeat,而在tfrepeat对应的是tf.tile
  • 转置:
    • Tensor.t(): 只针对2D
    • Transpose(): 一次只能交换两个维度
    • Permute(): 一次交换所有
  • 拼接
    • Tensor.cat([a, b], dim=0), 在0维度上将a与b进行拼接, 如果a为(32, 512, 64, 64)维度, b为(20, 512, 64, 64)维度, 则结果为(52, 512, 64, 64)维度, 注意a与b除了要拼接的维度可以不一样外, 其他都要一样
    • Tensor.stack([a, b], dim=0), 在0维度上将a与b进行拼接,但是与Tensor.cat不同的是, 并不会将a和b合并在一起, 而是添加一个新的维度将a和b拼接在一起, 以a为(32, 512, 64, 64)维度, b为(32, 512, 64, 64)维度为例子, 结果为(2, 32, 512, 64, 64)维度, 当取[0, …]时为a, 当取[1, …]为b, 注意这里a与b的维度要一样
  • 拆分
    • Tensor.split(a, size, dim=0): 如果size为标量,表示将adim维度每size个拆分出来; 如果size为列表,表示将adim维度上第一个拆出来的维度个数为size[0], 第二个是size[1], 总和要等与a在dim维度的数值; 比如a的维度是(32, 512, 64, 64), 则tensor.split(a, 16, dim=0) 得到的结果的维度为(16, 512, 64, 64)和(16, 512, 64 ,64); 如果tensor.split(a, [2, 30], dim=0), 得到的结果的维度为(2, 512, 64, 64)和(20, 512, 64, 64)
    • Tensor.chunk(a, num, dim=0): 将a在dim维度分成num份
  • tensor矩阵乘法
    • *表示element-wise乘法
    • @对应tensor.matmul, @适用于任意维度的矩阵乘法
      • 高维矩阵乘法的规则,只关注最后两个维度,如a的维度为(32, 512, 64, 32), b的维度为(32, 1, 32, 64), b先广播成(32, 512, 32, 64), 如果b的第二个维度不是1,则不能进行矩阵乘法,因为无法将b广播到与a在除了倒数两个维度上的维度数相同, 再进行矩阵乘法, 得到的结果维度是(32, 512, 64, 64)
    • Tensor.mm只适用于2-D矩阵的乘法
  • tensor的统计
    • Tensor.norm(num): 计算范数
    • Tensor.max, min, mean, argmin, argmax, topk, kthvalue, prod(累乘)
      • 对于argminargmax,如果传入dim,则计算时会将tensor进行flatten,返回flatten对应元素的下标
      • maxmin除了返回最大值和最小值,还会返回索引(也就是argmaxargmin的功能)
    • 默认的情况下tensor的统计函数会对计算的维度进行消除, 比如a的维度是(32, 512, 128, 64), a.norm(1, dim=2)之后, 结果的维度是(32, 512, 64), 有时候为了保留这个维度, 再调用统计函数时, 使用keepdim=True, 得到的结果便是(32, 512, 1, 64)
      • 不过topk是没有keepdim的,返回的结果原来上述为1的维度变成了k
  • 复杂操作
    • Torch.where(cond, x, y): condTrue,用x对应的值,否则用y对应的值
    • Torch.gather(input, dim, index): index根据input进行转换, 一般用于对index的进行映射
  • 梯度
    • Tensor.requires_grad_(): 将requires_grad设置为True
    • Torch.autograd.grad(outputs, inputs, grad_outputs=None, retain_graph=False): outputs 一般为标量, inputs 为输入的x
    • tensorScalar.backward()
  • 交叉熵
    • F.cross_entropy:将输入的pred进行softmax,再进行log,再进行nll_loss
    • F.softmax: 计算softmax
    • F.nll_loss: 计算softmaxlog之后,两个分布的交叉熵,输入为向量,target为标量,没有转为one-hot编码
  • 参数初始化
    • Torch.nn.init模块中
      • Kaiming_normal
  • 学习率策略
    • Torch.optim.lr_scheduler.ReduceLROnPlateau
    • StepLR
    • MultiStepLR
  • 划分数据
    • Train_data, test_data = Torch.utils.data.random_split(database, [train_num, test_num])
    • 交叉验证
  • 插值放大
    • `F.interpolate`: 可以选择linearnearest等插值算法
    • `F.upsample`: 注意和`F.interpolate`的区别,官网建议使用`F.interpolate`
  • 网络模块
    • BatchNorm2D: 输入的是channel
      • 参数affine表示是否学习alphagamma缩放参数,一般设置为affineTrue,如果affineFalse,则alpha1gamma0,并且不会学习
      • batchnorm的作用
        • 可以使用更大的学习率,对于,如果没有使用batchnorm,则x的值可能会比较大,那么得到的梯度就会很大,稍微增加一点学习率,可能就不会收敛到全局最优点
    • Dropout: 现在不能所以使用,因为版权
    • Net.train()Net.eval()只针对BatchNormDropOut
  • 保存和加载模型
    • Net.load_state_dict(torch.load('model.pkl'))
    • Torch.save(net.state_dict(), 'model.pkl')
  • 数据增强
    • T.RandomHorizontalFlip()
    • T.RandomVerticalFlip()
    • T.RandomRotation()
    • T.Resize()
    • T.RandomCrop()
    • T.CenterCrop()
    • 常见搭配
      • T.Resize先对图像进行尺度变换,一般先变换得大一些
      • 再使用T.RandomRotation对图像进行选择,这个时候会出现黑色边框,这不是我们需要得
      • 再使用T.CenterCrop去掉边框
  • 池化
    • Maxpool
    • Avgpool
    • Adaptive_avg_pool
  • 自定义数据集

   

  • RNN(NLP中,数据的维度为(seq_len, batch, feature_len),其中seq_len也是时间戳)容易导致梯度爆炸和梯度消失,RNN的梯度消失表示为RNN的记忆力短
    • nn.RNN(input_size, hidden_size, num_layers): input_size是一个单词词嵌入的维度,hidden_size也就是memory_size,表示的是一个词嵌入经过RNN输出的维度,num_layersRNNCell在空间上的个数
    • `rnn = nn.RNN(100, 20, 10)`的前向传播, `x = torch.randn(30, 10, 100)`表示有10个句子,每个句子单词有30个,每个单词是100维的词向量, `h0=torch.randn(10, 10, 20)`表示初始记忆,第一个10表示10个空间上的RNNCell,每个RNNCell都有一个向右传播的h(构成一个网格),第二个10表示batch,第三个20表示memory的维度, `out, h = rnn(x, h0)`其中out是所有htstackh是最后序列的输出, 维度为[10, 10, 20]
    • RNNCell不同,RNN输入的x就是[seq_len, batch, feature_len],但是在人工推导的时候,是一个[batch, feature_len]也就是时间戳设定的,为了和这个思路相一致,就诞生了RNNCell
  • RNNCell
    • RNNCell表示一个RNN块,输入的x[batch, feature_len], 需要人工保存中间的h,并输入到下一个RNNCell中,一般使用for训练迭代[seq_len, batch, feature_len],按照时间戳迭代,依次输入一个[batch, feature_len]
    • RNNCell(input_size, hidden_size),没有num_layers, 因此空间上RNNCell的展开也需要手动编写
  • LSTM(用于解决RNN的梯度消失现象)

    导致损失函数不再下降的原因

  1. 出现了梯度消失现象,与之对一个的是梯度爆炸,pytorch通过torch.nn.utils.clip_grad_norm(param, threshold)解决梯度爆炸问题,本质上是将grad方向不变,大小缩小,梯度爆炸的表现为loss现在比较小,突然loss变大
  2. 陷入了局部最优解
    1. 选择其他优化器,比如SGD或者Adam,通过动量冲出局部最优解,在pytorch中通过优化器构造器的mometum设置动量参数beta,不过有些优化器,比如Adam是没有monmetum参数的,Adam内部的实现机制就是构建在momentum之上的
    2. 选择不同的参数初始化,使得网络在另外一个起点进行学习

   

解决过拟合问题

  1. 提供更多的数据
  2. 采用正则化(PyTorch中通过优化器的weight_decay进行l2正则化,它的值为lamda超参数)
    1. L1-regularization:在原始的损失函数之上添加
    2. L2-regularization:在原始的损失函数之上添加
    3. 模型在最小化损失函数的时候,也就最小化模型的参数,这样做是为了降低模型的复杂度,假如一个问题,只需要使用2次模型即可,但是因为实现我们并不知道,而采用了5次模型来学习,,如果通过正则化将参数取小,最终d,e和f的值会趋向于0,这样预测函数就变成2次的了

   

激活函数以及其用途

  1. tanh:常用于RNN
  2. ReLU:常用于卷积
  3. LeakyReLU
  4. SELUReLU + LeakyReLU
  5. softplus:是ReLU函数在0点平滑函数
  6. Sigmoid

   

   

   

数据可视化

  1. Tensorboardx
  2. Visdom
    1. Visom.line绘制曲线
    2. Visom.image绘制图像

 

   

推荐阅读