使用pytorch DDP(DistributedDataParallel,分布式数据并行)可以进行多卡训练,涉及到模型保存与加载问题时,一般会涉及到以下两种需求:
-
将多卡训练的模型保存到磁盘。
-
从磁盘加载模型,在多卡上继续训练。
如何无bug且高效的解决以上需求
?(假设训练设备为“单机4卡”)
对于需求1,由于DDP在多卡中维护了相同的模型参数(通过在4张GPU上确保模型初始化以及广播相同的梯度来保证4张卡中的模型参数是完全相同的),因此只需要在其中一张卡保存模型即可:
def save_checkpoint(local_rank, ddp_model, path):
#只在GPU 0 上保存模型
if local_rank== 0:
state = {
'model': ddp_model.module.state_dict(),
'optimizer': optimizer.state_dict(),
torch.save(state, path)
对于需求2,一般会使用torch.load()方法从磁盘加载文件:
def load_checkpoint(path):
checkpoint = torch.load(path)
model = Net()
model.load_state_dict(checkpoint['model'])
model = DDP(model, device_ids=[gpu])
return model
但是此时往往会遇到多进程在GPU0上占用过多显存的问题:
使用nvidia-smi命令:
上图中,在所有使用GPU0的进程中,除了PID为62250的进程外,还存在其他三个进程,而这三个进程还分别使用GPU1\2\3。这三个额外进程在GPU0占用了725MB*3的显存空间,这可能会导致GPU0在训练时出现爆显存的问题。
在DDP中,会为每张卡单独创建一个进程:
上图的情况是正常的,每个进程只会使用与其对应的一张显卡。
该问题出现的原因是:torch.load()的不正确使用。
在pytorch对
torch.
load()
方法的
官方文档
中,有以下说明:
If
map_location
is missing,
torch.load
will first load the module to CPU and then copy each parameter to where it was saved
意思是,如果map_location参数是空的,则torch.load方法会先把模型加载到CPU,然后把模型参数复制到
保存它的地方
(根据上文,保存模型的位置恰好是GPU 0)。
跑在GPU1上的进程在执行到torch.load方法后,会先加载模型到CPU,之后该进程顺理成章地调用GPU0,把一部分数据复制到GPU0,也就出现了前面图中的问题。
与其说是bug,倒不如说没仔细阅读文档。
两种解决方法方法。
一,将map_location指定为CPU:
def load_checkpoint(path):
#加载到CPU
checkpoint = torch.load(path,map_location='cpu')
model = Net()
model.load_state_dict(checkpoint['model'])
model = DDP(model, device_ids=[gpu])
return model
二,将map_location指定为local_rank对应的GPU:
def load_checkpoint(path):
#加载到CPU
checkpoint = torch.load(path,map_location='cuda:{}'.format(local_rank))
model = Net()
model.load_state_dict(checkpoint['model'])
model = DDP(model, device_ids=[gpu])
return model
本文主要解决
pytorch
在进行
模型
训练
时
出现
GPU
的0卡
占用
显存
比其他卡要多的
问题
。
如下图所示:本机
GPU
卡为TITAN RTX,
显存
24220M,batch_size = 9,用了三张卡。第0卡
显存
占用
24207M,这
时
仅仅是刚开始运行,数据只是少量的移到显卡上,如果数据在多点,0卡的
显存
肯定撑爆。
出现
0卡
显存
更高的原因:网络在反向传播的
时
候,计算loss的梯度默认都在0卡上计算。因此会比其他显卡多用一些
显存
,具体多用多少,主要还要看网络的结构。
因此,为了防止训练由于 out of memory
某次训练
深度学习
模型
时
,
使用
*** roberta-large
模型
作为基础模块,起初
使用
DataParallel *** 的方式,进行单机多卡训练,卡数为2,每张卡
显存
为10G。
训练期间发现,无法
使用
较大的batch_size,batch_size最大为4。同
时
,训练
时
间增加到3个小
时
一个epoch,
时
间开销太大。
观察
GPU
利用
显存
率,0卡
占用
显存
明显比1卡
占用
的要多,这也是*** DataParallel *** 这种模式的弊端。
*** DataParallel *** 数据传输过程包括:
模型
并行
模型
并行主要应用于
模型
相比
显存
来说更大,一块 device 无法
加载
的场景,通过把
模型
切割为几个部分,分别
加载
到不同的 device 上。比如早期的 AlexNet,当
时
限于显卡,
模型
就是分别
加载
在两块显卡上的。
这个是日常会应用的比较多的情况。每一个 device 上会
加载
一份
模型
,然后把数据分发到每个 device 并行进行计算,加快训练速度。
如果要再细分,又可以分
感谢知乎作者 https://www.zhihu.com/question/67209417/answer/866488638
在
使用
DDP
进行单机多卡分布式训练
时
,
出现
了在
加载
预训练权重
时
显存
不够的现象,但是相同的代码单机单卡运行并不会
出现
问题
,后来发现是在多卡训练
时
,额外
出现
了3个进程同
时
占用
了0卡的部分
显存
导致的,而这3个进程正是另外3张卡load进来的数据,默认这些数据被放在了0卡上。解决的方法是把load进来的数据放在cpu(也就是内存)里。
# 原来代码,load进的数据放在
gpu
里
# pre
1. 搭建自己的简单二分类网络,
使用
pytorch
训练和测试;
2. 将
pytorch
训练的pth
模型
转换成ONNX,并编码测试;
3. 含训练和测试数据,含训练ok的pth
模型
和ONNX
模型
,含完整python和C++实现;
4.
使用
方法:首先运行“TrainTestConvertOnnx.py”执行“训练数据读入、
模型
训练、
模型
测试、导出onnx”,再运行“TestOnnx.cpp”测试onnx(需要配置OpenCV);
在公司用多卡训练
模型
,得到权值文件后保存,然后回到实验室,没有多卡的环境,用单卡训练,
加载
模型
时
出错,因为单卡机器上,没有
使用
DataParallel来
加载
模型
,所以会
出现
加载
错误。
DataParallel包装的
模型
在保存
时
,权值参数前面会带有module字符,然而自己在单卡环境下,没有用DataParallel包装的
模型
权值参数不带module。本质上保存的权值文件是一个有序字典。
1.在单卡环境下,用DataParallel包装
模型
。
2.自己重写Load函数,灵活。
from collections import OrderedDict
def myOwnLoa
如果在python内调用
pytorch
有可能
显存
和
GPU
占用
不会被自动释放,此
时
需要加入如下代码
torch.cuda.empty_cache()
我们来看一下官方文档的说明
Releases all unoccupied cached memory currently held by the caching allocator so that those can be used in other
GPU
application and visible in nvidia-smi.
empty_cache() doesn’t increase the amount of
GPU
me
问题
描述
首先说明: 由于我的测试集很大, 因此需要对测试集进行分批次推理.
在写代码的
时
候发现进行训练的
时
候大概
显存
只
占用
了2GB左右, 而且训练过程中
显存
占用
量也基本上是不变的. 而在测试的
时
候, 发现
显存
在每个batch数据推理后逐渐增加, 直至最后导致爆
显存
, 程序fail.
这里放一下我测试的代码:
y, y_ = torch.Tensor(), torch.Tensor()
for batch in tqdm(loader):
x, batch_y =
PyTorch
是一个流行的
深度学习
框架,支持
使用
单个
GPU
或多个
GPU
s进行训练和测试。在
使用
单个
GPU
时
,需要将
模型
和数据
加载
到
GPU
上。
使用
单个
GPU
进行训练和测试的示例代码如下:
示例代码如下:
```python
#将
模型
加载
到
GPU
device = torch.device('cuda:0') #选择第一个
GPU
model.to(device)
#将数据
加载
到
GPU
x = x.to(device)
y = y.to(device)
#在
GPU
上进行前向传播
y_pred = model(x)
#计算损失
loss = criterion(y_pred, y)
#在
GPU
上进行反向传播
loss.backward()
#更新权重
optimizer.step()
使用
多个
GPU
可以加快训练和测试的速度。
PyTorch
提供了两种多
GPU
的方法:DataParallel和DistributedDataParallel(
DDP
)。其中,DataParallel是在单台机器上
使用
多个
GPU
的方法,而
DDP
是在多台机器上
使用
多个
GPU
的方法。
使用
DataParallel
时
,可以将
模型
和数据
加载
到单个
GPU
上,然后
使用
torch.nn.DataParallel将
模型
复制到其他
GPU
上。
使用
DataParallel进行训练和测试的示例代码如下:
示例代码如下:
```python
#将
模型
加载
到
GPU
device = torch.device('cuda:0') #选择第一个
GPU
model.to(device)
#
使用
DataParallel将
模型
复制到其他
GPU
上
if torch.cuda.device_count() > 1:
model = nn.DataParallel(model)
#将数据
加载
到
GPU
x = x.to(device)
y = y.to(device)
#在
GPU
上进行前向传播
y_pred = model(x)
#计算损失
loss = criterion(y_pred, y)
#在
GPU
上进行反向传播
loss.backward()
#更新权重
optimizer.step()
使用
DDP
时
,需要在多台机器上安装
PyTorch
和NCCL库,并按照官方文档中的说明进行配置。
使用
DDP
进行训练和测试的示例代码如下:
```python
#在每个进程中选择一个
GPU
device = torch.device('cuda', rank % torch.cuda.device_count())
#
使用
DistributedDataParallel初始化
模型
model = nn.parallel.DistributedDataParallel(model, device_ids=[device])
#在
GPU
上进行前向传播
y_pred = model(x)
#计算损失
loss = criterion(y_pred, y)
#在
GPU
上进行反向传播
loss.backward()
#更新权重
optimizer.step()
以上就是
PyTorch
使用
单个
GPU
和多个
GPU
进行训练和测试的方法。具体
使用
哪种方法,需要根据硬件和应用场景的要求进行选择。
玩转CIFAR10—Pytorch复现LeNet,AlexNet,VGG,GoogLeNet,MobileNet,ResNet,DenseNet,Vision Transformer等模型(持续更新)
在VS Code中使用jupyter notebook,输出过多时show more显示不正常,出现乱码问题的解决方案