《动手学深度学习》笔记2.5——神经网络从基础→使用GPU (CUDA-单卡-多卡-张量操作)

目录

[0. 前言](#0. 前言)

原书正文

[1. 计算设备 (CPU和GPU)](#1. 计算设备 (CPU和GPU))

补充:torch版本cuda报错的解决方案

[2. 张量与GPU](#2. 张量与GPU)

[3. 存储在GPU上](#3. 存储在GPU上)

[4. 复制(多卡操作)](#4. 复制(多卡操作))

[5. 旁注 (CPU和GPU之间挪数据)](#5. 旁注 (CPU和GPU之间挪数据))

[6. 神经网络与GPU](#6. 神经网络与GPU)

小结


0. 前言

原书正文

在 :numref:tab_intro_decade中, 我们回顾了过去20年计算能力的快速增长。 简而言之,自2000年以来,GPU性能每十年增长1000倍。

本节,我们将讨论如何利用这种计算性能进行研究。 首先是如何使用单个GPU,然后是如何使用多个GPU和多个服务器(具有多个GPU)。

我们先看看如何使用单个NVIDIA GPU进行计算。 首先,确保至少安装了一个NVIDIA GPU。 然后,下载NVIDIA驱动和CUDA 并按照提示设置适当的路径。 当这些准备工作完成,就可以使用nvidia-smi命令来(查看显卡信息。)

In [1]:

python 复制代码
!nvidia-smi
复制代码
Out[1]:
Thu Sep 26 14:05:46 2024       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 472.19       Driver Version: 472.19       CUDA Version: 11.4     |
|-------------------------------+----------------------+----------------------+
| 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  On |                  N/A |
| N/A   40C    P0    13W /  N/A |    635MiB /  4096MiB |      2%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
|    0   N/A  N/A      1780    C+G   ...ge\Application\msedge.exe    N/A      |
|    0   N/A  N/A      5080    C+G   ...2txyewy\TextInputHost.exe    N/A      |
|    0   N/A  N/A      7448    C+G   ...5n1h2txyewy\SearchApp.exe    N/A      |
|    0   N/A  N/A     10332    C+G   ...lPanel\SystemSettings.exe    N/A      |
|    0   N/A  N/A     12196    C+G   ...perience\NVIDIA Share.exe    N/A      |
|    0   N/A  N/A     12516    C+G   ...perience\NVIDIA Share.exe    N/A      |
|    0   N/A  N/A     13000    C+G   Insufficient Permissions        N/A      |
|    0   N/A  N/A     16252    C+G   Insufficient Permissions        N/A      |
|    0   N/A  N/A     19636    C+G   C:\Windows\explorer.exe         N/A      |
|    0   N/A  N/A     19812    C+G   ...y\ShellExperienceHost.exe    N/A      |
+-----------------------------------------------------------------------------+

在PyTorch中,每个数组都有一个设备(device), 我们通常将其称为环境(context)。 默认情况下,所有变量和相关的计算都分配给CPU。 有时环境可能是GPU。 当我们跨多个服务器部署作业时,事情会变得更加棘手。 通过智能地将数组分配给环境, 我们可以最大限度地减少在设备之间传输数据的时间。 例如,当在带有GPU的服务器上训练神经网络时, 我们通常希望模型的参数在GPU上。

要运行此部分中的程序,至少需要两个GPU。 注意,对大多数桌面计算机来说,这可能是奢侈的,但在云中很容易获得。 例如可以使用AWS EC2的多GPU实例。 本书的其他章节大都不需要多个GPU, 而本节只是为了展示数据如何在不同的设备之间传递。

**1. 计算设备 (**CPU和GPU)

我们可以指定用于存储和计算的设备,如CPU和GPU。 默认情况下,张量是在内存中创建的,然后使用CPU计算它。

在PyTorch中,CPU和GPU可以用torch.device('cpu')torch.device('cuda')表示。 应该注意的是,cpu设备意味着所有物理CPU和内存, 这意味着PyTorch的计算将尝试使用所有CPU核心。 然而,gpu设备只代表一个卡和相应的显存。 如果有多个GPU,我们使用torch.device(f'cuda:{i}') 来表示第𝑖块GPU(𝑖从0开始)。 另外,cuda:0cuda是等价的。

In [2]:

python 复制代码
import torch

from torch import nn
​
torch.device('cpu'), torch.device('cuda'), torch.device('cuda:1')  # 若有多个GPU,cuda:1表示访问第一个
复制代码
Out[2]:
复制代码
(device(type='cpu'), device(type='cuda'), device(type='cuda', index=1))

我们可以(查询可用gpu的数量。)

In [3]:

python 复制代码
print(torch.cuda.is_available()) # 查询cuda是否可用,显示False,大概是因为torch版本不对,cpu版的torch不行

print(torch.cuda.device_count())  # 查询可用gpu的数量,显示0,得看看cuda和pytorch的版本是否匹配
复制代码
Out[3]:
True
1

补充:torch版本cuda报错的解决方案

个人补充:torch版本不对,得找匹配cuda版本的torch 推荐教程(本人亲测成功):动手学深度学习-GPU常见报错-CUDA11.4-AssertionError: Torch not compiled with CUDA enabled-CSDN博客

现在我们定义了两个方便的函数, [这两个函数允许我们在不存在所需所有GPU的情况下运行代码。]

In [4]:

python 复制代码
def try_gpu(i=0): #@save # @save指的是存到了d2l包里,后面会用到

    """如果存在,则返回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())]  # 遍历所有GPU
    return devices if devices else [torch.device('cpu')]  # (除cpu之外)只返回gpu
​
print(try_gpu())  # 用print()
print(try_gpu(10))  # 没有10个gpu,就返回cpu
print(try_all_gpus())
​
try_gpu(), try_gpu(10), try_all_gpus()  # 不用print()
复制代码
cuda:0
cpu
[device(type='cuda', index=0)]

Out[4]:

复制代码
(device(type='cuda', index=0),
 device(type='cpu'),
 [device(type='cuda', index=0)])

2. 张量与GPU

我们可以[查询张量所在的设备。] 默认情况下,张量是在CPU上创建的。

In [5]:

python 复制代码
x = torch.tensor([1, 2, 3]) # 查询张量所在的设备

x.device  # 默认在cpu内存上创建
复制代码
Out[5]:
复制代码
device(type='cpu')

需要注意的是,无论何时我们要对多个项进行操作, 它们都必须在同一个设备上。 例如,如果我们对两个张量求和, 我们需要确保两个张量都位于同一个设备上, 否则框架将不知道在哪里存储结果,甚至不知道在哪里执行计算。

3. 存储在GPU上

有几种方法可以在GPU上存储张量。 例如,我们可以在创建张量时指定存储设备。接 下来,我们在第一个gpu上创建张量变量X。 在GPU上创建的张量只消耗这个GPU的显存。 我们可以使用nvidia-smi命令查看显存使用情况。 一般来说,我们需要确保不创建超过GPU显存限制的数据。

In [6]:

python 复制代码
X = torch.ones(2, 3, device=try_gpu()) # 默认放在第0个GPU显存上

X
复制代码
Out[6]:
复制代码
tensor([[1., 1., 1.],
        [1., 1., 1.]], device='cuda:0')

假设我们至少有两个GPU,下面的代码将在(第二个GPU上创建一个随机张量。)

In [7]:

python 复制代码
Y = torch.rand(2, 3, device=try_gpu(1)) # 这里没有第2张GPU,因此实际上是在cpu上创建的

Y  # 对比上一个输出,本次执行的输出没有显示"device='cuda:0'",说明该张量是在cpu上创建的
复制代码
Out[7]:
复制代码
tensor([[0.9381, 0.7002, 0.2132],
        [0.3292, 0.8469, 0.2711]])

4. 复制(多卡操作)

如果我们[要计算X + Y,我们需要决定在哪里执行这个操作 ]。 例如,如 :numref:fig_copyto所示, 我们可以将X传输到第二个GPU并在那里执行操作。 不要 简单地X加上Y,因为这会导致异常, 运行时引擎不知道该怎么做:它在同一设备上找不到数据会导致失败。 由于Y位于第二个GPU上,所以我们需要将X移到那里, 然后才能执行相加运算。

:label:fig_copyto

In [8]:

python 复制代码
"""
Z = X.cuda(1)  # 咱只有一张卡,所以会报错CUDA error: invalid device ordinal
print(X)
print(Z)
"""

**现在数据在同一个GPU上(`Z`和`Y`都在),我们可以将它们相加。**

In [9]:

python 复制代码
# Y + Z # 现在只有一张卡,就直接注释掉啦,如果有两张卡,可以取消注释跑跑看

假设变量Z已经存在于第二个GPU上。 如果我们还是调用Z.cuda(1)会发生什么? 它将返回Z,而不会复制并分配新内存。

In [10]:

python 复制代码
# Z.cuda(1) is Z # 现在只有一张卡,就直接注释掉啦,如果有两张卡,可以取消注释跑跑看

李沐老师有两张卡,看看他演示的输出:

5. 补充 (CPU和GPU之间挪数据)

李沐老师谈为什么不能直接 Y + X :

"实际上这样实现也不会报错,不这么做的历史原因是GPU之间挪数据,特别在CPU和GPU之间挪数据(假设某一层数据建在CPU上,别的都在GPU上),来来回回传数据会特别慢,出现性能问题,还很难debug"

人们使用GPU来进行机器学习,因为单个GPU相对运行速度快。 但是在设备(CPU、GPU和其他机器)之间传输数据比计算慢得多。 这也使得并行化变得更加困难,因为我们必须等待数据被发送(或者接收), 然后才能继续进行更多的操作。 这就是为什么拷贝操作要格外小心。 根据经验,多个小操作比一个大操作糟糕得多。 此外,一次执行几个操作比代码中散布的许多单个操作要好得多。 如果一个设备必须等待另一个设备才能执行其他操作, 那么这样的操作可能会阻塞。 这有点像排队订购咖啡,而不像通过电话预先订购: 当客人到店的时候,咖啡已经准备好了。

最后,当我们打印张量或将张量转换为NumPy格式时, 如果数据不在内存中,框架会首先将其复制到内存中, 这会导致额外的传输开销。 更糟糕的是,它现在受制于全局解释器锁,使得一切都得等待Python完成。

6. 神经网络与GPU

类似地,神经网络模型可以指定设备。 下面的代码将模型参数放在GPU上。

In [11]:

python 复制代码
net = nn.Sequential(nn.Linear(3, 1))

net = net.to(device=try_gpu())  # .to 这个方法(method) 传入函数try_gpu(),相当于把全部参数复制到第0号GPU

在接下来的几章中, 我们将看到更多关于如何在GPU上运行模型的例子, 因为它们将变得更加计算密集。

当输入为GPU上的张量时,模型将在同一GPU上计算结果。

In [12]:

python 复制代码
net(X)  # 显示在device='cuda:0'上

Out[12]:

复制代码
tensor([[-0.8239],
        [-0.8239]], device='cuda:0', grad_fn=<AddmmBackward0>)

让我们(确认模型参数存储在同一个GPU上。)

In [13]:

python 复制代码
net[0].weight.data.device # 再确认一下weight模型参数

Out[13]:

复制代码
device(type='cuda', index=0)

总之,只要所有的数据和参数都在同一个设备上, 我们就可以有效地学习模型。 在下面的章节中,我们将看到几个这样的例子。

小结

  • 我们可以指定用于存储和计算的设备,例如CPU或GPU。默认情况下,数据在主内存中创建,然后使用CPU进行计算。
  • 深度学习框架要求计算的所有输入数据都在同一设备上,无论是CPU还是GPU。
  • 不经意地移动数据可能会显著降低性能。一个典型的错误如下:计算GPU上每个小批量的损失,并在命令行中将其报告给用户(或将其记录在NumPy ndarray中)时,将触发全局解释器锁,从而使所有GPU阻塞。最好是为GPU内部的日志分配内存,并且只移动较大的日志。
相关推荐
DolphinScheduler社区几秒前
白鲸开源与亚马逊云科技携手推动AI-Ready数据架构创新
人工智能·科技·开源·aws·白鲸开源·whalestudio
无敌小茶8 分钟前
Linux学习笔记之环境变量
linux·笔记
__lost21 分钟前
Python图像变清晰与锐化,调整对比度,高斯滤波除躁,卷积锐化,中值滤波钝化,神经网络变清晰
python·opencv·计算机视觉
海绵波波10726 分钟前
玉米产量遥感估产系统的开发实践(持续迭代与更新)
python·flask
欣然~29 分钟前
借助 OpenCV 和 PyTorch 库,利用卷积神经网络提取图像边缘特征
人工智能·计算机视觉
谦行38 分钟前
工欲善其事,必先利其器—— PyTorch 深度学习基础操作
pytorch·深度学习·ai编程
帅云毅1 小时前
Web3.0的认知补充(去中心化)
笔记·学习·web3·去中心化·区块链
豆豆1 小时前
day32 学习笔记
图像处理·笔记·opencv·学习·计算机视觉
逢生博客1 小时前
使用 Python 项目管理工具 uv 快速创建 MCP 服务(Cherry Studio、Trae 添加 MCP 服务)
python·sqlite·uv·deepseek·trae·cherry studio·mcp服务
xwz小王子1 小时前
Nature Communications 面向形状可编程磁性软材料的数据驱动设计方法—基于随机设计探索与神经网络的协同优化框架
深度学习