您的位置 首页 版本发布

PyTorch 推出0.2版本:加入分布式机器学习功能

PyTorch入门实战教程

正值 ICML 2017 期间,我们发行了下一代主版本 PyTorch V0.2.0,现在你可从官网 http://pytorch.org 安装它,该版本的软件包文档可从 http://pytorch.org/docs/0.2.0/ 获取 。PyTorch V0.2 新增了期待已久的功能,比如广播、高级索引、高阶梯度以及最重要的分布式 PyTorch。

由于引入了广播功能,特定可广播情景的代码行为不同于 V0.1.12 中的行为。这可能导致你的现有代码中存在不易发现的误差。在「重要的破损量与工作区」这一章节中,我们将提供识别这些模糊代码的简单方法。

  • 张量广播(numpy 风格)
  • 张量和变量的高级索引
  • 高阶梯度(Higher-order gradients)
  • 分布式 PyTorch(多结点训练等)
  • 神经网络层与特征: SpatialTransformers、WeightNorm、EmbeddingBag 等
  • torch 和 autograd 中的新东西:矩阵乘法、矩阵的逆等
  • 更简单的调试,更好的错误信息
  • 漏洞修复
  • 重要的破损量和工作区

张量广播(numpy 风格)

简单来说,如果一个 PyTorch 操作支持广播,那么其张量参数可自动扩展为相同大小(无需拷贝数据)。 PyTorch 广播的语义严格遵守 numpy 风格的广播;如果你很熟悉 numpy 广播,事情就会按照你的预期发展。

通用语义

如果以下规则成立,则两个张量是「可广播的」:

  • 每个张量至少有一个维度。
  • 当开始迭代维度大小时,从后面的维度开始,维度大小必须相同,它们中的一个是 1,或者其中一个不存在

例如:

如果两个张量 x、y 是可广播的,得到的张量大小计算如下:

如果维度 x 和 y 的数量不相同,则把带有更少维度的张量的维度设为 1 以使它们的长度相等。

接着,对于每一个维度大小,所得到的维度大小是该维度上的 x 和 y 大小的最大值。

例如:

更多细节请参见 PyTorch 文档网站 http://pytorch.org/docs/0.2.0/notes/broadcasting.html。同样,每个 torch 函数列举了其在文档中的广播语义。

张量和变量的高级索引

PyTorch 现在支持 NumPy 风格的高级索引的一个子集,这允许用户使用相同的

[]风格的操作在张量的每一个维度上选择任意的索引,包括非相邻索引和重复索引;这也使得无需调用 PyTorch Index[Select, Add, …]函数即可获得一个更加灵活的索引策略。

让我们看一些实例:

纯整数组索引 – 在每个维度上指定任意的索引

同样支持广播、复制

允许任意的分度器(indexer)形状

可以使用冒号、椭圆

也可以使用张量来索引!

选择小于 n维,请注意逗号的使用。

高阶梯度

现在你可以评估 PyTorch 中的高阶微分。例如,你可以计算 Hessian-Vector积,以模型的梯度的范数为罚项,实现展开的 GAN 和提升的 WGAN 等。在 0.2 版本中,我们使得所有 torch.XXX

函数和最流行的 n 层具备了计算高阶梯度的能力。其余的将会在下一个版本中介绍。

下面是一个简短的实例,它以 Resnet-18 模型权重梯度的范数为罚项,因此权重数量的变化比较缓慢。

这里我们看两个新的概念:

  • torch.autograd.grad 是一个函数,它包含 [输出、输入列表(为了它你需要梯度)],并且返回梯度 wrt。这些输入作为元组,而不是将梯度累加到 .grad 属性中。如果你想要进一步在梯度上操作,这很有帮助。
  • 你可以在梯度上操作,并在其上调用 backward()。

支持高阶梯度的 n 层列表是:

  • AvgPool*d、 BatchNorm*d、 Conv*d、MaxPool1d,2d、Linear、 Bilinear
  • pad、ConstantPad2d、ZeroPad2d、LPPool2d、PixelShuffle
  • ReLU6、LeakyReLU、PReLU、Tanh、Tanhshrink、Threshold、Sigmoid、HardTanh、ELU、Softsign、SeLU
  • L1Loss、NLLLoss、 PoissonNLLLoss、LogSoftmax、Softmax2d

其余的将在下一个版本中启用。

为了高阶梯度,我们引入了一种编写 autograd.Function 的新风格。更多函数新风格的信息请参阅http://pytorch.org/docs/0.2.0/notes/extending.html。你们中的大多数并不亲自写

autograd.Functions ,因为它们是把新操作引入到 autograd 引擎的低级基元,其中你指定了前向与后向调用。

分布式PyTorch

