!nvidia-smi
Fri Sep 9 12:48:41 2022
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 512.72 Driver Version: 512.72 CUDA Version: 11.6 |
|-------------------------------+----------------------+----------------------+
| GPU Name TCC/WDDM | Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|===============================+======================+======================|
| 0 NVIDIA GeForce ... WDDM | 00000000:01:00.0 Off | N/A |
| N/A 51C P8 10W / N/A | 0MiB / 6144MiB | 0% Default |
| | | N/A |
+-------------------------------+----------------------+----------------------+
+-----------------------------------------------------------------------------+
| Processes: |
| GPU GI CI PID Type Process name GPU Memory |
| ID ID Usage |
|=============================================================================|
| No running processes found |
+-----------------------------------------------------------------------------+
在PyTorch中,每个数组都有一个设备(device),我们通常及那个其称为上下文(context)。默认情况下,所有变量和相关的计算都分配给CPU,有时上下文是CPU。当我们跨多个服务器部署作业时,事情会变得更加棘手。通过智能地将数组分配给上下文,我们可以最大限度地减少在设备之间传输数据的时间。例如,当在带有GPU的服务器上训练神经网络时,我们通常希望模型的参数在GPU上
我们可以指定用于存储和计算的设备,比如CPU和GPU。莫情况下,张量是在内存中创建,使用CPU计算它
在PyTorch中,CPU和GPU可以用torch.device(‘cpu’)和torch.device(‘cuda’)表示。应该注意的是,cpu设备意味着所有物理CPU和内存,这意味着PyTorch的计算将尝试使用所有CPU核心。然而,GPU设备只代表一个卡和相应的显存。若有多个GPU,我们使用torch.device(f’cuda:{i}')来表示第i块GPU,另外,cuda:0和cuda是等价的
import torch
from torch import nn
torch.device('cpu'),torch.device('cuda'), torch.device('cuda:1')
(device(type='cpu'), device(type='cuda'), device(type='cuda', index=1))
查看可以GPU数量
torch.cuda.device_count()
1
我们定义两个方便的函数,这两个函数允许我们在不存在所需GPU的情况下允许代码
def try_gpu(i = 0): #@save
"""如果存在,则返回gpu(i),否则返回cpu()"""
if torch.cuda.device_count() >= i + 1:
return torch.device(f'cuda:{i}')
return torch.device('cpu')
def try_all_gpus(): #@save
"""返回所有可用的GPU,如果没有GPU,则返回[cpu(),]"""
devices = [torch.device(f'cuda:{i}')
for i in range(torch.cuda.device_count())]
return devices if devices else [torch.device('cpu')]
try_gpu(), try_gpu(10), try_all_gpus()
(device(type='cuda', index=0),
device(type='cpu'),
[device(type='cuda', index=0)])
我们可用查询张量所在的设备。默认情况下,张量是在CPU上创建的
x = torch.tensor([1,2,3])
x.device
device(type='cpu')
需要注意的是,⽆论何时我们要对多个项进⾏操作,它们都必须在同⼀个设备上。例如,如果我们对两个张量求和,我们需要确保两个张量都位于同⼀个设备上,否则框架将不知道在哪⾥存储结果,甚⾄不知道在哪⾥执⾏计算
有⼏种⽅法可以在GPU上存储张量。例如,我们可以在创建张量时指定存储设备。接下来,我们在第⼀个gpu上创建张量变量X。在GPU上创建的张量只消耗这个GPU的显存。我们可以使⽤nvidia-smi命令查看显存使⽤情况。⼀般来说,我们需要确保不创建超过GPU显存限制的数据
X = torch.ones(2,3,device = try_gpu())
X
tensor([[1., 1., 1.],
[1., 1., 1.]], device='cuda:0')
假设你⾄少有两个GPU,下⾯的代码将在第⼆个GPU上创建⼀个随机张量
Y = torch.rand(2, 3, device=try_gpu(1))
Y
tensor([[0.6770, 0.3752, 0.0100],
[0.4635, 0.7391, 0.4980]])
如果我们要计算X + Y,我们需要决定在哪⾥执⾏这个操作。例如,如 图5.6.1所⽰,我们可以将X传输到第⼆个GPU并在那⾥执⾏操作。不要简单地X加上Y,因为这会导致异常,运⾏时引擎不知道该怎么做:它在同⼀设备上找不到数据会导致失败。由于Y位于第⼆个GPU上,所以我们需要将X移到那⾥,然后才能执⾏相加运算
⼈们使⽤GPU来进⾏机器学习,因为单个GPU相对运⾏速度快。但是在设备(CPU、GPU和其他机器)之间传输数据⽐计算慢得多。这也使得并⾏化变得更加困难,因为我们必须等待数据被发送(或者接收),然后才能继续进⾏更多的操作
根据经验,多个⼩操作⽐⼀个⼤操作糟糕得多。此外,⼀次执⾏⼏个操作⽐代码中散布的许多单个操作要好得多(除⾮你确信⾃⼰在做什么)。如果⼀个设备必须等待另⼀个设备才能执⾏其他操作,那么这样的操作可能会阻塞
最后,当我们打印张量或将张量转换为NumPy格式时,如果数据不在内存中,框架会⾸先将其复制到内存中,这会导致额外的传输开销。更糟糕的是,它现在受制于全局解释器锁,使得⼀切都得等待Python完成
类似地,神经网络模型可以指定设备,下面的代码将模型参数放在GPU上
net = nn.Sequential(nn.Linear(3,1))
net = net.to(device = try_gpu())
在接下来的⼏章中,我们将看到更多关于如何在GPU上运⾏模型的例⼦,因为它们将变得更加计算密集
当输⼊为GPU上的张量时,模型将在同⼀GPU上计算结果
net(X)
tensor([[-0.1674],
[-0.1674]], device='cuda:0', grad_fn=)
让我们确认模型参数存储在同一个GPU上
net[0].weight.data.device
device(type='cuda', index=0)
总而言之,当所有的数据和参数都在同一个设备上,我们就可以有效地学习模型