DistributedDataParallel的简单使用/常见问题/一些原理

前言

想变得更快

简单使用

import

from torch.utils.data.distributed import DistributedSampler
import torch.distributed as dist

argments

在参数中添加local_rank

parser.add_argument("--local_rank",type = int,default=-1)

主函数中配置

local_rank = parser.local_rank
    if local_rank != -1:
        dist_backend = 'nccl'
        dist.init_process_group(backend=dist_backend) #  初始化进程组,同时初始化 distributed 包
    device = local_rank if local_rank != -1 else (torch.device('cuda:0') if torch.cuda.is_available() else torch.device('cpu'))
    torch.cuda.set_device(local_rank)  # 配置每个进程的gpu

模型

model.to(device) #  封装之前要把模型移到对应的gpu
    if torch.cuda.device_count() > 1: 
        print("Let's use", torch.cuda.device_count(), "GPUs!")
        model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[local_rank], find_unused_parameters=True)

sampler

# 使用DistributedSampler
train_loader = DataLoader(train_data,
                          shuffle=True if not train_sampler else False,
                          sampler = train_sampler,
                          batch_size=args.batch_size)
                              
train_sampler = DistributedSampler(train_data) # 这里的train_data是一个dataset
# 下面错了,valid不需要分布
valid_sampler = DistributedSampler(valid_data)
valid_loader = DataLoader(valid_data,
                              shuffle=False,
                              sampler=valid_sampler,
                              batch_size=batch_size)

命令行执行

CUDA_VISIBLE_DEVICES=0,1,2,3 python -m torch.distributed.launch --nproc_per_node=4 train_snli.py --checkpoint=False --batch_size=128

torch.distributed.launch 会给模型分配一个args.local_rank的参数,也可以通过torch.distributed.get_rank()获取进程id

常见问题

问题一:显存不释放

我认为是我的代码有问题:
在这里插入图片描述
代码逻辑是这样:在0节点上我去判断accuracy有没有下降,没有的话patience_counter就会+1…
然后下面的if是判断程序是否要提前stop…
由于只在0节点上判断,patience_counter也只可能在0节点上+1,导致其它子进程不会随着0节点的进程一起停止,这就会导致子进程依旧占用着显存的问题。

#解决问题一(1):
其实,上面的代码那样写的本意是想让一个节点去保存模型,因为多个节点好像会出错(?)。那我其实可以让所有节点做判断,然后让其中一个节点保存模型:
在这里插入图片描述
#解决问题一(2):
我试着在程序的最后加上了(还不知道有没有用):

dist.destroy_process_group()

#解决问题一(3):
话说,如果产生了显存不释放的问题怎么办?这时候可能你用nvidia-smi语句都会卡住,解决方法如下:
首先,执行下面语句,会看到很多卡死的进程

fuser -v /dev/nvidia*

在这里插入图片描述

然后试着用kill -9去杀死它们,我因为使用kill -9没有用…所以直接执行下面的语句,杀死了我的所有进程:

killall -u usrname

问题二:DistributedDataParallel’ object has no attribute XXXX

我在我的模型里定义了损失函数:
在这里插入图片描述
然后把模型
在这里插入图片描述
然后报错:
在这里插入图片描述
#解决问题二
model.module.XXXX
在这里插入图片描述

问题三:

self.reducer.prepare_for_backward(list(_find_tensors(output)))
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 its output (the
return value of forward). You can enable unused parameter detection
by passing the keyword argument find_unused_parameters=True to
torch.nn.parallel.DistributedDataParallel. If you already have this
argument set, 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 structure of the return value
of forward of your module when reporting this issue (e.g. list,
dict, iterable). (prepare_for_backward at
/pytorch/torch/csrc/distributed/c10d/reducer.cpp:408)

在这里插入图片描述
emmmmm我跟网上的情况不太一样,我明明已经加了:fine_unused_parameters=True了在这里插入图片描述
我猜测自己的错误原因在于,我的model在定义的时候用到了encoder,这里面的encoder是我用来作embeddings的另一个类。

在这里插入图片描述
然后可能encoder的参数更新出了某些问题,anyway…

#问题三解决:
翻了翻我以前的代码,看看是怎么用分布式进行训练的,于是就在validate和test过程中, 送入模型之间加入了:with torch.no_grad():
在这里插入图片描述
然后莫名就不报错了。。。
但真正的错误原因,一直没有找到。

一些原理

原理一:batch_size

如果我用三块显卡:

CUDA_VISIBLE_DEVICES=0,1,2 python -m torch.distributed.launch --nproc_per_node=3