我们还介绍了torch.distributed包,它允许我们在多机器间交换Tensors。使用该软件包,我们能够将神经网络放在多机器和使用较大批量进行训练。例如,给定一些基元,我们就能实现《精确的、大批量随机梯度下降:在1小时内训练ImageNet》。

该distributed包遵循MPI风格的程序设计模型。这意味着该软件包会提供像send、recv、all_reduce那样的函数以允许在结点(机器)之间交换Tensors。

对于每个机器最开始识别彼此并分配唯一的编码(排级),我们提供了简单的初始方法:

  • 共享文件系统(要求所有进程都能访问一个单一的文件系统)
  • IP 组播传输(要求所有的进程都在一个网络中)
  • 环境变量(要求我们手动配置等级,并且需要知道从所有进程中可获得结点的地址)

我们的软件包文档包含更多初始化和可用后端的信息,现在我们了解一下使用组播地址初始化的案例:

该代码片段将从第三个机器的process 2中打印“Hello”。

World size就是将参与整个工作的进程数量。每一个进程都将分配一个等级,即从0到world_size^( – 1)的数字,这些代号在整个工作中都是唯一的。它将成为处理识别符,并用于替代地址,例如指定哪一个进程将发送张量。

下面的代码片段展示了点到点的传输是如何简单地实现的:

异步p2p函数(isend、irecv)同样是可用的。

然而,一些通信模式经常出现,并且已经开发了更高效的集合调用。它们一般占用整个进程组,并且比使用send/recv的朴素算法要更快一些。例如all_reduce:

该分布式包是相当低级的,所以它允许实现更高级的算法和剪切代码以适应特定的任务目标,但是数据并行训练更为通用,所以我们为它构建了高级助手。因此,我们将介绍DistributedDataParallel,该函数基本上是nn.DataParallel的普适性替代。

下面的代码展示了添加它到已存代码的必要修改:

我们可以从以下查看全部的ImageNet训练案例:

https://github.com/pytorch/examples/tree/master/imagenet

新型神经网络层:SpatialTransformers、WeightNorm、EmbeddingBag等

新特征

  • 引入forward_pre_hook,以在前向函数(forward function)被调用之前执行用户指定的闭包。
  • 易于获得非叶梯度(non-leaf gradient):目前,我们必须使用hooks获取和检查中间值的梯度。这种做法对于简单的检查并不方便。因此,我们引入了retain_grad。以下示例可充分解释该方法:

  • DataParallel now supports dicts as inputs
  • 现在,DataParallel作为输入支持dicts。

新型层

使用 F.grid_sample和 F.affine_grid 的空间变换网络

论文《自正则化神经网络(Self-Normalizing Neural Networks)》提出 nn.SeLU和nn.AlphaDropout。 论文《从卷积序列到序列学习(Convolutional Sequence to Sequence Learning)》提出 nn.GLU(线性门控单元)。

通过 torch.utils.weight_norm 实现权值归一化(Weight Normalization)。

计算 cross_entropy_loss 和 nll_loss时,可以使用 ignore_index参数来

忽略特定的目标索引(target indice)。这是实现掩码的一种廉价、有用的方式,你可以获取计算损失时所忽略的mask指数。

F.normalize实现各维度的重归一化。

F.upsample和nn.Upsample将多个上采样层合并成一个函数。该函数可实现第二次和第三次双线性/三线性/最近上采样(bilinear/trilinear/nearest upsampling)。

nn.EmbeddingBag:在构建词袋模型(bag-of-words model)时,在Sum或Mean之后执行Embedding是一种常见做法。对于不同长度的序列,计算词袋嵌入涉及到掩码。我们提供一个单独的nn.EmbeddingBag,它能够更高效、快捷地计算词袋嵌入,尤其是不同长度的序列。

使用 bce_with_logits 的数值稳定的二元交叉熵参数(Binary Cross-Entropy loss)。

使用 PoissonNLLLoss 的带有目标泊松分布的负对数似然损失。

cosine_similarity

:沿维度计算并返回x1和x2之间的余弦相似度(cosine similarity)。

训练工具

学习率调度器: torch.optim.lr_scheduler 提供多个简单或聪明的方法来调整当前的学习率。在实验过程中,这些方法都很方便,i为用户可能想要做的事提供代理。

提供多种策略,可根据具体情况使用,详见

http://pytorch.org/docs/master/optim.html#how-to-adjust-learning-rate:

ReduceLROnPlateau, LambdaLR, StepLR, MultiStepLR, ExponentialLR

ConcatDataset是一种可以合并和连接两个单独的数据集的数据集元类。

torch 和 autograd 中的新特性

