Navigation

    Gpushare.com

    • Register
    • Login
    • Search
    • Popular
    • Categories
    • Recent
    • Tags

    Batch Normalization

    语音识别与语义处理领域
    1
    1
    38
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • 155****7220
      155****7220 last edited by

      Batch Normalization(批量归一化)是深度学习中经常用到的一种归一化方法

      我们知道Sigmoid函数在定义域为(−∞,−4)∪(4,∞)(-\infty,-4) \cup (4,\infty)(−∞,−4)∪(4,∞)内导数趋于0,由于容易出现梯度消失的现象,因此ReLU函数使用的较多

      但在某些场合不可避免的要去使用Sigmoid函数,因此我们希望能把输入的xxx值控制在有效的区间内,进行一个等效变化,使这些值均匀的分布在0附近,这样输入到Sigmoid函数后就能大概率避免梯度消失

      如下面左侧的图所示,x1x_1x1​的值处于一个比较小的区间x2x_2x2​的值处于一个比较大的区间,因此对于w2w_2w2​来说,少量的变化就会对Loss产生急剧的变化,而对w1w_1w1​来说,变化就会相对小一点,可以看下面的“等高线”图,沿着纵轴方向变化的话,等高线会急剧的变化,沿着横轴方向变化,等高线的变化就稍平缓一点

      但如果如右图所示,输入的值区间都很接近,这样w1w_1w1​和w2w_2w2​对Loss的影响就比较接近,就会形成一种“圆形”路径,在这种情况进行搜索的时候,不管从哪个点出发,梯度的方向都是指向全局最小解的方向,这种搜索过程会比较快一些,而且更稳定

      Batch Normalization较多的应用于两个方面

      • Image Normalization,例如对RGB三通道进行Normalization,将数据进行统一缩放
      normalize = transforms.Normalize(mean=ean[0.485, 0.456, 0.406],
                                       std=[0.229, 0.224, 0.225])
      

      mean有三个值,分别对应RGB三个通道的均值,具体的Normalize过程就是
      $$
      \begin{align*}
      x_R&= \frac{x_R-0.485}{0.229} \
      x_G&= \frac{x_G-0.456}{0.224} \
      x_B&= \frac{x_B-0.406}{0.225} \
      \end{align*}
      $$

      常见的Normalization

      目前常见的Normalization有四种,将输入的图像shape记为[N,C,H,W],这几个方法主要区别是:

      • BatchNorm:batch方向做归一化,计算NHW的均值,对小batchsize效果不好;(BN主要缺点是对batchsize的大小比较敏感,由于每次计算均值和方差是在一个batch上,所以如果batchsize太小,则计算的均值、方差不足以代表整个数据分布)
      • LayerNorm:channel方向做归一化,计算CHW的均值;(对RNN作用明显)
      • InstanceNorm:一个batch,一个channel内做归一化。计算HW的均值,用在风格化迁移;(因为在图像风格化中,生成结果主要依赖于某个图像实例,所以对整个batch归一化不适合图像风格化中,因而对HW做归一化。可以加速模型收敛,并且保持每个图像实例之间的独立)
      • GroupNorm:将channel方向分group,然后每个group内做归一化,算(C//G)HW的均值;这样与batchsize无关,不受其约束

      详述Batch Normalization

      如下图,输入数据是6张3通道784个像素点的数据,将其分到三个通道上,在每个通道上也就是[6,784]的数据,然后分别得到和通道数一样多的统计数据均值μ\muμ和标准差σ\sigmaσ。将每个像素值减去μ\muμ,除以σ\sigmaσ就变换成了近似于N(0,1)N(0,1)N(0,1)分布的数据,之后再用参数γ\gammaγ和β\betaβ将其变化到近似N(β,γ)N(\beta, \gamma)N(β,γ)的分布


      μ\muμ和σ\sigmaσ只是样本中的统计数据,是没有梯度信息的,不过会保存在运行参数里。而γ\gammaγ和β\betaβ属于训练的参数,是有梯度信息的

      Batch Normalize的规范化写法为

      前三步是batch normalization的工序,经过这三步以后数据就近似于标准正态分布N(0,1)N(0,1)N(0,1),但是后面的公式还有一个反向操作,将normalize后的数据再平移和扩展,让数据近似于N(β,γ)N(\beta, \gamma)N(β,γ),这位为了让神经网络自己去学着使用和修改这两个扩展参数,这样神经网络就能自己慢慢琢磨出前面的normalization操作到底有没有起到优化的做哟个,如果没有起到优化的作用,就用γ\gammaγ和β\betaβ来抵消一些normalization的操作

      下面具体看一下在PyTorch中如何实现Batch Normalize

      import torch
      import torch.nn as nn
      import torch.nn.functional as F
      
      # 随机生成一个Batch的模拟,100张16通道784像素点的数据
      # 均匀分布U(0~1)
      x = torch.rand(128, 16, 784)
      # 将28*28变为打平为一维的784
      layer = nn.BatchNorm1d(16)
      # Batch Normalization层,因为输入是将高度H和宽度W合成了一个维度,所以这里用1d
      # 因为Batch Norm的参数直接是由channel数量得来的,因此这里直接给定了channel的数量为16,后续会输出16个channel的统计信息
      out = layer(x) # f orward
      
      print(out.shape)
      print(layer.running_mean) # 全局的均值
      print(layer.running_var) # 全局的方差
      

      运行结果

      tensor([0.0500, 0.0498, 0.0499, 0.0501, 0.0500, 0.0502, 0.0500, 0.0500, 0.0500,
              0.0500, 0.0499, 0.0501, 0.0500, 0.0499, 0.0499, 0.0502])
      tensor([0.9084, 0.9084, 0.9083, 0.9084, 0.9084, 0.9083, 0.9083, 0.9083, 0.9083,
              0.9083, 0.9084, 0.9084, 0.9083, 0.9083, 0.9083, 0.9083])
      

      注意layer.running_mean和layer.running_var得到的是全局的均值和方差,不是当前Batch上的,只不过这里只跑了一个Batch而已所以它就是这个Batch上的。现在还没有办法直接查看某个Batch上的这两个统计量的值

      x = torch.randn(1, 16, 7, 7)  # 1张16通道的7乘7的图像
      
      # Batch Normalization层,因为输入是有高度H和宽度W的,所以这里用2d
      layer = nn.BatchNorm2d(16)  # 传入通道数
      out = layer(x)
      
      print(out.shape)
      print(layer.running_mean)  # 全局的均值
      print(layer.running_var)  # 全局的方差
      print(layer.weight)  # weight也就是前面公式里的gamma
      print(layer.bias)  # bias也就是前面公式里的beta
      

      运行结果

      torch.Size([1, 16, 7, 7])
      tensor([ 0.0187,  0.0125, -0.0032,  0.0032,  0.0034,  0.0031,  0.0231, -0.0024,
               0.0002,  0.0194, -0.0097,  0.0177,  0.0324, -0.0013,  0.0128, -0.0086])
      tensor([0.9825, 0.9799, 0.9984, 0.9895, 0.9992, 0.9809, 0.9919, 0.9769, 0.9928,
              0.9949, 1.0055, 1.0368, 0.9867, 0.9904, 1.0097, 0.9910])
      Parameter containing:
      tensor([0.5925, 0.5662, 0.1066, 0.0073, 0.9517, 0.0476, 0.0416, 0.2041, 0.8666,
              0.6467, 0.7665, 0.0300, 0.9050, 0.8024, 0.2816, 0.1745],
             requires_grad=True)
      Parameter containing:
      tensor([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
             requires_grad=True)
      

      注意这里的layer.weight和layer.bias是当前batch上的

      如果在定义层时使用了参数affine=False,那么就是固定$\gamma = 1$和$\beta=0$不自动学习,这时参数layer.weight和layer.bias将是None

      总结

      使用了γ\gammaγ和β\betaβ之后,最后得到的分布是往N(β,γ)N(\beta, \gamma)N(β,γ)上靠的,而不是往N(0,1)N(0, 1)N(0,1)上靠的

      使用了Batch Normalization让Converge(收敛)的速度加快了,这个可以直观理解,使用了靠近0的部分的Sigmoid激活,其梯度信息更大了。并且能够得到一个更好的解

      提升了Robust(鲁棒性),这使得网络更加稳定,这可以从最前面第二张图所示来直观理解,如果参数有大有小,解空间像左边一样,那么稍微调整学习率可能就发生抖动(如图中左侧椭圆解空间上下方向走,且学习率太大时)或者训练速度太慢(如图中右侧椭圆解空间左右方向走,且学习率太小时)。这让超参数的调整没有那么敏感

      1 Reply Last reply Reply Quote 1
      • First post
        Last post