Python 为什么批次规范化会使我的批次如此异常?

Python 为什么批次规范化会使我的批次如此异常?,python,neural-network,pytorch,batch-normalization,Python,Neural Network,Pytorch,Batch Normalization,我第一次玩Pytork,我注意到在训练我的神经网络时,大约四分之一左右的损失会向左转向无穷远,然后很快就会消失。我看到了一些关于南宁的其他问题,但那里的建议似乎基本上是为了实现标准化;但是在我的网络下面的第一层是这样一个规范化,我仍然看到这个问题!完整的网络是,但我已经做了一些调试,试图生成一个非常小的、可以理解的网络,它仍然显示相同的问题 代码如下;它由16个输入(0-1)组成,这些输入通过批处理规范化,然后通过完全连接的层传递到单个输出。我想让它学习总是输出1的函数,所以我取1的平方误差作为

我第一次玩Pytork,我注意到在训练我的神经网络时,大约四分之一左右的损失会向左转向无穷远,然后很快就会消失。我看到了一些关于南宁的其他问题,但那里的建议似乎基本上是为了实现标准化;但是在我的网络下面的第一层是这样一个规范化,我仍然看到这个问题!完整的网络是,但我已经做了一些调试,试图生成一个非常小的、可以理解的网络,它仍然显示相同的问题

代码如下;它由16个输入(0-1)组成,这些输入通过批处理规范化,然后通过完全连接的层传递到单个输出。我想让它学习总是输出1的函数,所以我取1的平方误差作为损失

import torch as t
import torch.nn as tn
import torch.optim as to

if __name__ == '__main__':
    board = t.rand([1,1,1,16])
    net = tn.Sequential \
        ( tn.BatchNorm2d(1)
        , tn.Conv2d(1, 1, [1,16])
        )
    optimizer = to.SGD(net.parameters(), lr=0.1)
    for i in range(10):
        net.zero_grad()
        nn_outputs = net.forward(board)
        loss = t.sum((nn_outputs - 1)**2)
        print(i, nn_outputs, loss)
        loss.backward()
        optimizer.step()
如果您运行几次,最终会看到如下运行:

0 tensor([[[[-0.7594]]]], grad_fn=<MkldnnConvolutionBackward>) tensor(3.0953, grad_fn=<SumBackward0>)
1 tensor([[[[4.0954]]]], grad_fn=<MkldnnConvolutionBackward>) tensor(9.5812, grad_fn=<SumBackward0>)
2 tensor([[[[5.5210]]]], grad_fn=<MkldnnConvolutionBackward>) tensor(20.4391, grad_fn=<SumBackward0>)
3 tensor([[[[-3.4042]]]], grad_fn=<MkldnnConvolutionBackward>) tensor(19.3966, grad_fn=<SumBackward0>)
4 tensor([[[[823.6523]]]], grad_fn=<MkldnnConvolutionBackward>) tensor(676756.7500, grad_fn=<SumBackward0>)
5 tensor([[[[3.5471e+08]]]], grad_fn=<MkldnnConvolutionBackward>) tensor(1.2582e+17, grad_fn=<SumBackward0>)
6 tensor([[[[2.8560e+25]]]], grad_fn=<MkldnnConvolutionBackward>) tensor(inf, grad_fn=<SumBackward0>)
7 tensor([[[[inf]]]], grad_fn=<MkldnnConvolutionBackward>) tensor(inf, grad_fn=<SumBackward0>)
8 tensor([[[[nan]]]], grad_fn=<MkldnnConvolutionBackward>) tensor(nan, grad_fn=<SumBackward0>)
9 tensor([[[[nan]]]], grad_fn=<MkldnnConvolutionBackward>) tensor(nan, grad_fn=<SumBackward0>)
0张量([-0.7594]]],梯度fn=)张量(3.0953,梯度fn=)
1张量([[4.0954]]],梯度fn=)张量(9.5812,梯度fn=)
2张量([[5.5210]]],梯度fn=)张量(20.4391,梯度fn=)
3张量([-3.4042]]],梯度fn=)张量(19.3966,梯度fn=)
4张量([[823.6523]]],梯度fn=)张量(676756.7500,梯度fn=)
5张量([[3.5471e+08]]],梯度fn=)张量(1.2582e+17,梯度fn=)
6张量([[2.8560e+25]]],梯度fn=)张量(inf,梯度fn=)
7张量([[[inf]]],梯度fn=)张量(inf,梯度fn=)
8张量([[[nan]]],梯度fn=)张量(nan,梯度fn=)
9张量([[[nan]]],梯度fn=)张量(nan,梯度fn=)

