transformer训练过程中出现Nan

如图所示,在使用vit(PVT v2)作为backbone(并未加载预训练权重,因为修改了patch_size)做语义分割时,编码过程中即embed中的self.proj即Conv2D的输出为nan,导致无法训练。并不是在第一个epoch就出现这个问题,而是在训练了一定epoch之后才会出现这个问题。损失函数用的是Crossentropy2D,模型的最后一层输出是Softmax。请问是哪里出了问题?困扰了很多天,若能解决,红包奉上

class OverlapPatchEmbed(nn.Module):
    """ Image to Patch Embedding
    """

    def __init__(self, img_size=224, patch_size=7, satride=4, in_chans=3, embed_dim=768):
        super().__init__()

        img_size = to_2tuple(img_size)
        patch_size = to_2tuple(patch_size)

        assert max(patch_size) > stride, "Set larger patch_size than stride"

        self.img_size = img_size
        self.patch_size = patch_size
        self.H, self.W = img_size[0] // stride, img_size[1] // stride
        self.num_patches = self.H * self.W
        self.proj = nn.Conv2d(in_chans, embed_dim, kernel_size=patch_size, stride=stride,
                              padding=(patch_size[0] // 2, patch_size[1] // 2))

        self.norm = nn.LayerNorm(embed_dim)

        self.apply(self._init_weights)

    def _init_weights(self, m):
        if isinstance(m, nn.Linear):
            trunc_normal_(m.weight, std=.02)
            if isinstance(m, nn.Linear) and m.bias is not None:
                nn.init.constant_(m.bias, 0)
        elif isinstance(m, nn.LayerNorm):
            nn.init.constant_(m.bias, 0)
            nn.init.constant_(m.weight, 1.0)
        elif isinstance(m, nn.Conv2d):
            fan_out = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
            fan_out //= m.groups
            m.weight.data.normal_(0, math.sqrt(2.0 / fan_out))
            if m.bias is not None:
                m.bias.data.zero_()

    def forward(self, x):
        print(f"我在x = self.proj(x)的上面,我是输入,我有{torch.isnan(x).sum()}个nan")
        print(f"我在x = self.proj(x)的上面,我是输入,我有{torch.isinf(x).sum()}个inf")
        x = self.proj(x)
        print(self.proj.weight.grad)

        print(f"我在x = self.proj(x)的下面,我有{torch.isnan(x).sum()}个nan,此时self.proj{self.proj}")
        print(f"我在x = self.proj(x)的下面,我有{torch.isinf(x).sum()}个inf,此时self.proj{self.proj}")
        # Conv2d(1, 64, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2)):torch.Size([32, 64, 64, 64]):8388608
        #这里出现nan
        _, _, H, W = x.shape
        x = x.flatten(2).transpose(1, 2)
        # 出现nan
        print(f"我在patch_embed的norm上面,我有{torch.isnan(x).sum()}个nan")
        print(f"我在patch_embed的norm上面,我有{torch.isinf(x).sum()}个inf")
        x = self.norm(x)
        print(f"我在patch_embed的norm下面,我有{torch.isnan(x).sum()}个nan")
        print(f"我在patch_embed的norm下面,我有{torch.isinf(x).sum()}个inf")

        return x, H, W

self.proj的权重前几个批次打印的结果为全0,后来变为:
tensor([[[[nan, nan, nan, nan, nan],
          [nan, nan, nan, nan, nan],
          [nan, nan, nan, nan, nan],
          [nan, nan, nan, nan, nan],
          [nan, nan, nan, nan, nan]]],

        [[[nan, nan, nan, nan, nan],
          [nan, nan, nan, nan, nan],
          [nan, nan, nan, nan, nan],
          [nan, nan, nan, nan, nan],
          [nan, nan, nan, nan, nan]]],
        ...,

其余的打印结果:
我在def forward_features(self, x):下面,我有0个nan
我在def forward_features(self, x):下面,我有0个inf
我在x = self.proj(x)的上面,我是输入,我有0个nan
我在x = self.proj(x)的上面,我是输入,我有0个inf

img

参考GPT和自己的思路:您可以尝试以下步骤来进一步调试和解决问题:

1 请检查输入是否包含NaN或无穷大的值。您可以使用torch.isnan和torch.isinf函数检查张量中是否包含NaN或无穷大的值。

2 检查模型的权重是否包含NaN或无穷大的值。您可以使用以下代码来检查模型中的所有参数是否包含NaN或无穷大的值:

for name, param in model.named_parameters():
    if torch.isnan(param).any() or torch.isinf(param).any():
        print(name)


3 检查模型的输出是否包含NaN或无穷大的值。您可以在模型的前向传递中添加一些打印语句来检查模型的输出是否包含NaN或无穷大的值。
4 如果您在使用Adam或RMSprop等优化器,请检查梯度是否包含NaN或无穷大的值。您可以在训练过程中添加以下代码来检查梯度是否包含NaN或无穷大的值:

for name, param in model.named_parameters():
    if param.grad is not None and (torch.isnan(param.grad).any() or torch.isinf(param.grad).any()):
        print(name)

如果发现梯度包含NaN或无穷大的值,您可以尝试降低学习率或尝试使用其他优化器,例如SGD。

5 尝试使用FP16训练。FP16可以减少内存占用,并且可以更好地避免NaN值。
6 尝试使用其他的初始化方式,例如kaiming或xavier初始化。

是不是输出的值过大或者过小,或者非法。

该回答引用ChatGPT

如有疑问,可以回复我!

提供一些建议去排查:

1、学习率过高:学习率过高可能导致权重更新过大,从而引发梯度爆炸,最终导致NaN值。检查一下你的学习率设置,尝试降低学习率,看看问题是否得到解决。

2、损失函数计算:确保损失函数的计算没有出现除以零或者其他可能导致NaN值的操作。你可以在损失函数计算之前和之后添加检查,以确保没有NaN值出现。

3、数据预处理:检查输入数据,确保没有不合理的值(例如极端的大值或NaN值)。你可以在数据加载和预处理阶段添加一些检查,以确保输入数据的正确性。

4、梯度裁剪:在训练过程中,梯度可能会变得非常大,导致权重更新过大,从而引发NaN值。你可以尝试使用梯度裁剪(例如torch.nn.utils.clip_grad_norm_)来限制梯度的大小,防止梯度爆炸。

5、检查权重初始化:权重初始化对于训练的稳定性至关重要。如果权重初始化不合理,可能导致训练不稳定。你可以尝试使用不同的权重初始化策略,例如Xavier初始化或Kaiming初始化。

6、检查Batch大小:较小的Batch大小可能导致训练不稳定。尝试增大Batch大小,看看问题是否得到解决。

最后,如果以上建议都不能解决问题,你可以尝试在其他数据集上进行训练,以排除数据集本身的问题。同时,你也可以尝试使用其他的语义分割架构,看看是否会遇到相同的问题。这有助于确定问题是否与特定的模型架构相关。

该回答引用GPTᴼᴾᴱᴺᴬᴵ
出现NaN一般是由于数值溢出或数值不稳定所导致的,这通常是由于网络的层数太深、学习率过高或梯度爆炸等原因造成的。
·
在你的代码中,我注意到你的Conv2D层有一个padding参数为patch_size[0] // 2, patch_size[1] // 2,而stride=4,这会导致边缘的像素被重复多次,这可能会使网络更难以训练。因此,你可以尝试减少padding的大小,例如将其设置为patch_size[0] // 4, patch_size[1] // 4。
·
此外,你也可以尝试减小学习率以减少梯度爆炸的风险。你可以逐渐减小学习率,例如使用一些学习率调度程序来逐渐减小学习率。
·
最后,你也可以在网络的前向函数中添加一些输出来帮助你调试问题。例如,你可以在每个层之后输出输出张量的最大值和最小值以检查它们是否在合理范围内。

参考GPT和自己的思路,可能的原因是由于某些数值太大或太小,导致了梯度爆炸或梯度消失,进而出现NaN值。为了解决这个问题,你可以尝试以下几个方法:

1.梯度裁剪:在训练过程中限制梯度的范围,避免梯度爆炸。你可以使用PyTorch的torch.nn.utils.clip_grad_norm_()函数来实现梯度裁剪,例如:

torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1)

2.调整学习率:学习率太大也会导致模型出现NaN值。你可以尝试减小学习率,或使用学习率衰减策略,例如使用torch.optim.lr_scheduler模块中的学习率调度器。

3.参数初始化:你可以尝试不同的参数初始化方法,例如使用更小的标准差初始化权重。

4.检查数据集和预处理方式:如果输入数据中存在NaN值或过大/过小的值,会导致模型输出NaN值。你可以检查数据集是否存在这些异常值,并且检查数据预处理方式是否正确。

另外,你的代码中也存在一些小问题:

1.satride应该是stride的拼写错误,需要修改。

2.在使用nn.Conv2d进行卷积时,应该使用padding_mode='zeros',避免边界像素被忽略。

下面是修改后的代码:

class OverlapPatchEmbed(nn.Module):
    """ Image to Patch Embedding
    """

    def __init__(self, img_size=224, patch_size=7, stride=4, in_chans=3, embed_dim=768):
        super().__init__()

        img_size = to_2tuple(img_size)
        patch_size = to_2tuple(patch_size)

        assert max(patch_size) > stride, "Set larger patch_size than stride"

        self.img_size = img_size
        self.patch_size = patch_size
        self.H, self.W = img_size[0] // stride, img_size[1] // stride
        self.num_patches = self.H * self.W
        self.proj = nn.Conv2d(in_chans, embed_dim, kernel_size=patch_size, stride=stride,
                              padding=(patch_size[0] // 2, patch_size[1] // 2), padding_mode='zeros')

        self.norm = nn.LayerNorm(embed_dim)

        self.apply(self._init_weights)

    def _init_weights(self, m):
        if isinstance(m, nn.Linear):
            trunc_normal_(m.weight, std=.02)
            if isinstance(m, nn.Linear) and m.bias is not None:
                nn.init.constant_(m.bias, 0)
        elif isinstance(m, nn.LayerNorm):
            nn.init.constant_(m.bias, 0)
            nn.init.constant_(m.weight, 1.0)
        elif isinstance(m, nn.Conv2d):
            fan_out = m.kernel_size[0] * m.kernel_size[1]
and m.out_channels
fan_out //= m.groups
m.weight.data.normal_(0, math.sqrt(2.0 / fan_out))
if m.bias is not None:
m.bias.data.zero_()

def forward(self, x):
    print(f"我在x = self.proj(x)的上面,我是输入,我有{torch.isnan(x).sum()}个nan")
    print(f"我在x = self.proj(x)的上面,我是输入,我有{torch.isinf(x).sum()}个inf")
    x = self.proj(x)
    print(self.proj.weight.grad)

    print(f"我在x = self.proj(x)的下面,我有{torch.isnan(x).sum()}个nan,此时self.proj{self.proj}")
    print(f"我在x = self.proj(x)的下面,我有{torch.isinf(x).sum()}个inf,此时self.proj{self.proj}")
    # Conv2d(1, 64, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2)):torch.Size([32, 64, 64, 64]):8388608
    #这里出现nan
    _, _, H, W = x.shape
    x = x.flatten(2).transpose(1, 2)
    # 出现nan
    print(f"我在patch_embed的norm上面,我有{torch.isnan(x).sum()}个nan")
    print(f"我在patch_embed的norm上面,我有{torch.isinf(x).sum()}个inf")
    x = self.norm(x)
    print(f"我在patch_embed的norm下面,我有{torch.isnan(x).sum()}个nan")
    print(f"我在patch_embed的norm下面,我有{torch.isinf(x).sum()}个inf")

    return x, H, W

以下答案由GPT-3.5大模型与博主波罗歌共同编写:
在模型的训练过程中出现了NaN值通常是因为模型的权重或梯度溢出了范围,或者学习率过高等原因导致的。针对这个问题,可以有以下几点建议:

1.检查是否有数据预处理的问题,例如输入的均值和方差是否正确,是否做了归一化等。

2.检查模型权重初始化是否正确,初始化过大或过小都可能导致数值趋近于无穷大或趋近于零。

3.使用梯度裁剪技术,可以将梯度进行裁剪,使其不超过一个给定的阈值。

4.降低学习率,可以减缓参数的变化速度,避免过快地跳过最优解。

5.使用更小的batch size,可以降低参数的更新频率,减小梯度溢出的风险。

6.检查损失函数计算时是否出现了数值不稳定的情况,可以尝试使用数值更稳定的损失函数。

对于您的具体问题,我们可以看到您在self.proj这一层的输出中出现了NaN值。有可能是因为这一层权重初始化不正确,或者梯度溢出导致。建议先尝试打印一下self.proj这一层的权重,看看其是否存在问题。同时可以考虑使用梯度裁剪或减小学习率的方法进行处理。另外,还可以尝试修改batch size或者损失函数等来解决这个问题。

注意,在代码中出现了print语句并且重要的信息被输出。在检查的过程中,可以尝试增加更多的print语句来进行调试,找到具体是哪一步出现了问题以及产生问题的原因,这样可以更快地定位问题并解决它。
如果我的回答解决了您的问题,请采纳!