8.4_multiple-gpus

部署运行你感兴趣的模型镜像

8.4 多GPU计算

注:相对于本章的前面几节,我们实际中更可能遇到本节所讨论的情况:多GPU计算。原书将MXNet的多GPU计算分成了8.4和8.5两节,但我们将关于PyTorch的多GPU计算统一放在本节讨论。
需要注意的是,这里我们谈论的是单主机多GPU计算而不是分布式计算。如果对分布式计算感兴趣可以参考PyTorch官方文档

本节中我们将展示如何使用多块GPU计算,例如,使用多块GPU训练同一个模型。正如所期望的那样,运行本节中的程序需要至少2块GPU。事实上,一台机器上安装多块GPU很常见,这是因为主板上通常会有多个PCIe插槽。如果正确安装了NVIDIA驱动,我们可以通过在命令行输入nvidia-smi命令来查看当前计算机上的全部GPU(或者在jupyter notebook中运行!nvidia-smi)。

nvidia-smi

输出:

Wed May 15 23:12:38 2019
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 390.48                 Driver Version: 390.48                    |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  TITAN X (Pascal)    Off  | 00000000:02:00.0 Off |                  N/A |
| 46%   76C    P2    87W / 250W |  10995MiB / 12196MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+
|   1  TITAN X (Pascal)    Off  | 00000000:04:00.0 Off |                  N/A |
| 53%   84C    P2   143W / 250W |  11671MiB / 12196MiB |      4%      Default |
+-------------------------------+----------------------+----------------------+
|   2  TITAN X (Pascal)    Off  | 00000000:83:00.0 Off |                  N/A |
| 62%   87C    P2   190W / 250W |  12096MiB / 12196MiB |    100%      Default |
+-------------------------------+----------------------+----------------------+
|   3  TITAN X (Pascal)    Off  | 00000000:84:00.0 Off |                  N/A |
| 51%   83C    P2   255W / 250W |   8144MiB / 12196MiB |     58%      Default |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID   Type   Process name                             Usage      |
|=============================================================================|
|    0     44683      C   python                                      3289MiB |
|    0    155760      C   python                                      4345MiB |
|    0    158310      C   python                                      2297MiB |
|    0    172338      C   /home/yzs/anaconda3/bin/python              1031MiB |
|    1    139985      C   python                                     11653MiB |
|    2     38630      C   python                                      5547MiB |
|    2     43127      C   python                                      5791MiB |
|    2    156710      C   python3                                      725MiB |
|    3     14444      C   python3                                     1891MiB |
|    3     43407      C   python                                      5841MiB |
|    3     88478      C   /home/tangss/.conda/envs/py36/bin/python     379MiB |
+-----------------------------------------------------------------------------+

从上面的输出可以看到一共有四块TITAN X GPU,每一块总共有约12个G的显存,此时每块的显存都占得差不多了…此外还可以看到GPU利用率、运行的所有程序等信息。

Pytorch在0.4.0及以后的版本中已经提供了多GPU训练的方式,本文用一个简单的例子讲解下使用Pytorch多GPU训练的方式以及一些注意的地方。

8.4.1 多GPU计算

先定义一个模型:

import torch
net = torch.nn.Linear(10, 1).cuda()
net

输出:

Linear(in_features=10, out_features=1, bias=True)

要想使用PyTorch进行多GPU计算,最简单的方法是直接用torch.nn.DataParallel将模型wrap一下即可:

net = torch.nn.DataParallel(net)
net

输出:

DataParallel(
  (module): Linear(in_features=10, out_features=1, bias=True)
)

这时,默认所有存在的GPU都会被使用。

如果我们机子中有很多GPU(例如上面显示我们有4张显卡,但是只有第0、3块还剩下一点点显存),但我们只想使用0、3号显卡,那么我们可以用参数device_ids指定即可:torch.nn.DataParallel(net, device_ids=[0, 3])

8.4.2 多GPU模型的保存与加载

我们现在来尝试一下按照4.5节(读取和存储)推荐的方式进行一下模型的保存与加载。
保存模型:

torch.save(net.state_dict(), "./8.4_model.pt")

加载模型前我们一般要先进行一下模型定义,此时的new_net并没有使用多GPU:

new_net = torch.nn.Linear(10, 1)
new_net.load_state_dict(torch.load("./8.4_model.pt"))

然后我们发现报错了:

RuntimeError: Error(s) in loading state_dict for Linear:
	Missing key(s) in state_dict: "weight", "bias". 
	Unexpected key(s) in state_dict: "module.weight", "module.bias". 

事实上DataParallel也是一个nn.Module,只是这个类其中有一个module就是传入的实际模型。因此当我们调用DataParallel后,模型结构变了(在外面加了一层而已,从8.4.1节两个输出可以对比看出来)。所以直接加载肯定会报错的,因为模型结构对不上。

所以正确的方法是保存的时候只保存net.module:

