PyTorch 0.4新版本 升级指南
【导读】今天大家比较关心的是PyTorch在GitHub发布0.4.0版本,专知成员Huaiwen详细讲解了PyTorch新版本的变动信息, 本次升级, 只做了一件事情, 就是把Tensor 类和 Variable 类 合并了, 且官方同时更新了API和Tutorials, 可以肯定, 以后的人不会再学0.3.1。专知成员Huaiwen也计划于今日更新一个系列的新版PyTorch简单上手, 希望大家持续关注。
专知成员Huaiwen以前推出一系列PyTorch教程:
PyTorch 终于从0.3.1升级到0.4.0了, 首先引入眼帘的,是PyTorch官方对自己的描述的巨大变化.
PyTorch 0.3.1说:
PyTorch is a python package that provides two high-level features:
• Tensor computation (like numpy) with strong GPU acceleration
• Deep Neural Networks built on a tape-based autodiff system
而PyTorch 0.4.0说:
PyTorch is a python based scientific computing package targeted at two sets of audiences:
• A replacement for NumPy to use the power of GPUs
• a deep learning research platform that provides maximum flexibility and speed
显然, 不断提升的功能和不断发展的社区, 给了PyTorch充足的底气.
本次升级, 只做了一件事情, 就是将Tensor 类和 Variable 类 合并, 这一合并, 解决掉了很多原来令人困扰的问题.
在旧版本, Variable和Tensor分离, Tensor主要是多维矩阵的封装, 而Variable类是计算图上的节点, 它对Tensor进行了进一步的封装.
所以, 在训练过程中, 一个必要的步骤就是, 把Tensor转成Variable以便在模型中运行; 运行完之后, 我们还要将Variable转成Tensor,甚至Numpy. 我们在写代码和读代码的时候, 看到了各种辅助函数, 比如下面就是我常用的辅助函数:
-
# 旧版本实现
-
import torch
-
-
# 从Tensor转换到Vairable
-
def
to_var
(
x
)
:
-
if torch
.cuda
.
is_available
(
)
:
-
x
= x
.
cuda
(
)
-
return
Variable
(x
)
-
-
# 从CUDA Variable转换到Numpy
-
def
to_np
(
x
)
:
-
return x
.data
.
cpu
(
)
.
numpy
(
)
-
-
for epoch
in
range
(
3
)
:
# 训练
3
轮
-
for step
,
(batch_x
, batch_y
)
in
enumerate
(loader
)
:
# 每一步
-
# 把训练数据转成Variable
-
batch_x
, batch_y
=
to_var
(batch_x
)
,
to_var
(batch_y
)
-
pass
0.4.0, 我们就可以不用这么转化了
-
for epoch
in
range
(
3
)
:
# 训练
3
轮
-
for
step
,
(batch_x
, batch_y
)
in
enumerate
(loader
)
:
# 每一步
-
optimizer
.
zero_grad
(
)
-
# forward
+
backward
+
optimize
-
outputs
=
net
(batch_x
)
-
loss
=
criterion
(outputs
, batch_y
)
-
loss
.
backward
(
)
-
optimizer
.
step
(
)
-
-
print
(
'Finished Training'
)
好处当然很大, 但是我们更关心以下几个问题:
Variable没了, Variable 的功能怎么办?
1.requires_grad 标志怎么处理了?
requires_grad 在Variable中,用来标志一个Variable是否要求导(或者说,要不要放到计算图中), 合并之后,这个标志处理的?
2.volatile 标志怎么处理了?
volatile在Variable中,用来标志一个Variable是否要被计算图隔离出去, 合并之后, 这个标志怎么处理的?
3.data方法呢?
Variable中,都是将封装的Tensor数据存储在.data里, 现在Variable和Tensor合并了, .data怎么办?
4.张量和标量怎么统一?
在Tensor元素内部都是Python 标量类型, 而Variable都是Tensor 张量类型, 原本它们井水不犯河水, 但现在合并了, 怎么处理?
-
# 旧版
0.3
.
1
-
>>>
import torch
-
>>>
from torch
.autograd
import Variable
-
>>> a
= torch
.
Tensor
(
[
1
,
2
,
3
]
)
-
>>> a
[
0
]
# 内部元素是Python 标量
-
1.0
-
>>>
type
(a
[
0
]
)
# 类别是Python float
-
<
class
'float'
>
-
>>> b
=
Variable
(a
)
-
>>> b
[
0
]
# 内部元素是Tensor类型
,
张量
-
Variable
containing
:
-
1
-
[torch
.FloatTensor
of size
1
]
合并之后的Tensor是什么样的?
5.合并之后, 新版本Tensor是什么类型?
回答如下
1
requires_grad 标志怎么处理了?
直接挂在Tensor类下
-
>>>
import torch
-
>>> x
= torch
.
ones
(
1
)
-
>>> x
.requires_grad
-
False
2
volatile 标志怎么处理了?
弃用 , 但是做了一些替代, 比如torch.no_grad(), torch.set_grad_enabled(grad_mode)
-
>>>
import torch
-
>>>
x
= torch
.
zeros
(
1
, requires_grad
=
True
)
-
>>>
with torch
.
no_grad
(
)
:
# 将y 从计算图中排除
-
...
y
= x
*
2
-
>>>
y
.requires_grad
-
False
3
data方法呢?
保留功能, 但建议替代为x.detach()
.data方法,本质上是给当前Tensor加一个新引用, 它们指向的内存都是一样的, 因此不安全 。
比如y = x.data(), 而x参与了计算图的运算, 那么, 如果你不小心修改了y的data, x的data也会跟着变, 然而反向传播是监听不到x的data变化的, 因此造成梯度计算错误。
y = x.detach()正如其名, 将返回一个不参与计算图的Tensor y, Tensor y 一旦试图改变修改自己的data, 会被语法检查和python解释器监测到, 并抛出错误.
4
张量和标量怎么统一?
新增0维张量(0-dimensional Tensor), 用以封装标量(scalar), 将张量(Tensor), 标量(Scalar)都统一成张量.
-
>>>
import
torch
-
>>>
torch
.
tensor
(
3.1416
) # 创建标量
-
tensor
(
3.1416
)
-
>>>
torch
.
tensor
(
3.1416
)
.
size
(
) # 其实是
0维的张量
-
torch
.
Size
(
[
]
)
-
>>>
torch
.
tensor
(
[
3
]
)
.
size
(
) #
1维张量
-
torch
.
Size
(
[
1
]
)
5
合并之后, 新版本Tensor是什么类型?
torch.Tensor类型, 但是, 详细类型需要进一步调用方法:
-
>>>
import torch
-
>>> x
= torch
.
DoubleTensor
(
[
1
,
1
,
1
]
)
-
>>>
type
(x
)
-
<
class
'torch.Tensor'
>
-
>>> x
.
type
(
)
-
'torch.DoubleTensor'
-
>>>
isinstance
(x
, torch
.DoubleTensor
)
-
True
旧版本的PyTorch, 你可以在类型上直接看出一个Tensor的基本信息, 比如device在cuda上, layout是sparse,dtype是Float型的Tensor, 你可以:
-
#
0.3
.
1
-
>>>
type
(a
)
-
<
class
'torch.cuda.sparse.FloatTensor'
>
由新版本, 所有的Tensor对外都是torch.Tensor类型, 上述的属性, 从类名转移到了Tensor的属性了.
• torch.device, 描述设备的位置, 比如torch.device('cuda'), torch.device('cpu')
-
>>>
import torch
-
>>> cuda
= torch
.
device
(
'cuda'
)
-
>>> cpu
= torch
.
device
(
'cpu'
)
-
>>> a
= torch
.
tensor
(
[
1
,
2
,
3
]
, device
=cuda
)
-
>>> a
.device
-
device
(type
=
'cuda'
, index
=
0
)
-
>>> b
= a
.
to
(cpu
)
# 将数据从cuda copy 到 cpu
-
>>> b
.device
-
device
(type
=
'cpu'
)
-
>>>
type
(a
)
# type a 和 tpye b
,
看不出谁在cuda谁在cpu
-
<
class
'torch.Tensor'
>
-
>>>
type
(b
)
-
<
class
'torch.Tensor'
>
• torch.layout
torch.layout 是 一个表示Tensor数据在内存中样子的类, 默认torch.strided, 即稠密的存储在内存上, 靠stride来刻画tensor的维度. 目前还有一个实验版的对象torch.sparse_coo, 一种coo格式的稀疏存储方式, 但是目前API还不固定, 大家谨慎使用.
• torch.dtype
后续
回答完上述疑问, 我们也对新版本的PyTorch有了新的认识, 由于去除了Variable类, 且官方同时更新了API和Tutorials, 我们可以肯定, 以后的人不会再学0.3.1. 我计划于近日更新一个系列的新版PyTorch简单上手, 希望大家持续关注。
GitHub 发布地址:https://github.com/pytorch/pytorch/releases
PyTorch 官网:http://pytorch.org/