深度学习实验DDP方式多卡训练

在深度学习的实验中,我们经常需要用到多卡训练,但是具体需要怎么操作呢?在网上搜寻方案时,不是这里报错就是那里报错,所以有了本篇博客,介绍如何使用多卡运行,以及其中的注意事项。

我总结了以下四步(说明:此步骤针对我的实验是有效的,具体实验要具体设置),最后我会贴上自己的详细代码以供参考

1.预先设置多卡信息

首先要导入一些包,然后在设置一个使用if name == 'main':启动程序的方法,并在其中设置以下信息。

注意:这里backend如果是ubuntu系统需要设置为nccl,world-size的默认数量是自己的显卡数量

import os
os.environ["CUDA_VISIBLE_DEVICES"]="0,1"
from torch.backends import cudnn
import argparse
cudnn.benchmark = True


if __name__ == '__main__':
    #设置多线程优化
    parser = argparse.ArgumentParser()
    parser.add_argument("--local_rank", type=int, default=-1)
    parser.add_argument('--world-size', default=2, type=int, help='number of distributed processes')
    args_init = parser.parse_args()

    torch.cuda.set_device(args_init.local_rank)
    device = torch.device('cuda', args_init.local_rank)
    torch.distributed.init_process_group(backend='gloo')
	#torch.distributed.init_process_group(backend='nccl')
    
    # 固定随机种子
    seed = 42
    random.seed(seed)
    np.random.seed(seed)
    torch.manual_seed(seed)
    torch.cuda.manual_seed_all(seed)

2.分发数据

这里要注意的是batch_size是可改的,然后这里最好不要出现,其中使用默认的num_workers=0和shuffle=False

from torch.utils.data import DataLoader
from torch.utils.data.distributed import DistributedSampler

#这下面两行代码是放在torch.cuda.manual_seed_all(seed)之后运行的
train_sampler = DistributedSampler(train_set)
train_loader = DataLoader(train_set, batch_size=16, sampler=train_sampler)  

3.设置网络并行

这个find_unused_parameters=True最好设置上,不然会报错,具体的报错原理我也还在了解

#模型数据分发方法,接在 train_loader  = DataLoader(train_set, batch_size=16, sampler=train_sampler)  后
net = nn.parallel.DistributedDataParallel(net, device_ids=[args_init.local_rank], output_device=args_init.local_rank, find_unused_parameters=True)

4.模型训练

模型训练时注意train_loader.sampler.set_epoch(epoch)不可少,其他就可以先默认设置,至于说的损失要计算平均,我这里就不展开写了。

for epoch in range(0,100):
        net.train()
        train_loader.sampler.set_epoch(epoch)
        #获取数据
        for i,(inputs,labels) in enumerate(train_loader):
            inputs= inputs.to(device)
            labels = labels.to(device)
            
            # optimizer.zero_grad()
            output = net(inputs)
		   #loss的计算
		   loss=cal_loss(output,labels)

            optimizer.zero_grad()
            loss.backward()
            optimizer.step()

总结

这是我的整个训练的代码,去掉了部分数据处理和优化器初始化的代码,其他提供给大家参考

import os
import torch
from torch import nn
from torch.backends import cudnn
from torch.utils.data import DataLoader
from torch.utils.data.distributed import DistributedSampler

os.environ["CUDA_VISIBLE_DEVICES"]="0,1"
import argparse
cudnn.benchmark = True

def init_dataloader():
    .....
    
    #数据并行1
    train_sampler = DistributedSampler(train_set)
    train_loader = DataLoader(train_set, batch_size=16, sampler=train_sampler) 

    return train_loader,total_epoch,xxx,xxx,xxx

def init_optim(net):
    
    ...

    return optimizer


def train(net, optimizer):
    for epoch in range(args['last_epoch'] + 1, args['last_epoch'] + 1 + args['epoch_num']):
        net.train()
        train_loader.sampler.set_epoch(epoch)

        #获取数据
        for i,(inputs,labels) in enumerate(train_loader):
            inputs= inputs.to(device)
            labels = labels.to(device)
            
            predict= net(inputs)
            loss =cal_loss(predict, labels)

            optimizer.zero_grad()
            loss.backward()
            optimizer.step()

