RuntimeError: Expected to have finished reduction in the prior iteration before starting a new one.

pytorch多GPU(单机多卡)训练采坑:RuntimeError: Expected to have finished reduction in the prior iteration before starting a new one.

问题描述

由于我的模型参数较多,且训练时发现大的batchsize效果更好,这就导致单个GPU的显存可能不够用,所以需要使用多GPU训练。

我使用的是hugging face开源的accelerate库中的accelerate.Accelerator类来进行的多GPU训练。

训练中出现如下报错:

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 passing the keyword argument find_unused_parameters=True to torch.nn.parallel.DistributedDataParallel, and by
making sure all forward function outputs participate in calculating loss.
If you already have done the above, 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).
Parameter indices which did not receive grad for rank 1: 40 41 42 43 44 45 46 47 48 49 50 51
In addition, you can set the environment variable TORCH_DISTRIBUTED_DEBUG to either INFO or DETAIL to print out information about which particular parameters did not receive gradient on this rank as part of this error

翻译成中文是:

RuntimeError: 在开始新的迭代之前,预期上一个迭代已完成归约。此错误表明您的模块有未用于产生损失的参数。您可以通过向 torch.nn.parallel.DistributedDataParallel 传递关键字参数 find_unused_parameters=True 来启用未使用的参数检测,并确保所有的 forward 函数输出都参与计算损失。
如果您已经这样做了,那么分布式数据并行模块无法定位您模块的 forward 函数返回值中的输出张量。在报告此问题时,请包含损失函数和您模块的 forward 函数的返回值结构(例如,列表,字典,迭代器)。
未为排名1接收梯度的参数索引为: 40 41 42 43 44 45 46 47 48 49 50 51
此外,您可以将环境变量 TORCH_DISTRIBUTED_DEBUG 设置为 INFO 或 DETAIL,以打印有关此排名上哪些特定参数未收到梯度的信息作为此错误的一部分。

报错完整的截图如下
在这里插入图片描述

这似乎再说我定义了一些模块没有使用,或者输出了一些参数但是没有参与loss计算,导致这个问题

问题探究

forward输出多个参数但不参与loss计算并不会导致报错

一开始我看到这句话making sure all forward function outputs participate in calculating loss
以为是我的模型输出的值没有参与loss计算,导致报错,但是我后来经过研究发现:
模型的forward输出多个值,但是没有参与loss计算并不会导致报错!!
例如在有多个GPU的设备上运行下列代码:

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset
from accelerate import Accelerator
from accelerate.utils import write_basic_config
from collections import namedtuple


def main():

    # write_basic_config()  # Write a config file

    # 1. 初始化 Accelerator
    accelerator = Accelerator()

    # 2. 定义一个简单的模型和优化器
    class SimpleModel(nn.Module):

        def __init__(self):
            super(SimpleModel, self).__init__()
            self.fc1 = nn.Linear(10, 10)
            self.fc2 = nn.Linear(10, 2)

        def forward(self, x):
            fc1 = self.fc1(x)
            fc2 = self.fc2(fc1)
            fc3 = self.fc2(fc1) * 2 + 1
            return_namedtuple = namedtuple('return_namedtuple', ['fc1', 'fc2', 'fc3'])
            return return_namedtuple(fc1=fc1, fc2=fc2, fc3=fc3)

    model = SimpleModel()
    optimizer = optim.Adam(model.parameters(), lr=0.001)

    # 3. 定义虚拟数据
    x = torch.randn(10000, 10)
    y = (torch.randn(10000) > 0.5).long()
    dataset = TensorDataset(x, y)
    loader = DataLoader(dataset, batch_size=512, shuffle=True)

    # 4. 使用 accelerator.prepare 函数准备模型和优化器
    model, optimizer, loader = accelerator.prepare(model, optimizer, loader)

    # 5. 训练循环
    for epoch in range(10000):
        for batch in loader:
            inputs, targets = batch

            loss = 0

            outputnamedtuple = model(inputs)
            
            loss1 = nn.CrossEntropyLoss()(outputnamedtuple.fc1, targets)
            loss2 = nn.CrossEntropyLoss()(outputnamedtuple.fc2, targets)

            loss = loss1 + loss2

            optimizer.zero_grad()

            accelerator.backward(loss)

            optimizer.step()
            print(f"Loss: {loss.item()}")

    print("Training completed!")


if __name__ == "__main__":
    main()

注意看,我这里计算并输出一个值名称叫fc3,但是后续计算loss的时候只使用了fc1和fc2,这样设置代码是可以正常运行不会报错的。

在这里插入图片描述
在这里插入图片描述

在模型的__init__方法中定义了某些层但是没有使用才是发生报错的关键

我们现在将如上述代码的定义模型部分修改一下,就会发生报错

class SimpleModel(nn.Module):

    def __init__(self):
        super(SimpleModel, self).__init__()
        self.fc1 = nn.Linear(10, 10)
        self.fc2 = nn.Linear(10, 2)
        self.fc3 = nn.Linear(10, 2)

    def forward(self, x):
        fc1 = self.fc1(x)
        fc2 = self.fc2(fc1)
        fc3 = self.fc3(fc1)
        return_namedtuple = namedtuple('return_namedtuple', ['fc1', 'fc2', 'fc3'])
        return return_namedtuple(fc1=fc1, fc2=fc2, fc3=fc3)

