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()是会报错的,可是在实际案例时,比如神经网络里,我看并没有对输入参数对这个属性进行设置,可是在迭代时,照样可以反向传播,这是为什么?