if __name__ == '__main__':
    #设置多线程优化
    parser = argparse.ArgumentParser()
    parser.add_argument("--local_rank", type=int, default=-1)
    parser.add_argument('--world-size', default=2, type=int, help='number of distributed processes')
    args_init = parser.parse_args()

    torch.cuda.set_device(args_init.local_rank)
    device = torch.device('cuda', args_init.local_rank)
    torch.distributed.init_process_group(backend='gloo')

    # 固定随机种子
    seed = 42
    random.seed(seed)
    np.random.seed(seed)
    torch.manual_seed(seed)
    torch.cuda.manual_seed_all(seed)

    from XXNet import XXNet
    
    ...
    
    #初始化数据
    train_loader,total_epoch,xxx,xxx,xxx=init_dataloader()

    #模型创建
    net = XXNet(pvt_name)
    #修改模型状态
    net = net.cuda().train()

    #初始化优化策略
    optimizer=init_optim(net)

    #模型数据分发方法
    net = nn.parallel.DistributedDataParallel(net, device_ids=[args_init.local_rank], output_device=args_init.local_rank, find_unused_parameters=True)

    #训练网络
    train(net, optimizer)

报错处理

以下是我具体实现过程中遇到的报错情况

1.出现很长的一段信息

开始训练后一个特别长的报错

报错内容是:RuntimeError: Expected to have finished reduction in the prior iteration before starting a new one. This error indicates that your module has parameters that were not used in producing loss. You can enable unused parameter detection by (1) passing the keyword argument find_unused_parameters=True to torch.nn.parallel.DistributedDataParallel; (2) making sure all forward function outputs participate in calculating loss. If you already have done the above two steps, then the distributed data parallel module wasn’t able to locate the output tensors in the return value of your module’s forward function. Please include the loss function and the structure of the return value of forward of your module when reporting this issue (e.g. list, dict, iterable).

参考这位大佬(撑住我爱代码-CSDN博客)的解决方法:

翻译:运行时错误:期望在开始新迭代之前已完成前一次迭代的reduction(但没做到这一点)。此错误表明您的模块具有未参与产生loss的参数。您可以通过 (1) 将关键字参数 find_unused_parameters=True 传递给 torch.nn.parallel.DistributedDataParallel 来启用未使用的参数检测; (2) 确保所有 forward 函数的所有输出都参与计算损失。如果您已经完成了上述两个步骤,那么分布式数据并行模块无法在模块的 forward 函数的返回值中定位输出张量。报告此问题时,请包括损失函数和模块 forward 返回值的结构(例如 list、dict、iterable)。

经过检查,我的代码并没有某部分loss没有参与梯度反向传播的情况,根据提示,在torch.nn.parallel.DistributedDataParallel中添加参数“find_unused_parameters=True”,经过调整后,代码得以成功运行。

参考文章

感谢以下作者的知识分享

超详细逐步骤演示Pytorch深度学习多GPU并行训练全过程_pytorch多gpu训练-CSDN博客

【多GPU炼丹-绝对有用】PyTorch多GPU并行训练:深度解析与实战代码指南_torch上使用多gpu并行训练模型-CSDN博客

pytorch 分布式多卡训练DistributedDataParallel 踩坑记_加载多卡训练的预训练模型default process group has not been ini-CSDN博客

pytorch单机多卡训练 logger日志记录和wandb可视化_wandb多卡-CSDN博客

pytorch单机多卡的正确打开方式 以及可能会遇到的问题和相应的解决方法_find unused parameters会影响速度吗-CSDN博客

【创作不易,望点赞收藏,若有疑问,请留言,谢谢】

posted @ 2024-07-11 14:20  东血  阅读(6)  评论(0编辑  收藏  举报

载入天数...载入时分秒...