这一代码与上面的区别就在于定义了一个self.fc3 = nn.Linear(10, 2)这个层,但是这个层所计算出来的fc3结果没有参与loss计算,就会导致报错。

如果定义了某个层,但是也没有输出计算结果,同样会导致报错,比如将代码改成如下所示,也会报错

class SimpleModel(nn.Module):

    def __init__(self):
        super(SimpleModel, self).__init__()
        self.fc1 = nn.Linear(10, 10)
        self.fc2 = nn.Linear(10, 2)
        self.fc3 = nn.Linear(10, 2)

    def forward(self, x):
        fc1 = self.fc1(x)
        fc2 = self.fc2(fc1)
        return_namedtuple = namedtuple('return_namedtuple', ['fc1', 'fc2'])
        return return_namedtuple(fc1=fc1, fc2=fc2)

注意这里是说如果用self.xx的语句定义了层会报错,如果去掉self则不会报错,如下所示。

class SimpleModel(nn.Module):

    def __init__(self):
        super(SimpleModel, self).__init__()
        self.fc1 = nn.Linear(10, 10)
        self.fc2 = nn.Linear(10, 2)
        fc3 = nn.Linear(10, 2)

    def forward(self, x):
        fc1 = self.fc1(x)
        fc2 = self.fc2(fc1)
        return_namedtuple = namedtuple('return_namedtuple', ['fc1', 'fc2'])
        return return_namedtuple(fc1=fc1, fc2=fc2)

总结:在使用nn.Module的__init__方法时,如果使用self.xx这样的语句定义了层,但是这个层的计算结果后续没有用老计算loss,或者这个层没有使用,都会导致标题所述的报错

解决方案

上面只是一个用于排查错误的例子,那么我的模型哪里出现了这样的问题呢?我通过寻找,最终找到了问题所在,我发现是我使用的transformer模型中的问题,具体是nn.TransformerEncoderLayernn.TransformerEncoder的使用出现了问题。具体如下:

在这里插入图片描述

在使用transformer模型中的encoder的时候,通常需要使用nn.TransformerEncoderLayer先定义一层encoder,然后使用nn.TransformerEncoder将之前定义的一层encoder串联起来,形成一个多层的encoder。

那么问题来了,后面在进行forward的时候,我们是使用self.transformer_encoder来进行的,并没有使用self.encoder_layer,这就导致出现了先前分析所说的“使用self.xx语句定义了层但是并没有使用的情况”。解决方案就是去掉encoder_layer前面的self即可,如下图所示

在这里插入图片描述

  • 9
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
### 回答1: 这个错误提示意味着你的模块有一些未被用于计算损失的参数。你可以通过两种方式解决这个问题:第一种是在`torch.nn.parallel.distributeddataparallel`函数中传递`find_unused_parameters=true`关键字参数,让程序检测未使用的参数;第二种是确保所有的`forward`函数输出都参与计算损失。如果你已经完成了以上两个步骤,但仍然出现这个错误,则说明分布式数据并行模块无法找到模块`forward`函数的输出张量的位置。在报告这个问题时,请包括损失函数和模块`forward`函数的返回值结构(如列表、字典、可迭代对象)。 ### 回答2: 这个错误提示表明你的模块中存在一些参数没有被用于产生loss。如果你使用了`torch.nn.parallel.distributeddataparallel`,你可以通过传递关键字参数`find_unused_parameters=true` 来启用未使用参数检测功能。另外需要确保所有`forward`函数的输出都与loss有关。如果你已经完成了上述两个步骤,那么可能是分布式数据并行模块无法在返回值中找到`forward`函数的输出张量。在报告此问题时,请包括`loss`函数和`forward`函数返回值的结构(例如列表,字典,可迭代对象等)。 ### 回答3: 运行时错误:在先前的迭代中完成了减少操作,然后才开始新操作。此错误表示您的模块具有未用于生成损失的参数。您可以通过以下方式启用未使用的参数检测:(1)向torch.nn.parallel.distributeddataparallel传递关键字参数“find_unused_parameters=true”;(2)确保所有“forward”函数输出参与计算损失。如果您已经完成了上述两个步骤,则分布式数据并行模块无法在模块的“forward”函数的返回值中定位输出张量。请在报告此问题时包括损失函数和模块的“forward”函数返回值的结构(例如,列表,字典,可迭代对象)。 此错误通常在使用分布式数据并行训练模型时出现。在分布式数据并行训练中,模型参数通过多个进程分配,并在每个进程中独立更新。在进行此操作时,所有进程必须在同步点达成一致,以确保所有参数在同步点之前更新。否则,将会出现上述错误。 要解决此错误,您可以尝试以下步骤: 1.检查模型的参数是否都参与了生成损失,如果有未使用的参数,可以通过将关键字参数“find_unused_parameters=true”传递给torch.nn.parallel.distributeddataparallel以启用未使用的参数检测。 2.确保所有“forward”函数的输出都与计算损失相关联。 3.如果您已经完成了上述两个步骤,则分布式数据并行模块无法定位输出张量。请检查模型的损失函数和“forward”函数的返回值结构,以确保输出张量位于返回值中,随后使用正确的同步点。如果无法解决问题,可以在报告此问题时将模型的相应代码和数据提供给支持团队,以便他们更好地理解和解决问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值