如果我的batchsize设置为3:

parser.add_argument('--train_bs', help='Batch size', type=int, default=3)

那么每一次dataloader会给3块显卡都sample3个样本,等价于你设置的batch_size=9.

参考

解决GPU显存未释放问题
解决进程杀死,显存仍在的问题
PyTorch 报错:ModuleAttributeError: ‘DataParallel‘ object has no attribute ‘ xxx (已解决)

  • 7
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
PyTorch中的DistributedDataParallel(DDP)是一种用于训练分布式模型的工具。使用DDP,我们可以在多个节点或GPU上并行训练模型,以加快训练速度。 以下是使用DDP进行分布式训练的基本步骤: 1. 初始化进程组: ```python import torch.distributed as dist dist.init_process_group('gloo', init_method='file:///tmp/some_file', rank=rank, world_size=world_size) ``` 这会初始化一个进程组,用于协调不同节点或GPU之间的通信。`gloo`是一种通信后端,支持TCP/IP、IB和RoCE等协议。`init_method`参数指定了进程组的初始化方法,在此示例中,我们使用了文件方式。`rank`参数指定了当前进程的排名,`world_size`指定了进程组的总大小。 2. 定义模型: ```python model = nn.Sequential( nn.Linear(784, 256), nn.ReLU(), nn.Linear(256, 10) ) ``` 我们定义了一个简单的前馈神经网络模型。 3. 将模型包装在DDP中: ```python model = nn.parallel.DistributedDataParallel(model, device_ids=[rank], output_device=rank) ``` 我们使用`nn.parallel.DistributedDataParallel`将模型包装在DDP中。`device_ids`参数指定了当前进程使用的GPU ID,`output_device`参数指定了输出设备的GPU ID。 4. 定义数据加载器: ```python train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True, num_workers=num_workers, pin_memory=True) ``` 我们使用`torch.utils.data.DataLoader`定义数据加载器。`batch_size`参数指定了每个批次的大小,`shuffle`参数指定了是否打乱数据,`num_workers`参数指定了数据加载器的工作进程数量,`pin_memory`参数指定了是否将数据加载到固定的内存位置中。 5. 训练模型: ```python for epoch in range(num_epochs): model.train() for i, (images, labels) in enumerate(train_loader): images = images.to(rank) labels = labels.to(rank) outputs = model(images) loss = criterion(outputs, labels) optimizer.zero_grad() loss.backward() optimizer.step() if i % 100 == 0: print(f"Epoch [{epoch}/{num_epochs}], Step [{i}/{total_steps}], Loss: {loss.item():.4f}") ``` 我们使用常规的PyTorch训练循环训练模型。在每个批次中,我们将数据加载到当前进程使用的GPU中,计算损失,进行反向传播和优化。在训练过程中,DDP会自动将模型参数同步到其他节点或GPU中。 6. 清理: ```python dist.destroy_process_group() ``` 训练完成后,我们需要清理进程组。 完整的示例代码如下: ```python import torch import torch.nn as nn import torch.optim as optim import torch.distributed as dist import torchvision.datasets as datasets import torchvision.transforms as transforms # 初始化进程组 dist.init_process_group('gloo', init_method='file:///tmp/some_file', rank=rank, world_size=world_size) # 定义模型 model = nn.Sequential( nn.Linear(784, 256), nn.ReLU(), nn.Linear(256, 10) ) # 将模型包装在DDP中 model = nn.parallel.DistributedDataParallel(model, device_ids=[rank], output_device=rank) # 定义损失函数和优化器 criterion = nn.CrossEntropyLoss() optimizer = optim.SGD(model.parameters(), lr=lr) # 定义数据加载器 train_dataset = datasets.MNIST(root='data', train=True, transform=transforms.ToTensor(), download=True) train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True, num_workers=num_workers, pin_memory=True) # 训练模型 for epoch in range(num_epochs): model.train() for i, (images, labels) in enumerate(train_loader): images = images.to(rank) labels = labels.to(rank) outputs = model(images) loss = criterion(outputs, labels) optimizer.zero_grad() loss.backward() optimizer.step() if i % 100 == 0: print(f"Epoch [{epoch}/{num_epochs}], Step [{i}/{total_steps}], Loss: {loss.item():.4f}") # 清理进程组 dist.destroy_process_group() ``` 需要注意的是,在使用DDP进行分布式训练时,我们需要确保每个进程的代码都是相同的,否则可能会导致不一致的行为。因此,我们需要使用相同的随机种子、相同的数据加载顺序等等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值