PyTorch 笔记(19)— Tensor 用 GPU 加速

PyTorch 中以下数据结构分为 CPUGPU 两个版本:

  • Tensor
  • nn.Module (包括常用的 layerloss function ,以及容器 Sequential 等)

它们都带有一个 .cuda 方法,调用此方法即可将其转为对应的 GPU 对象。

注意,tensor.cuda 会返回一个新对象,这个新对象的数据已转移至GPU,而之前的 tensor 还在原来的设备上(CPU )。而 module.cuda 则会将所有的数据都迁移至 GPU ,并返回自己。所以 module = module.cuda()module.cuda() 所起的作用一致。

tensor = t.Tensor(3, 4)
# 返回一个新的tensor,但原来的tensor并没有改变
tensor.cuda(0)
tensor.is_cuda  # False

重新赋给自己,tensor 指向 cuda 上的数据,不再执行原数据。不指定使用的 GPU 设备,将默认使用第 1 块 GPU

tensor = tensor.cuda()
tensor.is_cuda	# True

nn.ModuleGPUCPU 之间的转换,本质上还是利用了 TensorGPUCPU 之间的转换。nn.Modulecuda 方法是将 nn.Module 下的所有 parameter (包括子 moduleparameter )都转移至GPU ,而 Parameter 本质上也是 tensor ( Tensor 的子类)。

# 将nn.Module模型放到cuda上,其子模型也都自动放到cuda上
from torch import nn
module = nn.Linear(3, 4)
module.cuda(device = 0)
module.weight.is_cuda	# True

注意: 为什么将数据转移至 GPU 的方法叫做 .cuda 而不是 .gpu ,就像将数据转移至 CPU 调用的方法是 .cpu
这是因为 GPU 的编程接口采用 CUDA ,而目前并不是所有的 GPU 都支持 CUDA ,只有部分 NvidiaGPU 才支持。PyTorch 未来可能会支持 AMDGPU ,而 AMD GPU 的编程接口采用 OpenCL ,因此PyTorch 还预留着 .cl 方法,用于以后支持 AMD 等的 GPU

下面将举例说明,这部分代码需要你具有两块GPU设备。

class VeryBigModule(nn.Module):
    def __init__(self):
        super(VeryBigModule, self).__init__()
        self.GiantParameter1 = t.nn.Parameter(t.randn(100000, 20000)).cuda(0)
        self.GiantParameter2 = t.nn.Parameter(t.randn(20000, 100000)).cuda(1)
    
    def forward(self, x):
        x = self.GiantParameter1.mm(x.cuda(0))
        x = self.GiantParameter2.mm(x.cuda(1))
        return x

上面最后一部分中,两个 Parameter 所占用的内存空间都非常大,大概是 8 个 G,如果将这两个都同时放在一块 GPU 上几乎会将显存占满,无法再进行任何其它运算。此时可通过这种方式将不同的计算分布到不同的 GPU 中。

关于使用 GPU 的一些建议:

  • GPU 运算很快,但对于很小的运算量来说,并不能体现出它的优势,因此对于一些简单的操作可直接利用CPU 完成;
  • 数据在 CPUGPU 之间,以及 GPUGPU 之间的传递会比较耗时,应当尽量避免;
  • 在进行低精度的计算时,可以考虑 HalfTensor ,它相比于 FloatTensor 能节省一半的显存,但需千万注意数值溢出的情况;

而除了调用对象的 .cuda 方法之外,还可以使用 torch.cuda.device ,来指定默认使用哪一块 GPU ,或使用torch.set_default_tensor_type 使程序默认使用 GPU ,不需要手动调用 cuda

# 如果未指定使用哪块GPU,默认使用GPU 0
x = t.cuda.FloatTensor(2, 3)
# x.get_device() == 0
y = t.FloatTensor(2, 3).cuda()
# y.get_device() == 0

# 指定默认使用GPU 0
with t.cuda.device(0):    
    # 在GPU 0上构建tensor
    a = t.cuda.FloatTensor(2, 3)

    # 将tensor转移至GPU 0
    b = t.FloatTensor(2, 3).cuda()
    print(a.get_device() == b.get_device() == 0 )	# True

    c = a + b
    print(c.get_device() == 0)	# True

    z = x + y
    print(z.get_device() == 0)	# True

    # 手动指定使用GPU 0
    d = t.randn(2, 3).cuda(0)
    print(d.get_device() == 2)	# False
# 指定默认tensor的类型为GPU上的FloatTensor
t.set_default_tensor_type('torch.cuda.FloatTensor') 
a = t.ones(2, 3)
a.is_cuda # True

如果服务器具有多个 GPUtensor.cuda() 方法会将 tensor 保存到第一块 GPU 上,等价于tensor.cuda(0) 。此时如果想使用第二块 GPU ,需手动指定 tensor.cuda(1) ,而这需要修改大量代码,很是繁琐。这里有两种替代方法:

  • 一种是先调用 t.cuda.set_device(1) 指定使用第二块 GPU ,后续的 .cuda() 都无需更改,切换 GPU 只需修改这一行代码。
  • 更推荐的方法是设置环境变量 CUDA_VISIBLE_DEVICES ,例如当 export CUDA_VISIBLE_DEVICE=1 (下标是从 0 开始,1 代表第二块 GPU ),只使用第二块物理 GPU ,但在程序中这块 GPU 会被看成是第一块逻辑 GPU ,因此此时调用 tensor.cuda() 会将 Tensor 转移至第二块物理 GPUCUDA_VISIBLE_DEVICES 还可以指定多个 GPU ,如 export CUDA_VISIBLE_DEVICES=0,2,3 ,那么第一、三、四块物理 GPU 会被映射成第一、二、三块逻辑 GPUtensor.cuda(1) 会将 Tensor 转移到第三块物理 GPU 上。

设置 CUDA_VISIBLE_DEVICES 有两种方法:

  • 一种是在命令行中 CUDA_VISIBLE_DEVICES=0,1 python main.py
  • 一种是在程序中
import os
os.environ["CUDA_VISIBLE_DEVICES"] = "2"

如果使用 IPython 或者 Jupyter notebook,还可以使用 %env CUDA_VISIBLE_DEVICES=1,2 来设置环境变量。

从 0.4 版本开始,PyTorch 新增了 tensor.to(device) 方法,能够实现设备透明,便于实现 CPU/GPU 兼容。

x = torch.randn(1)
if torch.cuda.is_available():
    device = torch.device("cuda")            
    y = torch.ones_like(x, device=device)    # directly create a tensor on GPU
    x = x.to(device)                         
    z = x + y
    print(z)
    print(z.to("cpu", torch.double))  

输出:

tensor([ 2.0718], device='cuda:0')
tensor([ 2.0718], dtype=torch.float64)

PyTorch 支持分布式 GPU 。分布式是指有多个 GPU 在多台服务器上,而并行一般指的是一台服务器上的多个 GPU。分布式涉及到了服务器之间的通信,因此比较复杂,PyTorch 封装了相应的接口,可以用几句简单的代码实现分布式训练。分布式对普通用户来说比较遥远,因为搭建一个分布式集群的代价十分大,使用也比较复杂。相比之下一机多卡更加现实。

  • 17
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值