PyTorch 4.0版本迁移指南

Posted ATYUN订阅号

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PyTorch 4.0版本迁移指南相关的知识,希望对你有一定的参考价值。

AiTechYun

编辑:yuxiangyu


PyTorch昨天发布了PyTorch 0.4.0版本。这个版本伴随着很多重大的更新,包括正式开始支持windows。以下为PyTorch官方为让大家使用新版PyTorch而发布的代码迁移指南。


欢迎阅读PyTorch 0.4.0的迁移指南。在此版本中,我们引入了许多振奋人心的新功能和重要的bug修复,旨在为用户提供更好,更清晰的接口。在这个指南中,我们将介绍从以前版本迁移现有代码时最重要的变化:

  • Tensor与Variable合并

  • 支持0维(标量)Tensor

  • 弃用volatile标记

  • dtypes,devices和Numpy风格的Tensor创建函数

  • 编写不限制设备的代码

合并Tensor和Variable类

torch.Tensor和torch.autograd.Variable现在是相同的类。更确切地说,torch.Tensor能够像旧版Variable一样追踪历史; Variable封装还像过去那样工作,但返回一个torch.Tensor类型的对象。这意味着你不再需要在代码中到处使用Variable封装器。


在type()的Tensor更改

还要注意,张量的type()不再反映数据类型。而是使用isinstance()或x.type()替代:

1 >>> x= torch.DoubleTensor([1,1,1])
2 >>>print(type(x)) # was torch.DoubleTensor
3 "<class 'torch.Tensor'>"
4 >>>print(x.type()) # OK: 'torch.DoubleTensor'
5 'torch.DoubleTensor'
6 >>>print(isinstance(x, torch.DoubleTensor)) # OK: True
7 True


autograd什么时候开始追踪历史记录?

requires_grad,autograd的主要标记,现在归属于Tensor。过去适用于Variables的规则同样适用于Tensor; 当一个操作的任何输入Tensor有requires_grad=True时,autograd开始跟踪历史记录。例如:

01 >>> x= torch.ones(1# create a tensor with requires_grad=False (default)
02 >>> x.requires_grad
03 False
04 >>> y= torch.ones(1# another tensor with requires_grad=False
05 >>> z= x+ y
06 >>># both inputs have requires_grad=False. so does the output
07 >>> z.requires_grad
08 False
09 >>># then autograd won't track this computation. let's verify!
10 >>> z.backward()
11 RuntimeError: element0 of tensors doesnot require gradand doesnot have a grad_fn
12 >>>
13 >>># now create a tensor with requires_grad=True
14 >>> w= torch.ones(1, requires_grad=True)
15 >>> w.requires_grad
16 True
17 >>># add to the previous result that has require_grad=False
18 >>> total= w+ z
19 >>># the total sum now requires grad!
20 >>> total.requires_grad
21 True
22 >>># autograd can compute the gradients as well
23 >>> total.backward()
24 >>> w.grad
25 tensor([1.])
26 >>># and no computation is wasted to compute gradients for x, y and z, which don't require grad
27 >>> z.grad== x.grad== y.grad== None
28 True


REQUIRES_GRAD标记

除了直接设置属性之外,你还可以使用my_tensor.requires_grad_()就地更改此标记,或者如上例所示,在创建时将其作为参数传递(默认为False),例如:

1 >>> existing_tensor.requires_grad_()
2 >>> existing_tensor.requires_grad
3 True
4 >>> my_tensor= torch.zeros(3,4, requires_grad=True)
5 >>> my_tensor.requires_grad
6 True


那么.DATA呢?

.data是从Variable获取底层Tensor的主要方式。合并后,调用y = x.data仍然具有类似的语义。因此y是一个与x共享相同数据的Tensor ,它与x的计算历史无关,并且requires_grad=False。


但是,.data在某些情况下可能不太稳定。x.data的任何变化都不会被autograd跟踪,并且如果在反向传递中需要x,计算出的梯度会出错。一种更安全的替代方法是使用x.detach(),它也返回一个与requires_grad=False共享数据的Tensor,但是如果x需要反向传递,则它将使用autograd就地更改记录。


支持0维(标量)张量

在过去,索引到一个Tensor向量(1维张量)会得到一个Python数字,而索引到一个Variable向量中会得到一个尺寸为(1,)的向量!类似的反应还存在与降阶函数中,例如tensor.sum()返回一个Python数字,但variable.sum()会重新调用一个尺寸为(1,)的向量。


幸运的是,此版本在PyTorch中引入了标量(0维张量)的支持!可以使用新torch.tensor函数来创建标量(后面会对其进行更详细的解释,现在你只需将它看作PyTorch中的numpy.array)。现在你可以这样做:

01 >>> torch.tensor(3.1416)        # create a scalar directly
02 tensor(3.1416)
03 >>> torch.tensor(3.1416).size() # scalar is 0-dimensional
04 torch.Size([])
05 >>> torch.tensor([3]).size()    # compare to a vector of size 1
06 torch.Size([1])
07 >>>
08 >>> vector= torch.arange(2,6# this is a vector
09 >>> vector
10 tensor([2.3.4.5.])
11 >>> vector.size()
12 torch.Size([4])
13 >>> vector[3]                   # indexing into a vector gives a scalar
14 tensor(5.)
15 >>> vector[3].item()            # .item() gives the value as a Python number
16 5.0
17 >>> mysum= torch.tensor([2,3]).sum()
18 >>> mysum
19 tensor(5)
20 >>> mysum.size()
21 torch.Size([])


累加损失

以广泛使用的模式total_loss += loss.data[0]为例。0.4.0之前,loss是一个封装了(1,)张量的Variable,但0.4.0的loss现在是一个零维的标量。索引到标量是没有意义的(现在它会给出一个警告,但在0.5.0中将是一个系统错误)。使用loss.item()可以从标量中获取Python数字。


请注意,如果您在累加损失时未将其转换为Python数字,则可能出现程序内存使用量增加的情况。这是因为上面表达式的右侧原本是一个Python浮点数,而它现在是一个零维张量。因此,总损失累加了张量和它们的梯度历史,这可能会使大的autograd 图保存比我们所需要长的时间。


弃用volatile标记

volatile标志现在已被弃用。之前,autograd不会跟踪任何涉及Variable(volatile=True)的计算。它已经被换成了一套更加灵活的上下文管理器,如:torch.no_grad(),torch.set_grad_enabled(grad_mode)等等。

01 >>> x= torch.zeros(1, requires_grad=True)
02 >>> with torch.no_grad():
03 ...     y= x* 2
04 >>> y.requires_grad
05 False
06 >>>
07 >>> is_train= False
08 >>> with torch.set_grad_enabled(is_train):
09 ...     y= x* 2
10 >>> y.requires_grad
11 False
12 >>> torch.set_grad_enabled(True# this can also be used as a function
13 >>> y= x* 2
14 >>> y.requires_grad
15 True
16 >>> torch.set_grad_enabled(False)
17 >>> y= x* 2
18 >>> y.requires_grad
19 False


dtypes,devices和NumPy风格的创建函数

在以前版本的PyTorch中,我们用来指定数据类型(例如float或 double),设备类型(cpu或cuda)和布局(dense或sparse)作为“tensor type”。例如,torch.cuda.sparse.DoubleTensor是Tensor类型代表了:double数据类型,使用CUDA设备,COO稀疏张量布局。


在此版本中,我们引入torch.dtype,torch.device以及torch.layout类,允许通过NumPy的样式创建函数,以便对他们的属性进行更好的管理。


TORCH.DTYPE

以下是可用torch.dtypes(数据类型)和其相应张量类型的完整列表。

张量的dtype可以通过其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()的结果。


张量设备可以通过访问device属性了解。


TORCH.LAYOUT

torch.layout代表一个Tensor的数据布局。当前torch.strided(默认为稠密张量)和torch.sparse_coo(COO格式的稀疏张量)均受支持。


张量的布局可以通过layout属性进行访问。


创建Tensor

现在创建Tensor的方法还包括dtype,device,layout和requires_grad选项来指定返回Tensor所需的属性。例如:

01 >>> device= torch.device("cuda:1")
02 >>> x= torch.randn(3,3, dtype=torch.float64, device=device)
03 tensor([[-0.63440.8562,-1.2758],
04         [0.84141.79621.0589],
05         [-0.1369,-1.0462,-0.4373]], dtype=torch.float64, device='cuda:1')
06 >>> x.requires_grad # default is False
07 False
08 >>> x= torch.zeros(3, requires_grad=True)
09 >>> x.requires_grad
10 True

torch.tensor(data, …)


torch.tensor是新增张量创建方法之一。它吸收所有类型array-like数据并将其包含的值复制到新的Tensor中。如前所述,PyTorch中的torch.tensor等同于NumPy的numpy.array构造函数。与torch.*Tensor方法不同,你也可以通过这种方式创建零维(即标量)Tensor(单个python数字在torch.*Tensor 方法中被视为Size)。而且,如果dtype没有给定,它会根据数据推理出合适的dtype。这是从现有数据(比如,Python列表)创建张量的推荐方法。如下:

01 >>> cuda= torch.device("cuda")
02 >>> torch.tensor([[1], [2], [3]], dtype=torch.half, device=cuda)
03 tensor([[1],
04         [2],
05         [3]], device='cuda:0')
06 >>> torch.tensor(1)              # scalar
07 tensor(1)
08 >>> torch.tensor([1,2.3]).dtype # type inferece
09 torch.float32
10 >>> torch.tensor([1,2]).dtype   # type inferece
11 torch.int64

我们还添加了更多的张量创建方法。其中一些为torch.*_like或tensor.new_*的变体。

  • torch.*_like接受输入Tensor,而不是形状(shape)。除非另有说明,它默认返回一个与输入Tensor属性相同的Tensor:

1 >>> x= torch.randn(3, dtype=torch.float64)
2  >>> torch.zeros_like(x)
3  tensor([0.0.0.], dtype=torch.float64)
4  >>> torch.zeros_like(x, dtype=torch.int)
5  tensor([000], dtype=torch.int32)
  • tensor.new_*也可以创建与tensor具有相同属性的Tensor,但它需要一个形状参数:

1 >>> x= torch.randn(3, dtype=torch.float64)
2 >>> x.new_ones(2)
3 tensor([1.1.], dtype=torch.float64)
4 >>> x.new_ones(4, dtype=torch.int)
5 tensor([1111], dtype=torch.int32)

要指定所需的形状,可以在大多数情况下使用元组(例如torch.zeros((2, 3)))或可变参数(例如,torch.zeros(2, 3))。

*:torch.from_numpy仅以NumPy ndarray作为输入参数。


编写不限制设备的代码

过去的PyTorch编写不限制设备的代码非常困难(即,可以在没有修改的情况下在启动CUDA和仅使用CPU的计算机上运行)。


PyTorch 0.4.0通过一下两种方式实现:

  • 通过device 属性获取所有 Tensor的torch.device(get_device仅适用于CUDA张量)

  • Tensor和Modules的to方法可以方便的将对象移动到不同的设备(不必在基于环境调用cpu()或cuda())


我们推荐以下模式:

1 # at beginning of the script
2 device= torch.device("cuda:0" if torch.cuda.is_available()else "cpu")
3
4 ...
5
6 # then whenever you get a new Tensor or Module
7 # this won't copy if they are already on the desired device
8 input = data.to(device)
9 model= MyModule(...).to(device)

迁移代码实例

为了了解0.4.0中整体变化的况,我们来看一个0.3.1和0.4.0中常见代码模式的简单例子:


0.3.1(旧):

01 model= MyRNN()
02   if use_cuda:
03       model= model.cuda()
04
05   # train
06   total_loss= 0
07   for input, targetin train_loader:
08       input, target= Variable(input), Variable(target)
09       hidden= Variable(torch.zeros(*h_shape)) # init hidden
10       if use_cuda:
11           input, target, hidden= input.cuda(), target.cuda(), hidden.cuda()
12       ... # get loss and optimize
13       total_loss+= loss.data[0]
14
15   # evaluate
16   for input, targetin test_loader:
17       input = Variable(input, volatile=True)
18       if use_cuda:
19           ...
20       ...

0.4.0(新):

01 # torch.device object used throughout this script
02   device= torch.device("cuda" if use_cudaelse "cpu")
03
04   model= MyRNN().to(device)
05
06   # train
07   total_loss= 0
08   for input, targetin train_loader:
09       input, target= input.to(device), target.to(device)
10       hidden= input.new_zeros(*h_shape) # has the same device & dtype as `input`
11       ... # get loss and optimize
12       total_loss+= loss.item()          # get Python number from 1-element Tensor
13
14   # evaluate
15   with torch.no_grad():                  # operations inside don't track history
16       for input, targetin test_loader:
17           ...

有关更多详细信息,请参阅我们的官方文档和版本说明。


版本说明:https://github.com/pytorch/pytorch/releases/tag/v0.4.0






以上是关于PyTorch 4.0版本迁移指南的主要内容,如果未能解决你的问题,请参考以下文章

PyTorch经验指南:技巧与陷阱

PyTorch 0.4 升级指南

《Nuitka打包实战指南》实战打包PyTorch

《Nuitka打包实战指南》实战打包PyTorch

《Nuitka打包实战指南》实战打包PyTorch

从 Apache cassandra 3.11.4 迁移到 4.0 beta 版本时出现问题