为什么我的损失会落到nan身上,我能做些什么?

要使批次标准有效,您希望您的培训批次较大。
在2d或3d批次规范的情况下,“有效批次”还包括特征地图的空间/时间维度。在您的示例中,您有
batch_size=1
,并且只有16个样本按空间排列。这可能太小,批处理规范无法有效工作。 尝试增加“有效批量大小”:

board=t.rand([1,1,16,16])
#或
board=t.rand([1,16,1,16])
看看这对你的训练有什么影响

顺便说一句,如果你删除批量标准,你的玩具示例会发生什么?

欢迎来到pytorch

以下是我如何安排您的培训。请检查评论

# how the comunity usually does the import:
import torch  # some people do: import torch as th 
import torch.nn as nn
import torch.optim as optim

if __name__ == '__main__':
    
    # setting some parameters:
    batch_size = 32
    n_dims = 128

    # select GPU if available
    device = 'cuda' if torch.cuda.is_available() else 'cpu'

    # initializing a simple neural net
    net = nn.Sequential(nn.Linear(n_dims, n_dims  // 2), # Batch norm is not usually used directly on the input
                        nn.BachNorm1d(n_dims  // 2), # Batch norm is used before the activation function (it centers the input and helps make the dims of the previous layers independent of each other)
                        nn.ReLU(), # the most common activation function
                        nn.Linear(n_dims  // 2, 1)  # final layer)
    net.to(device) # model is copied to the GPU if it is availalbe

    optimizer = to.SGD(net.parameters(), lr=0.01) # it is better to start with a low lr and increase it at later experiments to avoid training divergence, the range [1.e-6, 5.e-2] is recommended.

    for i in range(10): 
       # generating random data:
       board = torch.rand([batch_size, n_dims])
       # for sequences: [batch_size, channels, L]
       # for image data: [batch_size, channels, W, H]
       # for videos: [batch_size, chanels, L, W, H]
       boad = board.to(device) # data is copied to the gpu if it is available

        optimizer.zero_grad()  # the convension the comunity uses, though the result is the same as net.zero_grad() 

        nn_outputs = net(board) # don't call net.forward(x), call net(x). Pytorch applies some hooks in the net.__call__(x) that are useful for backpropagation.

        loss = ((nn_outputs - 1)**2).mean() # using .mean() makes your training less sensitive to the batch size.

        print(i, nn_outputs, loss.item())

        loss.backward()
        optimizer.step()
关于批处理规范的一条评论。根据维度,它计算批次的平均值和标准偏差(查看文档):


其中,缩放和平移是可学习的参数。如果每批只给出一个示例,
x.std(0)=0
将使
x\u规范化
包含非常大的值。

您是否尝试过不规范化运行?你确定它的bn吗?@BedirYilmaz是的,没有批量标准化,这可靠地产生接近零的损失。我承认我没有测试在没有完全连接层的情况下会发生什么,哈!你在哪里看到最后引用的代码?我一直在试图深入查看源代码,以找到
BatchNorm2d
实际上在哪里见底并遇到了一些问题(每个
batch\u norm
似乎只是调用了一个名为
batch\u norm
!)的不同函数)。但你的描述似乎不可能是对的。我没有看到像你预测的那么大的值。另外,
board.std(dim=0)
是一个充满了NAN的张量,而不是你所说的
0
;即使在第一次迭代时,我也会期望得到nan,但我也看不到会发生这种情况。最后的代码来自文档,我编辑了答案以添加链接。你得到张量为nan的原因可能是因为你只举了一个例子!我理解为什么
board.std(dim=0)
给我一个
nan
s的张量。我(还)不明白为什么训练我的神经网络会在无穷大(然后是
nan
)之前多次迭代给出合理的结果。我的第一个猜测是,在网络的权重退化为
nan
之前,需要几次迭代。如果你尝试不同的学习率,我预计如果你使用较低的学习率,它会花更长的时间爆炸。
x_normalized = (x.mean(dim=0) / (x.std(dim=0) + e-6)) * scale + shift