原来单片GPU跑程序没问题,但是用两片GPU跑代码时报了这个错,搜CSDN第一位说是:魔改代码的报应,有被笑道,本想进去看看高见结果还给我整付费文章?不行,必须自己弄明白!
在训练代码中报错:
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 0: 700 701
In addition, you can set the environment variable TORCH_DISTRIBUTED_DEBUG to either INFO of DETAIL to print out information about which particular parameters did not receive gradient on this rank as part of this error
这个报错的意思是说,希望在新的一轮迭代之前完成减少操作,(我理解就是一次forward处理一个batch的数据对模型参数进行梯度下降反向传播更新参数)然后造成这种问题的原因是因为模型中有的参数在产生损失的过程中完全没发挥作用(也就是没经过前向传播,无法通过pytorch的图网络结构计算更新的参数)
官方也给了两个解决办法:
- 一个是在to torch.nn.parallel.DistributedDataParallel中加入find_unused_parameters参数并设置初始值为True,如下这样:
model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[args.gpu], find_unused_parameters=True)
这样确实是能解决问题,但是是治标不治本因为
find_unused_parameters
是 PyTorch 中的一个参数,用于在分布式训练时优化梯度计算。具体来说,它的作用如下:
-
检测未使用的参数:当设置为
True
时,PyTorch 会在每个前向传播过程中检查哪些参数没有被使用。这对于某些模型来说非常有用,特别是当模型的某些部分在特定输入下可能不会被触发时。 -
减少损失计算时的内存开销,提高性能:通过识别并忽略未使用的参数,PyTorch 可以在计算梯度时减少内存开销,从而提高训练效率。在大型模型或复杂网络中,识别未使用的参数可以加速训练过程,尤其是在分布式设置中,因为可以避免不必要的梯度同步。
-
适用于动态计算图:对于需要动态变化的模型架构,使用
find_unused_parameters=True
可以确保所有参数都被正确处理。
但是他会增加额外的计算开销用于验证哪些参数是未使用的不用参加到损失的计算中,所以最好是仅在需要时使用此参数,尤其是在模型中确实存在未使用参数的情况下。
由此可见该方法是一个鸵鸟算法,我们想要直接解决问题的方法,不想要解决发现问题的人的方法
- 给出的第二个方法说 此外,您可以将环境变量TORCH_DISTRIBUTED_DEBUG设置为INFO或DETAIL,以打印有关哪些特定参数在此级别上没有收到梯度的信息,作为此错误的一部分
就是说在运行代码的命令之前指定TORCH_DISTRIBUTED_DEBUG=INFO(或者DETAIL)然后再调用代码,
比如
TORCH_DISTRIBUTED_DEBUG=DETAIL torchrun main.py
就可以清楚的显示哪些参数没有被使用(该图来自查看模型的哪些梯度/参数被更新了_如何查看模型梯度-CSDN博客)
但是感觉还是有点麻烦
给出我的办法,再前向传播计算计算损失之后:.backward()之后 加入以下代码:
the following code is only used in the Debug process,which well increase the time of training and testing
for name, param in model.named_parameters():
if param.requires_grad and param.grad is None:
print(f"Parameter {name} has not participated in loss calculation.")
寻找哪些需要计算梯度但是没有梯度的模块(众所周知,一般直接调用的封装好的模块的requires_grad都是True,自己设置的层的requires_gard需要指定为True),打印出来
发现这个input_proj层是需要计算梯度,但是没有梯度的层,(本质是个Conv2d,所以这会又显示权重又显示偏置)这是因为我把这个模块融合到我代码的encoder里了,这里忘了删,所以这就是那位博主所说的:魔改代码的报应,将这个self.input_proj注释掉就不报错了