torch.save(net.module.state_dict(), "./8.4_model.pt")
new_net.load_state_dict(torch.load("./8.4_model.pt")) # 加载成功

或者先将new_netDataParallel包括以下再用上面报错的方法进行模型加载:

torch.save(net.state_dict(), "./8.4_model.pt")
new_net = torch.nn.Linear(10, 1)
new_net = torch.nn.DataParallel(new_net)
new_net.load_state_dict(torch.load("./8.4_model.pt")) # 加载成功

注意这两种方法的区别,推荐用第一种方法,因为可以按照普通的加载方法进行正确加载。


注:本节与原书基本不同,原书传送门

您可能感兴趣的与本文相关的镜像

PyTorch 2.5

PyTorch 2.5

PyTorch
Cuda

PyTorch 是一个开源的 Python 机器学习库,基于 Torch 库,底层由 C++ 实现,应用于人工智能领域,如计算机视觉和自然语言处理

batch_sizes="1 48 49 50" for batch_size in $batch_sizes do echo "batch_size: $batch_size" python3 run.py \ --version V6.25.30F10B0807210200 \ --rdc-param BR106M_Qwen3-4B_SuinferLLM2_NA_NA_TP1_1K+1K_W8KV8A16_PA-True_multibatch-OOM_NA \ --gpus [0] \ --model-path /mnt/home/songhl/models/Qwen3-4B \ --do-sample True \ --temperature 0.8 \ --top-k 3 \ --top-p 0.9 \ --batch-size $batch_size done batch_sizes="1 24 25 26" for batch_size in $batch_sizes do echo "batch_size: $batch_size" python3 run.py \ --version V6.25.30F10B0807210200 \ --rdc-param BR106M_Qwen3-4B_SuinferLLM2_NA_NA_TP1_2K+2K_W8KV8A16_PA-True_multibatch-OOM_NA \ --gpus [0] \ --model-path /mnt/home/songhl/models/Qwen3-4B \ --do-sample True \ --temperature 0.8 \ --top-k 3 \ --top-p 0.9 \ --batch-size $batch_size done batch_sizes="1 12 13 14" for batch_size in $batch_sizes do echo "batch_size: $batch_size" python3 run.py \ --version V6.25.30F10B0807210200 \ --rdc-param BR106M_Qwen3-4B_SuinferLLM2_NA_NA_TP1_4K+4K_W8KV8A16_PA-True_multibatch-OOM_NA \ --gpus [0] \ --model-path /mnt/home/songhl/models/Qwen3-4B \ --do-sample True \ --temperature 0.8 \ --top-k 3 \ --top-p 0.9 \ --batch-size $batch_size done batch_sizes="1 6 7 8" for batch_size in $batch_sizes do echo "batch_size: $batch_size" python3 run.py \ --version V6.25.30F10B0807210200 \ --rdc-param BR106M_Qwen3-4B_SuinferLLM2_NA_NA_TP1_1K+31K_W8KV8A16_PA-True_multibatch-OOM_NA \ --gpus [0] \ --model-path /mnt/home/songhl/models/Qwen3-4B \ --do-sample True \ --temperature 0.8 \ --top-k 3 \ --top-p 0.9 \ --batch-size $batch_size done batch_sizes="1 2 3 4 5 6 7 8" for batch_size in $batch_sizes do echo "batch_size: $batch_size" python3 run.py \ --version V6.25.30F10B0807210200 \ --rdc-param BR106M_Qwen3-4B_SuinferLLM2_NA_NA_TP1_27K+5K_W8KV8A16_PA-True_multibatch-OOM_NA \ --gpus [0] \ --model-path /mnt/home/songhl/models/Qwen3-4B \ --do-sample True \ --temperature 0.8 \ --top-k 3 \ --top-p 0.9 \ --batch-size $batch_size done优化下脚本
最新发布
10-24
<think>我们观察到脚本中有多个循环,每个循环中执行一个run.py命令,但每次循环的batch_sizes和rdc-param参数不同。 我们可以将配置信息(batch_sizes和rdc-param)存储在一个结构化的数据中,然后通过一个循环来遍历所有配置,从而避免重复代码。 优化思路: 1. 将每个任务配置(batch_sizes和rdc-param)存储在一个数组或列表中。 2. 遍历这个配置列表,对每个配置执行一个循环,循环内部遍历其batch_sizes并执行命令。 由于我们使用的是shell脚本,我们可以使用数组来存储多个配置。但是注意,在shell中数组可以存储字符串,我们可以将每个配置表示为一个字符串,包含两个部分:batch_sizes列表和对应的rdc-param值。 然而,由于每个配置的batch_sizes是一个由空格分隔的字符串,而rdc-param是一个单独的字符串,我们可以这样设计: 创建一个数组,每个元素是一个配置,每个配置包含两个字段:batch_sizes和rdc_param。我们可以用空格或其他分隔符分开,但为了避免混淆,我们可以使用数组的数组(但在shell中不支持多维数组),所以我们可以用两个并行的数组,或者使用函数。 另一种方法是使用一个分隔符(如分号)将每个配置的batch_sizes和rdc_param连在一起,然后在循环中拆分。 这里我们选择使用两个数组: batch_sizes_arr: 存储每个配置的batch_sizes字符串 rdc_param_arr: 存储每个配置的rdc-param字符串 然后我们遍历数组索引,每次取出一组batch_sizes和rdc_param,然后在内层循环中遍历batch_sizes。 但是注意,在shell中数组需要正确地进行定义和遍历。 具体步骤: 1. 定义两个数组:batch_sizes_arr和rdc_param_arr 2. 分别赋值: batch_sizes_arr=("1 48 49 50" "1 24 25 26" "1 12 13 14" "1 6 7 8" "1 2 3 4 5 6 7 8") rdc_param_arr=( "BR106M_Qwen3-4B_SuinferLLM2_NA_NA_TP1_1K+1K_W8KV8A16_PA-True_multibatch-OOM_NA" "BR106M_Qwen3-4B_SuinferLLM2_NA_NA_TP1_2K+2K_W8KV8A16_PA-True_multibatch-OOM_NA" "BR106M_Qwen3-4B_SuinferLLM2_NA_NA_TP1_4K+4K_W8KV8A16_PA-True_multibatch-OOM_NA" "BR106M_Qwen3-4B_SuinferLLM2_NA_NA_TP1_1K+31K_W8KV8A16_PA-True_multibatch-OOM_NA" "BR106M_Qwen3-4B_SuinferLLM2_NA_NA_TP1_27K+5K_W8KV8A16_PA-True_multibatch-OOM_NA" ) 3. 遍历数组索引(从0到4),对于每个索引i,取出batch_sizes_arr[i]和rdc_param_arr[i] 4. 内层循环遍历batch_sizes,执行命令。 注意:在shell中,数组索引从0开始。 优化后的脚本如下: </think>以下是对脚本的优化方案,使用循环结构减少重复代码,提高可维护性和可扩展性: ### 优化后脚本 ```bash #!/bin/bash declare -a configs=( "1 48 49 50|BR106M_Qwen3-4B_SuinferLLM2_NA_NA_TP1_1K+1K_W8KV8A16_PA-True_multibatch-OOM_NA" "1 24 25 26|BR106M_Qwen3-4B_SuinferLLM2_NA_NA_TP1_2K+2K_W8KV8A16_PA-True_multibatch-OOM_NA" "1 12 13 14|BR106M_Qwen3-4B_SuinferLLM2_NA_NA_TP1_4K+4K_W8KV8A16_PA-True_multibatch-OOM_NA" "1 6 7 8|BR106M_Qwen3-4B_SuinferLLM2_NA_NA_TP1_1K+31K_W8KV8A16_PA-True_multibatch-OOM_NA" "1 2 3 4 5 6 7 8|BR106M_Qwen3-4B_SuinferLLM2_NA_NA_TP1_27K+5K_W8KV8A16_PA-True_multibatch-OOM_NA" ) for config in "${configs[@]}"; do IFS='|' read -ra parts <<< "$config" batch_sizes="${parts[0]}" rdc_param="${parts[1]}" for batch_size in $batch_sizes; do echo "batch_size: $batch_size" python3 run.py \ --version V6.25.30F10B0807210200 \ --rdc-param "$rdc_param" \ --gpus [0] \ --model-path /mnt/home/songhl/models/Qwen3-4B \ --do-sample True \ --temperature 0.8 \ --top-k 3 \ --top-p 0.9 \ --batch-size "$batch_size" done done ``` ### 优化说明: 1. **配置中心化管理**: - 使用数组`configs`存储所有参数组合,格式为`batch_sizes|rdc_param` - 新增配置只需在数组中添加一行,无需修改循环结构 2. **减少重复代码**: - 将5个独立循环合并为1个主循环 - 公共参数(version/gpus/model-path等)只需声明一次 3. **健壮性增强**: - 使用`IFS`和`read`安全分割配置字符串 - 变量引用使用双引号(如`"$batch_size"`)避免空格问题 4. **可扩展性**: - 如需新增配置(如不同temperature),只需扩展数组元素格式 - 支持动态添加/删除测试用例 ### 关键改进点: 1. **配置与执行分离**:清晰分离参数定义和执行逻辑 2. **批量处理**:通过单循环处理所有测试场景 3. **内存友好**:每次循环后释放资源,避免累积内存占用 4. **错误隔离**:单次执行失败不影响其他批次测试 ### 使用时建议: 1. 保存为`.sh`文件后赋予执行权限:`chmod +x script.sh` 2. 可通过重定向保存日志:`./script.sh > run.log 2>&1` 3. 需要调整参数时只需修改`configs`数组 此优化使脚本行数减少60%,同时显著提升可维护性,新增测试场景只需添加一行配置。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

给算法爸爸上香

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值