强烈推荐浅谈将Pytorch模型从CPU转换成GPU
Posted 奇点
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了强烈推荐浅谈将Pytorch模型从CPU转换成GPU相关的知识,希望对你有一定的参考价值。
主要内容:最近将Pytorch程序迁移到GPU上去的一些工作和思考 环境:Ubuntu 16.04.3 Python版本:3.5.2 Pytorch版本:0.3.0
大家知道,在深度学习中使用GPU来对模型进行训练是可以通过并行化其计算来提高运行效率,这里就不多谈了。
最近申请到了实验室的服务器来跑程序,成功将我简陋的程序改成了“高大上”GPU版本。
看到网上没有太多这方面的介绍(TensorFlow的倒是不少~),这里决定将我的一些思考和工作记录下来。
由于我使用的是Pytorch写的模型,网上给出了一个非常简单的转换方式: 对模型和相应的数据使用.cuda()处理。通过这种方式,我们就可以将内存中的数据复制到GPU的显存中去。从而可以通过GPU来进行运算了。
网上说的非常简单,但是实际使用过程中还是遇到了一些疑惑。下面分数据和模型两方面的迁移来进行说明介绍。
判定使用GPU
下载了对应的GPU版本的Pytorch之后,要确保GPU是可以进行使用的,通过`torch.cuda.is_available()`的返回值来进行判断。返回`True`则具有能够使用的GPU。
通过`torch.cuda.device_count()`可以获得能够使用的GPU数量。其他就不多赘述了。
常常通过如下判定来写可以跑在GPU和CPU上的通用模型:
if torch.cuda.is_available(): ten1 = ten1.cuda() MyModel = MyModel.cuda()
数据方面常用的主要是两种 —— Tensor和Variable。实际上这两种类型是同一个东西,因为Variable实际上只是一个容器,这里先视其不同。
将Tensor迁移到显存中去
不论是什么类型的Tensor(FloatTensor或者是LongTensor等等),一律直接使用方法.cuda()即可。
例如:
ten1 = torch.FloatTensor(2)
>>>> 6.1101e+24 4.5659e-41 [torch.FloatTensor of size 2] ten1_cuda = ten1.cuda()
>>>> 6.1101e+24 4.5659e-41[torch.cuda.FloatTensor of size 2 (GPU 0)]
其数据类型会由`torch.FloatTensor`变为`torch.cuda.FloatTensor (GPU 0)`这样代表这个数据现在存储在
`GPU 0`的显存中了。
如果要将显存中的数据复制到内存中,则对cuda数据类型使用`.cpu()`方法即可。
将Variable迁移到显存中去
在模型中,我们最常使用的是Variable这个容器来装载使用数据。主要是由于Variable可以进行反向传播来进行自动求导。
同样地,要将Variable迁移到显存中,同样只需要使用`.cuda()`即可实现。
这里笔者先前有一个小疑问,对Variable直接使用`.cuda`和对Tensor进行`.cuda`然后再放置到Variable中结果是否一致呢。答案是肯定的。
ten1 = torch.FloatTensor(2)
>>> 6.1101e+24 4.5659e-41 [torch.FloatTensor of size 2]
ten1_cuda = ten1.cuda()
>>>> 6.1101e+24 4.5659e-41 [torch.cuda.FloatTensor of size 2 (GPU 0)]
V1_cpu = autograd.Variable(ten1)>>>> Variable containing: 6.1101e+24 4.5659e-41 [torch.FloatTensor of size 2]
V2 = autograd.Variable(ten1_cuda)
>>>> Variable containing: 6.1101e+24 4.5659e-41 [torch.cuda.FloatTensor of size 2 (GPU 0)]
V1 = V1_cpu.cuda()>>>> Variable containing: 6.1101e+24 4.5659e-41 [torch.cuda.FloatTensor of size 2 (GPU 0)]
最终我们能发现他们都**能够达到相同的目的**,但是他们完全一样了吗?我们使用`V1 is V2`发现,结果是否定的。
对于V1,我们是直接对Variable进行操作的,这样子V1的`.grad_fn`中会记录下创建的方式。因此这二者并不是完全相同的。
数据迁移小结
`.cuda()`操作默认使用GPU 0也就是第一张显卡来进行操作。当我们想要存储在其他显卡中时可以使用`.cuda(<显卡号数>)`来将数据存储在指定的显卡中。还有很多种方式,具体参考官方文档。
对于不同存储位置的变量,我们是不可以对他们直接进行计算的。存储在不同位置中的数据是不可以直接进行交互计算的。
换句话说也就是上面例子中的`torch.FloatTensor`是不可以直接与`torch.cuda.FloatTensor`进行基本运算的。位于不同GPU显存上的数据也是不能直接进行计算的。
对于Variable,其实就仅仅是一种能够记录操作信息并且能够自动求导的容器,实际上的关键信息并不在Variable本身,而更应该侧重于Variable中存储的data。
这里使用基本的单层感知机来进行举例(线性模型)。
data1 = torch.FloatTensor(2)
data2 = data1.cuda# 创建一个输入维度为2,输出维度为2的单层神经网络
linear = torch.nn.Linear(2, 2)
>>>> Linear(in_features=2, out_features=2)
linear_cuda = linear.cuda()
>>>> Linear(in_features=2, out_features=2)
我们很惊奇地发现对于模型来说,不像数据那样使用了`.cuda()`之后会改变其的数据类型。模型看起来没有任何的变化。
但是他真的没有改变吗。
我们将`data1`投入`linear_cuda`中去可以发现,系统会报错,而将`.cuda`之后的data2投入`linear_cuda`才能正常工作。并且输出的也是具有cuda的数据类型。
那是怎么一回事呢?
这是因为这些所谓的模型,其实也就是对输入参数做了一些基本的矩阵运算。所以我们对`模型.cuda()`实际上也相当于将模型使用到的参数存储到了显存上去。
对于上面的例子,我们可以通过观察参数来发现区别所在。
linear.weight
>>>> Parameter containing: -0.6847 0.2149 -0.5473 0.6863 [torch.FloatTensor of size 2x2]
linear_cuda.weight
>>>> Parameter containing: -0.6847 0.2149 -0.5473 0.6863 [torch.cuda.FloatTensor of size 2x2 (GPU 0)]
自己模型的迁移
对于自己创建的模型类,由于继承了`torch.nn.Module`,则可同样使用`.cuda()`来将模型中用到的所有参数都存储到显存中去。
这里笔者曾经有一个疑问:当我们对模型存储到显存中去之后,那么这个模型中的方法后面所创建出来的Tensor是不是都会默认变成cuda的数据类型。答案是否定的,具体留给读者自己去做实验。
模型小结
对于模型而言,我们可以将其看做是一种类似于Variable的容器。我们对它进行`.cuda()`处理,是将其中的参数放到显存上去(因为实际使用的时候也是通过这些参数做运算)。
Pytorch使用起来直接简单,迁移方式也是简单明了。而对于多GPU和CPU的协同使用则还需要进一步地加强学习。
以上是关于强烈推荐浅谈将Pytorch模型从CPU转换成GPU的主要内容,如果未能解决你的问题,请参考以下文章
强烈推荐浅谈Java中String.equals()和==的区别
强烈推荐浅谈Java中String.equals()和==的区别