现在,所有reduce函数如sum和mean默认为挤压降维。例如,torch.sum(torch.randn(10, 20))返回一个1D 张量。x.shape,与numpy类似。一种等价于x.size()的便捷属性。torch.matmul,与np.matmul类似。bitwise and、or、xor、lshift、rshift 对 inverse、 gesv、 cumprod、 atan2的autograd支持通过关键字参数选项(keyword argument option)可获取无偏 var和 std。torch.scatter_add – torch.scatter,除了重复指数的情况,这些值都可以汇总。无给定参数时,torch.median与torch.sum类似,即它减少所有维度,并返回扁平张量(flattened Tensor)的单个median值。masked_copy_被重命名为masked_scatter_(因为对masked_copy_有反对声)。torch.manual_seed现在也对所有的CUDA设备播种。你现在可以通过关键字参数 torch.rand(1000, generator=gen)指定随机数生成器对象(random number generator object)。

修正和小提升

  • 现在,当变量转为布尔类型时,会出现一个错误,例如:

  • 修正了CUDA中qr分解的正确性问题。
  • 现已支持 IBM PowerPC64 平台。
  • 现在在运行时会检查 CuDNN 版本是否相同。
  • 改进了CUDA子进程中的错误消息。
  • 现在,Pytorch在CPU上可以更快地转置。
  • 改进了InstanceNorm上的错误信息。
  • 为各种例程添加了更多的参数检查,特别是BatchNorm和Convolution例程。
  • 在CPU后端时报告可以提出更好的错误信息。
  • 支持每台机器超过 8 块 GPU (仍有 CUDA p2p 限制)
  • 当访问不存在的属性时,错误消息获得了改进。
  • 变量的T()与Tensor一致。
  • 防止被零除时dropout p=1
  • 修复在非当前设备上共享CUDA张量的问题。
  • 当 BNε < 允许 CuDNN 值时,回退到THNN
  • 修正了当使用不同线程数量的MKL和OMP时线程破坏问题。
  • 在使用CuDNN RNN时提升了内存使用效率。
  • 使用负填充修正了ZeroPad2d后向的问题。
  • 加入了虚拟tensor.data属性,为用户提供可解释的错误消息。
  • 修正了Python3原位分配。
  • 在0-dim数组上调用from_numpy时生成错误。
  • 现在,空张量在多处理器共享时不会发生错误了。
  • 修复扩展张量的baddbmm
  • 现在,parallel_apply 可以接受任意输入了。
  • Tensor 和 Variable 中的关键字参数现在是一致的。
  • 修正了Magma不可用时的fix torch.inverse
  • 为ByteTensor添加逻辑非运算符。
  • 在分散/集中核心里加入设备声明。

重要问题和解决方法

值得注意的是,有两个重要的变化是无法向下兼容的:

  • Numpy风格的广播
  • 缩减函数,如 sum(1) 现在默认为 keepdim=False

我们提供不同级别的Python警告,它们可以在代码变更、使用错误操作时提醒用户。

例子

以下是一个代码片段,你可以将其添加到脚本的顶部。

添加此代码会高亮不合适的代码,并生成警告。

修复代码,警告就会消除。

在所有警告消失后,你就可以删除此代码片段了。

细节

下面将介绍三种不兼容的变化和例子。

使用(现已弃用)一维视图点态函数

早期版本的PyTorch允许点态函数在不同形状的张量上执行,只要每个张量中的元素数量和相等即可。旧的框架可以将每个张量视为一维来执行点操作。新版PyTorch支持广播。“一维”点操作被认为是不推荐的,并且在张量不可广播但具有相同数量元素的情况下会产生Python警告。

在实现没有出现过的代码中进行广播

在两个张量尺寸不同的情况下,广播的引入可能导致向后不兼容的变化,但是可以广播并具有相同数量的元素,例如:

可以预先生成一个特定尺寸的张量: torch.Size([4,1])

现在则生成张量尺寸: torch.Size([4,4])

为了帮助你识别代码中广播可能造成的后向不兼容情况,你可能需要将torch.utils.backcompat.broadcast_warning.enabled设置为 True ,这样就会在相应的问题发生时生成python警告。例如:

注意:此设置会触发广播有效性(包含库代码)的警告,所以你或许会希望在迁移代码后关闭这个警告。

减少函数:使用Keepdim=False

需在默认Keepdim参数时使用维度缩减获函数得警告,请将torch.utils.backcompat.keepdim_warning.enabled设置为 True 。例如:

可能会出现 torch.utils.backcompat.broadcast_warning.enabled,这一警告会被有效代码出发,所以你肯定希望在代码迁移后屏蔽它。

同时需注意:用 keepdim=False 可以使你的已有代码和广播“可以工作”。例如:

下载

本站微信群、QQ群(三群号 726282629):

PyTorch入门实战教程

发表回复

您的电子邮箱地址不会被公开。

返回顶部