2017-08-06Pytorch进入0.2版本,主要更新了高阶梯度,分布式PyTorch,广播,高级索引,新图层等;Pytorch在2017年5月3日发布了版v0.1.12,和以往更新版本的速度不一样,以前为每月更新;这次经过了3个月的时间,同时迎来了v0.2版本,我们来看一下这次主要更新内容吧?
本文转载自Pytorch中文网,更多内容可以访问Pytorch中文网
这里是PyTorch的下一个主要版本,恰恰适用于ICML。今天从我们的网站http://pytorch.org安装此版本的软件包文档,网址为http://pytorch.org/docs/0.2.0/
我们引入了期待已久的功能,如广播,高级索引,高阶渐变,最后:分布式PyTorch
。
由于引入了广播,某些可广播情况的代码行为与0.1.12中的行为不同。这可能会导致您现有代码中的无声错误。我们提供了在“ 重要破损和解决方法”部分中轻松识别此模糊代码的方法。
目录:
- Tensor广播(numpy风格)
- 传感器和变量的高级索引
- 高阶梯度
- 分布式PyTorch(多节点训练等)
- 神经网络层和特征:SpatialTransformers,WeightNorm,EmbeddingBag等
- 火炬和自动调整新功能:matmul,反转等
- 更容易调试,更好的错误信息
- Bug修复
- 重要的破损和解决方法
Tensor广播(numpy风格)
简而言之,如果PyTorch操作支持广播,则其Tensor参数可以自动扩展为相同大小(不复制数据)。
PyTorch广播语义密切跟随numpy式广播 ; 如果你熟悉数字广播,事情应该按预期工作。
一般语义学
如果以下规则成立,则两个张量是“可广播的”
- 每个张量具有至少一个维度。
- 当从尺寸尺寸开始迭代时,从尾部维度开始,尺寸大小必须相等,其中一个为1,或其中一个不存在。
例如:
>>> x=torch.FloatTensor(5,7,3)
>>> y=torch.FloatTensor(5,7,3)
# same shapes are always broadcastable (i.e. the above rules always hold)
# can line up trailing dimensions
>>> x=torch.FloatTensor(5,3,4,1)
>>> y=torch.FloatTensor( 3,1,1)
# x and y are broadcastable.
# 1st trailing dimension: both have size 1
# 2nd trailing dimension: y has size 1
# 3rd trailing dimension: x size == y size
# 4th trailing dimension: y dimension doesn't exist
# but:
>>> x=torch.FloatTensor(5,2,4,1)
>>> y=torch.FloatTensor( 3,1,1)
# x and y are not broadcastable, because in the 3rd trailing dimension 2 != 3
如果两个张量x,y是“可广播”,则所得到的张量大小计算如下:
- 如果x和y的维数不相等,则将尺寸缩小到尺寸较小的张量的前端,以使其长度相等。
- 然后,对于每个维度大小,生成的维度大小是沿该维度的x和y的大小的最大值。
例如:
# can line up trailing dimensions to make reading easier
>>> x=torch.FloatTensor(5,1,4,1)
>>> y=torch.FloatTensor( 3,1,1)
>>> (x+y).size()
torch.Size([5, 3, 4, 1])
# error case
>>> x=torch.FloatTensor(5,2,4,1)
>>> y=torch.FloatTensor( 3,1,1)
>>> (x+y).size()
RuntimeError: The size of tensor a (2) must match the size of tensor b (3) at non-singleton dimension 1
更多细节可以在PyTorch文档网站上找到。此外,每个手电筒功能列出了其文档中的广播语义。
传感器和变量的高级索引
PyTorch现在支持NumPy样式高级索引的子集。这允许用户使用相同的[]操作来选择Tensor的每个维度上的任意索引,包括不相邻的索引和重复的索引。这允许更灵活的索引策略,而不需要调用PyTorch的Index[Select, Add, ...] 功能。
我们来看一些例子:
x = torch.Tensor(5, 5, 5)
纯整数组索引 - 在每个维上指定任意索引
x[[1, 2], [3, 2], [1, 0]]
--> yields a 2-element Tensor (x[1][3][1], x[2][2][0])
也支持广播,重复
x[[2, 3, 2], [0], [1]]
--> yields a 3-element Tensor (x[2][0][1], x[3][0][1], x[2][0][1])
任意分度器形状允许
x[[[1, 0], [0, 1]], [0], [1]].shape
--> yields a 2x2 Tensor [[x[1][0][1], x[0][0][1]],
[x[0][0][1], x[1][0][1]]]
可以使用冒号,椭圆
x[[0, 3], :, :]
x[[0, 3], ...]
--> both yield a 2x5x5 Tensor [x[0], x[3]]
也可以使用Tensors来索引!
y = torch.LongTensor([0, 2, 4])
x[y, :, :]
--> yields a 3x5x5 Tensor [x[0], x[2], x[4]]
选择小于ndim,请注意使用逗号
x[[1, 3], ]
--> yields a 2x5x5 Tensor [x[1], x[3]]
高阶梯度
现在您可以评估PyTorch中的高阶差分。例如,您可以计算Hessian-Vector产品,惩罚您的模型的渐变的规范,实施展开的GAN和改进的WGAN等。
在0.2发行版中,我们已经启用了为所有torch.XXX功能和最受欢迎的nn图层计算更高阶梯度的功能。其余的将在下一个版本中介绍。
这是一个简短的例子,惩罚了Resnet-18模型的权重梯度的规范,使权重的数量变化缓慢。
import torch
from torchvision.models import resnet18
from torch.autograd import Variable
model = resnet18().cuda()
# dummy inputs for the example
input = Variable(torch.randn(2,3,224,224).cuda(), requires_grad=True)
target = Variable(torch.zeros(2).long().cuda())
# as usual
output = model(input)
loss = torch.nn.functional.nll_loss(output, target)
grad_params = torch.autograd.grad(loss, model.parameters(), create_graph=True)
# torch.autograd.grad does not accumuate the gradients into the .grad attributes
# It instead returns the gradients as Variable tuples.
# now compute the 2-norm of the grad_params
grad_norm = 0
for grad in grad_params:
grad_norm += grad.pow(2).sum()
grad_norm = grad_norm.sqrt()
# take the gradients wrt grad_norm. backward() will accumulate
# the gradients into the .grad attributes
grad_norm.backward()
# do an optimization step
optimizer.step()
我们在这里看到两个新概念:
- torch.autograd.grad是一个接收[输出,输入列表(您需要渐变)]的函数,并返回渐变wrt。这些输入作为元组,而不是将渐变累加到.grad属性中。如果您想进一步操作梯度,这很有用。
- 您可以在梯度上操作,并呼叫backward()它们。
nn支持更高阶梯度的层列表有:
- AvgPoold,BatchNormd,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(当前/旧式的写作功能完全向后兼容)。您可以在这里阅读更多关于新风格的功能。
大多数人不会写你自己autograd.Function的,他们是低级原语,为autograd引擎引入新的操作,你可以在其中指定向前和向后的呼叫。
分布式PyTorch
我们介绍torch.distributed包,允许您在多台机器之间交换Tensors。使用此软件包,您可以通过多台机器和更大的小批量扩展网络培训。例如,您将获得实现精准大型小型SGD:1小时培训ImageNet的原语。
该distributed软件包遵循MPI风格的编程模型。这意味着有提供给您的功能,例如send,recv,all_reduce将节点(机器)之间交换张量。
对于每个机器首先识别彼此并分配唯一的数字(等级),我们提供简单的初始化方法:
- 共享文件系统(要求所有进程可以访问单个文件系统)
- IP组播(要求所有进程都在同一个网络中)
- 环境变量(需要您手动分配等级并知道所有进程可访问节点的地址)
我们的包文档包含有关初始化和可用后端的更多详细信息,但以下是使用多播地址进行初始化的示例:
import torch.distributed as dist
dist.init_process_group(backend='tcp',
init_method='tcp://[ff15:1e18:5d4c:4cf0:d02d:b659:53ba:b0a7]:23456',
world_size=4)
print('Hello from process {} (out of {})!'.format(
dist.get_rank(), dist.get_world_size()))
这将打印Hello from process 2 (out of 4)在第3台机器上。
世界大小是参与工作的过程的数量。每个将被分配一个等级,它是0和world_size-1之间的数字,在此作业中是唯一的。它将用作进程标识符,并且将被使用而不是地址,例如,指定张量应被发送到哪个进程。
这是一个代码段,显示如何执行简单的点对点通信:
# All processes (receiving ones too!) need to have tensors of appropriate
# size preallocated.
x = torch.Tensor(10)
if dist.get_rank() == 0:
x.normal_()
# Send x to process with rank 1
dist.send(x, dst=1)
else: # rank == 1
# Receive data from process with rank 0 and save result in x
dist.recv(x, src=0)
异步p2p函数(isend,irecv)也可用。
然而,一些通信模式经常出现,已经开发出更有效的集体呼叫。他们通常参与整个过程组,并且比使用send/的天真算法要快得多recv。一个例子是all_reduce:
x = torch.Tensor([dist.get_rank()])
# Add tensors from all processes such that they all receive the result.
# x is an input and output to this operation.
dist.all_reduce(x)
分布式软件包是相当低级别的,因此它允许实现更先进的算法,并将代码定制到特定的目的,但数据并行培训是我们为此创建高级辅助工具的常见方法。
因此,我们介绍了DistributedDataParallel,这意味着几乎可以替代nn.DataParallel。以下是一个代码段,展示了将其添加到现有培训代码中所需的更改:
# Wrap model in DistributedDataParallel (CUDA only for the moment)
model = torch.nn.parallel.DistributedDataParallel(model.cuda())
# Use a DistributedSampler to restrict each process to a distinct subset
# of the dataset.
train_dataset = ...
train_sampler = torch.utils.data.distributed.DistributedSampler(train_dataset)
train_loader = torch.utils.data.DataLoader(
train_dataset, batch_size=args.batch_size, num_workers=args.workers,
pin_memory=True, sampler=train_sampler)
for epoch in range(args.num_epochs):
# Use .set_epoch() method to reshuffle the dataset partition at every iteration
train_sampler.set_epoch(epoch)
# training loop
...
您可以在这里看到更完整的Imagenet培训示例
新的nn层:SpatialTransformers,WeightNorm,EmbeddingBag等
新功能
- 引入forward_pre_hook来在调用forward函数之前执行用户指定的闭包。
- 方便访问非叶梯度:目前,要访问和检查中间值的梯度,我们必须使用hooks。这不方便进行简单的检查。因此,我们介绍retain_grad。最好通过一个例子来解释:
input = Variable(torch.rand(1, 3), requires_grad=True)
h1 = input * 3
out = (h1 * h1).sum()
h1.retain_grad()
out.backward()
print(h1.grad)
# without calling retain_grad(), h1.grad is None
- DataParallel现在支持dicts作为输入
新图层
- 空间变压器网络通过F.grid_sample和F.affine_grid
- nn.SeLU并nn.AlphaDropout从论文中引入:自我规范化神经网络
- nn.GLU(门控线性单元)从卷积序列到序列学习引入
- 体重归一化现在通过torch.utils.weight_norm来实现。
- 现在,您可以在计算cross_entropy_loss和nll_loss使用ignore_index参数时忽略特定的目标索引。这是一种实现掩蔽的便宜实用方法,您可以mask在其中计算丢失的索引。
- F.normalize 实现维度重整化
- F.upsample并将nn.Upsample多个上采样层合并成一个功能。它实现了2d和3d双线性/三线性/最近的上采样。
- nn.EmbeddingBag:当构建袋子模型时,做一个Embedding跟随Sum或是Mean常见的。对于可变长度序列,计算嵌入包涉及掩蔽。我们提供了一种nn.EmbeddingBag更加高效和更快速的嵌入嵌入方式,特别是对于可变长度序列。
- 数值稳定的二进制交叉熵损失通过 bce_with_logits
- 目标通过泊松分布的负对数似然损失 PoissonNLLLoss
- cosine_similarity:返回x1和x2之间的余弦相似度,沿着dim计算。
新的火炬和自动化
- 所有减少功能,比如sum和mean现在默认为挤压缩小的尺寸。例如torch.sum(torch.randn(10, 20))返回1D Tensor。
- x.shape,类似于numpy。方便property等同于x.size()
- torch.matmul,类似于np.matmul
- 按位和,或,xor,lshift,rshift
- autograd支持inverse,gesv,cumprod,atan2
- 无偏见var,std现在可以通过关键字参数选项
- torch.scatter_add - torch.scatter,除了遇到重复索引时,这些值被求和。
- 在没有给出参数的情况下,torch.median的行为类似于torch.sum,即它减少了所有维度,并返回了平坦Tensor的单个中值。
- masked_copy_已重命名为maskedscatter(在masked_copy_上已弃用)
- torch.manual_seed现在也种下所有的CUDA设备
- 您现在可以通过关键字参数指定随机数生成器对象 torch.rand(1000, generator=gen)
错误修复和小改进
- 现在,当将变量转换为bool时,我们会发出错误。例如:
b = Variable(torch.zeros(1))
if b[0]: # errors now
重要的破损和解决方法
如您所见,我们引入了两个不能向后兼容的重要更改:
- 颠覆式广播
- 缩减功能sum(1)现在默认为keepdim=False
我们提供不同级别的Python警告,您可以启用以警告您,如果您使用不赞成的行为,或者您的代码的行为已更改。
TL;博士
这是一个代码片段,您可以添加到脚本的顶部。添加此代码将生成突出显示不兼容代码的警告。
修复代码不再生成警告。
# insert this to the top of your scripts (usually main.py)
import sys, warnings, traceback, torch
def warn_with_traceback(message, category, filename, lineno, file=None, line=None):
sys.stderr.write(warnings.formatwarning(message, category, filename, lineno, line))
traceback.print_stack(sys._getframe(2))
warnings.showwarning = warn_with_traceback; warnings.simplefilter('always', UserWarning);
torch.utils.backcompat.broadcast_warning.enabled = True
torch.utils.backcompat.keepdim_warning.enabled = True
一旦所有警告消失,您可以删除代码段。
更精细地
现在,让我们看看这三个不相容的变化与例子。
使用(现已弃用)1维视图点分函数
PyTorch的先前版本允许某些点函数在不同形状的张量上执行,只要每个张量中的元素数量相等即可。然后通过将每个张量视为一维来执行点操作。PyTorch现在支持广播。“一维”点行为被认为是不推荐的,并且在张量不可广播但具有相同数量的元素的情况下会产生Python警告。
例如:
>>> torch.add(torch.ones(4), torch.ones(2,2))
__main__:1: UserWarning: self and other not broadcastable, but have the same
number of elements. Falling back to deprecated pointwise behavior.
2
2
2
2
[torch.FloatTensor of size 4]
在以前没有发生过的代码中进行广播
广播的引入可能导致向后不兼容的变化,其中两张张量的形状不同,但可广播并具有相同数量的元素。
例如:
>>> torch.add(torch.ones(4,1), torch.randn(4))
以前将生产一个尺寸为:的Tensor torch.Size([4,1]),但现在生产的尺寸为:torch.Size([4,4])。
为了帮助识别你的代码中通过广播介绍向后不兼容性可能存在的情况下,可以设定torch.utils.backcompat.broadcast_warning.enabled到True,这将产生在这种情况下,一个python警告。
例如:
>>> torch.utils.backcompat.broadcast_warning.enabled=True
>>> torch.add(torch.ones(4,1), torch.ones(4))
__main__:1: UserWarning: self and other do not have the same shape, but are broadcastable, and have the same number of elements.
请注意,此设置可以触发广播有效使用的警告(包括库代码),因此您可能希望在迁移代码后关闭此警告。
减少函数的KeepDim = False
要在使用默认的keepdim参数使用维度缩小功能时发出警告,请设置torch.utils.backcompat.keepdim_warning.enabled为True。例如:
>>> torch.sum(torch.ones(2,3), 1)
__main__:1: UserWarning: backwards compatibility: call to "sum" uses default value for keepdim which has changed default to False. Consider passing as kwarg.
3
3
[torch.FloatTensor of size 2]
与torch.utils.backcompat.broadcast_warning.enabled此相反,此警告可以从有效的代码触发,因此您很可能希望在迁移代码后禁用此警告。
还要注意,使用keepdim=False可以使您现有的代码与广播“正常工作”。例如:
# behavior with (old) keepdim=True, causes accidental broadcast
>>> torch.add(torch.ones(4), torch.ones(4,4).sum(dim=1, keepdim=True))
5 5 5 5
5 5 5 5
5 5 5 5
5 5 5 5
[torch.FloatTensor of size 4x4]
# new behavior with keepdim=False is equivalent to non-broadcasted result
>>> torch.add(torch.ones(4), torch.ones(4,4).sum(dim=1, keepdim=False))
5
5
5
5
[torch.FloatTensor of size 4]
下载
- 源代码(zip)
- 源代码(tar.gz)
原创文章,转载请注明 : Pytorch v0.2版本发布--高阶梯度,分布式PyTorch,广播,高级索引,新图层等 - pytorch中文网
原文出处: https://ptorch.com/news/34.html
问题交流群 :168117787