PyTorch 0.4.0 的发布给我们带来了很多新的特性。但与此同时,很多重要的语法和格式都发生了变化。这篇文章带给大家怎行从旧的代码迁移到 PyTorch 0.4.0 的一些具体方法。主要集中在以下方面:
Tensor
和Variable
合并了Tensor
支持 0 维度(即标量)- 放弃使用
volatile
dtypes
,devices
和 Numpy-style 的Tensor
初始函数- 设备无关性
- 一些新的参数命名规则
文章目录
Tensor
和 Variable
合并了
torch.Tensor
and torch.autograd.Variable
现在是一样的了。torch.Tensor
可以像以前的 Variable
一样记录历史数据,Variable
还可以继续使用,但会返回一个 torch.Tensor
类型的变量。这意味着,我们再也不需要在代码里到处用 Variable
了。
Tensor
类型查看
type()
方法不会再返回 Tensor 的数据类型了。需要使用 isinstance()
or x.type()
。
1 2 3 4 5 6 7 | >>> x = torch.DoubleTensor([1, 1, 1]) >>> print(type(x)) # was torch.DoubleTensor "<class 'torch.Tensor'>" >>> print(x.type()) # OK: 'torch.DoubleTensor' 'torch.DoubleTensor' >>> print(isinstance(x, torch.DoubleTensor)) # OK: True True |
关于 Tensor
的 requires_grad
requires_grad 现在是 Tensor 的一个属性,用法和以前 Variable 一样。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | >>> x = torch.ones(1) # create a tensor with requires_grad=False (default) >>> x.requires_grad False >>> y = torch.ones(1) # another tensor with requires_grad=False >>> z = x + y >>> # both inputs have requires_grad=False. so does the output >>> z.requires_grad False >>> # then autograd won't track this computation. let's verify! >>> z.backward() RuntimeError: element 0 of tensors does not require grad and does not have a grad_fn >>> >>> # now create a tensor with requires_grad=True >>> w = torch.ones(1, requires_grad=True) >>> w.requires_grad True >>> # add to the previous result that has require_grad=False >>> total = w + z >>> # the total sum now requires grad! >>> total.requires_grad True >>> # autograd can compute the gradients as well >>> total.backward() >>> w.grad tensor([ 1.]) >>> # and no computation is wasted to compute gradients for x, y and z, which don't require grad >>> z.grad == x.grad == y.grad == None True |
除了直接给定 requires_grad,还可以原地设定:my_tensor.requires_grad_()
。
1 2 3 4 5 6 | >>> existing_tensor.requires_grad_() >>> existing_tensor.requires_grad True >>> my_tensor = torch.zeros(3, 4, requires_grad=True) >>> my_tensor.requires_grad True |
关于 .data
.data
是从Variable
中获取Tensor
的主要方法。合并后,调用y = x.data
后 y
和 x
共享相同的数据,但 y
与 x
的计算历史无关,而且 requires_grad=False
。
但是,.data
在某些情况下不安全。x.data
上的任何变化都不被 autograd
跟踪,并且在向后传播的过程中 x
的值将出现差错。一种更安全的替代方法是使用 x.detach()
,它也返回一个 requires_grad=False
的 Tensor
,但它会有梯度变化的信息。
下面是一个.data
和x.detach()
(以及为什么我们建议使用 detach
)区别的例子。
如果你使用 Tensor.detach()
,梯度计算一定是正确的。
1 2 3 4 5 6 7 8 9 10 11 | >>> a = torch.tensor([1,2,3.], requires_grad = True) >>> out = a.sigmoid() >>> c = out.detach() >>> c.zero_() tensor([ 0., 0., 0.]) >>> out # modified by c.zero_() !! tensor([ 0., 0., 0.]) >>> out.sum().backward() # Requires the original value of out, but that was overwritten by c.zero_() RuntimeError: one of the variables needed for gradient computation has been modified by an |
但是用 Tensor.data
是不安全的,当引用的 tensor 被修改后,原 tensor 也会被修改,会导致梯度计算出错:
1 2 3 4 5 6 7 8 9 10 11 12 | >>> a = torch.tensor([1,2,3.], requires_grad = True) >>> out = a.sigmoid() >>> c = out.data >>> c.zero_() tensor([ 0., 0., 0.]) >>> out # out was modified by c.zero_() tensor([ 0., 0., 0.]) >>> out.sum().backward() >>> a.grad # The result is very, very wrong because `out` changed! tensor([ 0., 0., 0.]) |
Tensor
支持 0 维度(即 标量)
之前版本中,一维 Tensor
的索引会返回一个数值,一维 Variable
会返回(1,)
。类似的情况出现在求和函数中,例如 tensor.sum()
返回一个数值,然而 Variable.sum()
返回的是(1,)
。
此版本在PyTorch
中引入了适当的标量(0维张量)支持,不像原来单个数据还给搞出个一维数组!可以使用新torch.tensor
函数来创建标量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | >>> torch.tensor(3.1416) # create a scalar directly tensor(3.1416) >>> torch.tensor(3.1416).size() # scalar is 0-dimensional torch.Size([]) >>> torch.tensor([3]).size() # compare to a vector of size 1 torch.Size([1]) >>> >>> vector = torch.arange(2, 6) # this is a vector >>> vector tensor([ 2., 3., 4., 5.]) >>> vector.size() torch.Size([4]) >>> vector[3] # indexing into a vector gives a scalar tensor(5.) >>> vector[3].item() # .item() gives the value as a Python number 5.0 >>> mysum = torch.tensor([2, 3]).sum() >>> mysum tensor(5) >>> mysum.size() torch.Size([]) |
关于 Loss 的累加
之前都使用 total_loss += loss.data[0]
累积损失率。在0.4版本中有0维的标量,直接用loss.item()
得到其 loss 的数值就可以了。
请注意,如果您在累加损失时未将其转换为 Python 数字,则可能出现程序内存使用量增加的情况。这是因为上面表达式的右侧原本是一个Python浮点数,而它现在是一个零维张量。因此,总损失累加了张量和它们的梯度历史,这可能会使大的autograd 图保存比我们所需要长的时间。
放弃使用 volatile
volatile
标志现在已被弃用。之前,autograd 不会跟踪任何涉及 Variable(volatile=True)
的计算。它已经被换成了一套更加灵活的上下文管理器,如:torch.no_grad()
,torch.set_grad_enabled(grad_mode)
等等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | >>> x = torch.zeros(1, requires_grad=True) >>> with torch.no_grad(): ... y = x * 2 >>> y.requires_grad False >>> >>> is_train = False >>> with torch.set_grad_enabled(is_train): ... y = x * 2 >>> y.requires_grad False >>> torch.set_grad_enabled(True) # this can also be used as a function >>> y = x * 2 >>> y.requires_grad True >>> torch.set_grad_enabled(False) >>> y = x * 2 >>> y.requires_grad False |
dtypes
,devices
和 Numpy-style 的 Tensor
初始函数
在以前的 PyTorch 版本中,我们用数据类型(例如float vs double
),设备类型(cpu vs cuda
)和 layout(dense vs sparse
)共同作为”张量类型”。例如,torch.cuda.sparse.DoubleTensor
是Tensor
的double
数据类型,支持CUDA
,以及支持 COO Sparse Tensor Layout。
在此版本中,我们引入torch.dtype
,torch.device
以及torch.layout
类,通过NumPy
的风格更好的管理这些类型。
torch.dtype
下图是 dtype 的完整列表以及对应的 tensor 类型:
一个 tensor 的 dtype 可以同过 tensor.dtype
获得。
torch.device
torch.device
包含设备类型(cpu
或cuda
)和可选设备序号(id
)。它可以用torch.device('{device_type}')
或torch.device('{device_type}:{device_ordinal}')
初始化。
如果没有设备序号,则表示设备为当前设备; 例如,torch.device('cuda')
等同于torch.device('cuda:X')
,这里的X
是torch.cuda.current_device()
。
张量设备可以通过tensor.device
获得。
torch.layout
torch.layout 代表一个 Tensor 的数据布局。
张量的布局可以通过tensor.layout
获得。
创建Tensor
现在创建Tensor的方法还包括dtype,device,layout和requires_grad选项来指定返回Tensor所需的属性。例如:
1 2 3 4 5 6 7 8 9 10 | >>> device = torch.device("cuda:1") >>> x = torch.randn(3, 3, dtype=torch.float64, device=device) tensor([[-0.6344, 0.8562, -1.2758], [ 0.8414, 1.7962, 1.0589], [-0.1369, -1.0462, -0.4373]], dtype=torch.float64, device='cuda:1') >>> x.requires_grad # default is False False >>> x = torch.zeros(3, requires_grad=True) >>> x.requires_grad True |
torch.tensor 是新增张量创建方法之一。torch.tensor()
就像numpy.array()
构造器,可以将数组类数据直接转换为Tensor,本版本中这个函数也可以构造标量。如果初始化没有指定dtype
数据类型,PyTorch 将自动分配合适类型,我们极力推荐使用这种方法将已有的数据类(例如list)转化为Tensor。
1 2 3 4 5 6 7 8 9 10 11 | >>> cuda = torch.device("cuda") >>> torch.tensor([[1], [2], [3]], dtype=torch.half, device=cuda) tensor([[ 1], [ 2], [ 3]], device='cuda:0') >>> torch.tensor(1) # scalar tensor(1) >>> torch.tensor([1, 2.3]).dtype # type inferece torch.float32 >>> torch.tensor([1, 2]).dtype # type inferece torch.int64 |
下面介绍其他创建Tensor的方法:
torch.*_like
接受Tensor数据(注意不是数据的尺寸),如果不设置相关参数,它默认返回一个具有相同属性的Tensor
1 2 3 4 5 | >>> x = torch.randn(3, dtype=torch.float64) >>> torch.zeros_like(x) tensor([ 0., 0., 0.], dtype=torch.float64) >>> torch.zeros_like(x, dtype=torch.int) tensor([ 0, 0, 0], dtype=torch.int32) |
tensor.new_*
使用尺寸作为参数创建具有相同属性的Tensor
1 2 3 4 5 | >>> x = torch.randn(3, dtype=torch.float64) >>> x.new_ones(2) tensor([ 1., 1.], dtype=torch.float64) >>> x.new_ones(4, dtype=torch.int) tensor([ 1, 1, 1, 1], dtype=torch.int32) |
如果需要创建指定尺寸的Tensor,可以直接用元组指定尺寸作为参数,例如
torch.zeros((2,3))
或torch.zeros(2,3)
,这样就能创建尺寸为 2×3,元素为 0 的Tensor。设备无关性
在之前的版本中,当不确定计算设备 (CPU 或 GPU) 的情况时不太好写代码。 0.4版本做出了如下更新
- 使用
to
方法可以轻松转换训练的网络(module)和数据到不同计算设备运行 device
属性用来指定使用的计算设备,之前要用cpu()
,cuda()
转换模型或数据
1 2 3 4 5 6 7 8 9 | # at beginning of the script device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") ... # then whenever you get a new Tensor or Module # this won't copy if they are already on the desired device input = data.to(device) model = MyModule(...).to(device) |
一些新的参数命名规则
name
如果是一个空字符串或包含”.”,将不再可以作为 module.add_module(name, value)
,module.add_parameter(name, value)
或者module.add_buffer(name, value)
的参数。因为这样可能会在state_dict
中导致数据丢失。
PyTorch 0.3.1 和 0.4.0 代码对比实例
0.3.1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | model = MyRNN() if use_cuda: model = model.cuda() # train total_loss = 0 for input, target in train_loader: input, target = Variable(input), Variable(target) hidden = Variable(torch.zeros(*h_shape)) # init hidden if use_cuda: input, target, hidden = input.cuda(), target.cuda(), hidden.cuda() ... # get loss and optimize total_loss += loss.data[0] # evaluate for input, target in test_loader: input = Variable(input, volatile=True) if use_cuda: ... ... |
0.4.0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | # torch.device object used throughout this script device = torch.device("cuda" if use_cuda else "cpu") model = MyRNN().to(device) # train total_loss = 0 for input, target in train_loader: input, target = input.to(device), target.to(device) hidden = input.new_zeros(*h_shape) # has the same device & dtype as `input` ... # get loss and optimize total_loss += loss.item() # get Python number from 1-element Tensor # evaluate with torch.no_grad(): # operations inside don't track history for input, target in test_loader: ... |
文章来源:PyTorchTutorial.com PyTorch 中文网 翻译。
本站微信群、QQ群(三群号 726282629):
对于atuograd那里有一点疑问,如果想要计算梯度,需要把tensor的requires_grad设为True,否则,.backward()是会报错的,可是在实际案例时,比如神经网络里,我看并没有对输入参数对这个属性进行设置,可是在迭代时,照样可以反向传播,这是为什么?