详解pytorch 0.4.0迁移指南

时间:2021-10-09 08:47:07

总说

由于pytorch 0.4版本更新实在太大了, 以前版本的代码必须有一定程度的更新. 主要的更新在于 variable和tensor的合并., 当然还有windows的支持, 其他一些就是支持scalar tensor以及修复bug和提升性能吧. variable和tensor的合并导致以前的代码会出错, 所以需要迁移, 其实迁移代价并不大.

tensor和variable的合并

说是合并, 其实是按照以前(0.1-0.3版本)的观点是: tensor现在默认requires_grad=false的variable了.torch.tensortorch.autograd.variable现在其实是同一个类! 没有本质的区别! 所以也就是说,现在已经没有纯粹的tensor了, 是个tensor, 它就支持自动求导!你现在要不要给tensor包一下variable, 都没有任何意义了.

查看tensor的类型

使用.isinstance()或是x.type(), 用type()不能看tensor的具体类型.

?
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

requires_grad 已经是tensor的一个属性了

?
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
>>> x = torch.ones(1)
>>> x.requires_grad #默认是false
false
>>> y = torch.ones(1)
>>> z = x + y
>>> # 显然z的该属性也是false
>>> z.requires_grad
false
>>> # 所有变量都不需要grad, 所以会出错
>>> z.backward()
runtimeerror: element 0 of tensors does not require grad and does not have a grad_fn
>>>
>>> # 可以将`requires_grad`作为一个参数, 构造tensor
>>> w = torch.ones(1, requires_grad=true)
>>> w.requires_grad
true
>>> total = w + z
>>> total.requires_grad
true
>>> # 现在可以backward了
>>> total.backward()
>>> w.grad
tensor([ 1.])
>>> # x,y,z都是不需要梯度的,他们的grad也没有计算
>>> z.grad == x.grad == y.grad == none
true

通过.requires_grad()来进行使得tensor需要梯度.

不要随便用.data

以前.data是为了拿到variable中的tensor,但是后来, 两个都合并了. 所以.data返回一个新的requires_grad=false的tensor!然而新的这个tensor与以前那个tensor是共享内存的. 所以不安全, 因为

?
1
2
3
y = x.data # x需要进行autograd
# y和x是共享内存的,但是这里y已经不需要grad了,
# 所以会导致本来需要计算梯度的x也没有梯度可以计算.从而x不会得到更新!

所以, 推荐用x.detach(), 这个仍旧是共享内存的, 也是使得y的requires_grad为false,但是,如果x需要求导, 仍旧是可以自动求导的!

scalar的支持

这个非常重要啊!以前indexing一个一维tensor,返回的是一个number类型,但是indexing一个variable确实返回一个size为(1,)的vector.再比如一些reduction操作, 比如tensor.sum()返回一个number, 但是variable.sum()返回的是一个size为(1,)的vector.

scalar是0-维度的tensor, 所以我们不能简单的用以前的方法创建, 我们用一个torch.tensor注意,是小写的!

?
1
2
3
y = x.data # x需要进行autograd
# y和x是共享内存的,但是这里y已经不需要grad了,
# 所以会导致本来需要计算梯度的x也没有梯度可以计算.从而x不会得到更新!

从上面例子可以看出, 通过引入scalar, 可以将返回值的类型进行统一.
重点:
1. 取得一个tensor的值(返回number), 用.item()
2. 创建scalar的话,需要用torch.tensor(number)
3.torch.tensor(list)也可以进行创建tensor

累加loss

以前了累加loss(为了看loss的大小)一般是用total_loss+=loss.data[0], 比较诡异的是, 为啥是.data[0]? 这是因为, 这是因为loss是一个variable, 所以以后累加loss, 用loss.item().
这个是必须的, 如果直接加, 那么随着训练的进行, 会导致后来的loss具有非常大的graph, 可能会超内存. 然而total_loss只是用来看的, 所以没必要进行维持这个graph!

弃用volatile

现在这个flag已经没用了. 被替换成torch.no_grad(),torch.set_grad_enable(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

dypes,devices以及numpy-style的构造函数

dtype是data types, 对应关系如下:

详解pytorch 0.4.0迁移指南

通过.dtype可以得到

其他就是以前写device type都是用.cup()或是.cuda(), 现在独立成一个函数, 我们可以

?
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

新的创建tensor方法

主要是可以指定dtype以及device.

?
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来创建tensor

这个等价于numpy.array,用途:
1.将python list的数据用来创建tensor
2. 创建scalar

?
1
2
3
4
5
6
7
8
9
# 从列表中, 创建tensor
>>> 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.*like以及torch.new_*

第一个是可以创建, shape相同, 数据类型相同.

?
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, 但是shape不想要一致:

?
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)

书写 device-agnostic 的代码

这个含义是, 不要显示的指定是gpu, cpu之类的. 利用.to()来执行.

?
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)

迁移代码对比

以前的写法

?
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:
     ...
   ...

现在的写法

?
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:
    ...

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:https://www.cnblogs.com/z1141000271/p/9473096.html