Pytorch 搭建神经网络(6)GPU 加速:CUDA 的使用
基于《深度学习框架 Pytorch 入门与实践》陈云
参考 Github 的 pytorch-book 项目
笔记和代码存储在我的 GitHub 库中 github.com/isKage/pytorch-notes
使用 GPU 加速技术,可以大幅减少训练时间。Pytorch 中的 Tensor
张量和 nn.Module
类就分为 CPU 和 GPU 两种版本。一般使用 .cuda()
和 .to(device)
方法实现从 CPU 迁移到 GPU ,从设备迁移到设备。
但 Tensor
和 nn.Module
使用 .cuda()
方法时返回的对象不同。
Tensor.cuda()
返回一个新对象,即拷贝了一份张量到 GPU ,之前的张量仍然储存在 CPU
nn.Module
实例化后的 module
使用 module.cuda()
直接将所有数据推送到 GPU 不备份自己,即 module.cuda()
与 module = module.cuda()
等价
.to(device)
可以更灵活地在不同设备上迁移
1 .cuda() 方法
1.1 张量 .cuda() 返回新的对象
1 2 3 4 5 6 7 8 9 10 import torcht = torch.tensor([1 , 2 , 3 ]) if torch.cuda.is_available(): t_cuda = t.cuda() print (t.is_cuda) print (t_cuda.is_cuda) else : print ("no CUDA" )
.cuda()
等价于 .cuda(0)
or .cuda(device=0)
迁移到第 0 块 GPU 上
1.2 module.cuda() 返回自己
1 2 3 4 5 6 7 8 9 from torch import nnmodule = nn.Linear(3 , 4 ) if torch.cuda.is_available(): module.cuda(device=0 ) print (module.weight.is_cuda) else : print ("no CUDA" )
迁移到 GPU 本质都是对张量 Tensor 做变换,所以对于模型 module ,也是其权重等参数进行迁移
2 .to(device) 方法
.to(device)
方法可以指定设备
1 2 3 4 5 6 7 8 t = torch.tensor([1 , 2 , 3 ]) if torch.cuda.is_available(): t_cuda = t.to('cuda:0' ) print (t_cuda.is_cuda) else : print ("no CUDA" )
3 损失函数迁移到 GPU
大部分的损失函数都属于 nn.Module
,在使用 GPU 时,建议使用 .cuda
或 .to
迁移到 GPU 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 if torch.cuda.is_available(): criterion = torch.nn.CrossEntropyLoss(weight=torch.Tensor([1 , 3 ])) inputs = torch.randn(4 , 2 ).cuda() target = torch.Tensor([1 , 0 , 0 , 1 ]).long().cuda() criterion.cuda() loss = criterion(inputs, target) print (criterion._buffers)
1 [Out]: OrderedDict([('weight' , tensor([1. , 3. ], device='cuda:0' ))])
4 torch.cuda.device() 指定默认设备
使用 torch.cuda.device()
指定默认设备,则不需要每次调用 .cuda
或 .to
方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 with torch.cuda.device(0 ): a = torch.cuda.FloatTensor(2 , 3 ) b = torch.FloatTensor(2 , 3 ).cuda() print (a.get_device) print (b.get_device) c = a + b print (c.get_device)
1 2 3 <built-in method get_device of Tensor object at 0x0000019C70C59CC0 > <built-in method get_device of Tensor object at 0x0000019C04443930 > <built-in method get_device of Tensor object at 0x0000019C04441360 >
或者使用 torch.set_default_tensor_type()
方法,指定张量类型
1 2 3 4 5 torch.set_default_tensor_type('torch.cuda.FloatTensor' ) a = torch.ones(2 , 3 ) print (a.is_cuda) torch.set_default_tensor_type('torch.FloatTensor' )
5 多 GPU 操作
多个 GPU 方便快捷地指定迁移设备。
5.1 方法一:调用 torch.cuda.set_device()
例如:指定先调用 torch.cuda.set_device(1)
指定默认使用 GPU “cuda:1” ,后续的 .cuda()
都无需更改,切换 GPU 只需修改这一行代码
5.2 方法二:设置环境变量 CUDA_VISIBLE_DEVICES
例如当 CUDA_VISIBLE_DEVICE=1
时,代表优先使用 GPU “cuda:1” 而不是 GPU “cuda:0” 。此时调用tensor.cuda()
会将Tensor转移至 GPU “cuda:1”
CUDA_VISIBLE_DEVICES
还可以指定多个 GPU 。例如 CUDA_VISIBLE_DEVICES=0,2,3
代表按照 GPU “cuda:0”, “cuda:2”, “cuda:3” 的顺序使用 GPU 。此时 cuda(0)
迁移到 GPU “cuda:0” ,而 .cuda(1)
迁移到 GPU “cuda:2” ,.cuda(2)
迁移到 GPU “cuda:3” 。
5.3 设置 CUDA_VISIBLE_DEVICES
的方法:
法一:命令行中执行 CUDA_VISIBLE_DEVICES=0,1 python main.py
来运行主程序
法二:程序中编写
1 2 import osos.environ['CUDA_VISIBLE_DEVICES' ] = '0,1'
法三:IPython 或者 Jupyter notebook 中(即 .ipynb
文件),则可以使用魔法方法
1 %env CUDA_VISIBLE_DEVICES=0 ,1
6 CPU 与 GPU 并存
考虑到不同电脑可能会有差异,例如当遇到无 GPU 的主机时,容易出现不兼容。(例如:无法迁移到 GPU),所以建议判断是否存在 GPU ,再迁移数据。
1 2 3 device = torch.device("cuda" if torch.cuda.is_available() else "cpu" ) print (device)
确定了设备之后,再迁移
1 2 3 4 5 x = torch.randn(2 , 3 ) x_to = x.to(device) print (x_to.device)
7 张量指定设备
7.1 创建张量时指定设备
1 2 3 4 5 6 7 device = torch.device("cuda" if torch.cuda.is_available() else "cpu" ) x_cpu = torch.empty(2 , device='cpu' ) print (x_cpu, x_cpu.is_cuda) x_gpu = torch.empty(2 , device=device) print (x_gpu, x_gpu.is_cuda)
7.2 new_* 保留原属性
1 2 3 4 5 6 7 y_cpu = x_cpu.new_full((3 , 4 ), 9 ) print (y_cpu, y_cpu.is_cuda) y_gpu = x_gpu.new_zeros(3 , 4 ) print (y_gpu, y_gpu.is_cuda)
7.3 *_like 保留原属性
1 2 3 4 5 6 7 8 z_cpu = torch.ones_like(x_cpu) print (z_cpu, z_cpu.is_cuda) z_gpu = torch.zeros_like(x_gpu) print (z_gpu, z_gpu.is_cuda)