• Batch Normalization


    Abstract

    1 问题

    Internal Covariate Shift: 训练神经网络主要就是让各个层学习训练数据的分布。在深度神经网络的训练过程中,之前层(之前的任何一层)的参数的发生变化,那么前一层的输出数据分布也会发生变化,也即当前层的输入数据分布会发生变化。由于网络层的输入数据的分布本身都在发生变化,那么训练过程中,当前神经网络层将要持续的学习适应新的数据分布。所以,当一个学习系统的输入数据分布发生变化时,我们说它在经历Internal Covariate Shift。

    由于以上原因,在神经网络训练中,学习率需要比较小,并且需要谨慎的进行参数初始化,并且在使用饱和非线性的激活函数时效果很不好。

    2 解决方法

    每一个mini-batch进行一次batch normalization。

    3 效果

    可以使用更大的学习率 -> 加速训练过程,不必过于担心参数初始化,有时可以省略dropout。

    1. Introduction

    神经网络在优化时,通常采用SGD或其变种(momentum, Adagrad等), 实际中通常采用批量梯度下降。

    SGD虽然简单有效,但需要谨慎调节超参数,尤其是学习率和初始参数值。训练过程也较为复杂,因为之前层中任何一点小的参数的变化,在层数加深的过程中,都会使当前层的输入数据分布有很大变化。所以网络层需要不断适应新的数据分布,也就是面临Internal Covariate Shift的问题。

    可以在 子网络或是某一层 中考虑Internal Covariate Shift。

    $$ l = F_2( F_1 (u, Theta_1),  Theta_2) ag1$$

    固定子网络(sub-network)的输入分布对 子网络本身 和 子网络外部 都有积极影响。

    1.1 对子网络本身

    $$ l = F_2(x, Theta_2) ag2$$

    $$x = F_1 (u, Theta_1) ag3$$

    如果可以固定x的数据分布, 那么$Theta_2$不必重新调整来补偿x的分布的变化。

    1.2 对子网络外部

    $$x = Wu + b ag4$$

    $$ z = g(x) = frac{1}{1 + exp(-x)} ag5$$

    若$|x|$很大,那么$g'(x)$ 将趋于0,会降低网络训练速度。因为$x$是受 $W,b$影响的,所以$W,b$的变化很可能使得$x$在饱和非线性函数中很多维度的值发生变化,$g'(x)$ 趋于0,网络很难收敛。这个问题在网络层加深的过程中将会被放大。

    目前的解决方法:

    1) $ReLU(x) = max(x, 0) $

    2) 初始化(carefully initialization)

    3) 设置小的学习率

    综上,如果可以使得在训练过程中,非线性输入分布更稳定,那么将降低优化器stuck在饱和区域的可能性,那么可以加速训练。

    1.3 Batch Normalization的优势

    1) 加速训练 -> 通过normaliztion固定输入均值和方差;--> 使用更少步数(7%)达到同样准确度。

    2) 对于梯度传递的益处:降低梯度对于参数尺度(scale)和初始值的关系(dependence);

    3) 对模型正则化,降低对dropout的需要;

    4) 可以使用饱和非线性函数,避免网络stuck在饱和区域。

    2. Towards Reducing Internal Covariate Shift

    2.1 目标

    本文的目标即为降低 internal covarivate shift, 也就是希望网络各层的输入数据的分布尽量稳定。

    LeCun, Wiesler & Ney等前人工作总结出:如果网络的输入白化(均值为0,方差为单位1,去相关),那么网络会收敛的更快。

    那么本文就希望通过白化activations来达到目标。

    2.2 白化操作与优化步骤分离的问题

    这里作者提出一个问题:如果白化操作与优化步骤分离,那么在进行梯度下降更新参数时,norm也会被更新,这将影响梯度下降的效果

    很难从字面意思理解这句话。这里作者举了个例子:

    对于$x = u + b$, $chi = {x_{1...N}}$, $E[x] = 1 / n * sum^N_{i = 1}x_i$,

    白化为:$hat{x} = x - E[x]$

    那么进行梯度下降,更新参数:$b leftarrow b + Delta{b}$

    那么白化后,该层的输入为:

    $hat{x}' = u + (b + Delta{b}) - E[u + (b + Delta{b})] = u + b - E[u + b]$

    那么网络的输出、损失将没有任何变化。但是,在下一次梯度下降更新参数后, $b$又会有$ Delta{b}$的变化。那么随着训练继续$b$会趋于无穷,但loss不变。

    鉴于以上问题,作者提出,normalization过程中,要把optimization过程考虑进来

    2.3 归一化与优化综合考虑

    以上问题产生的原因是,在优化时,并不没有考虑到已经进行了归一化操作。

    那么如果可以使得归一化与模型参数有dependence,那么在对loss进行梯度下降时,便可以将归一化考虑进来。

    作者提出:

    $$ hat{x} = Norm(x, chi) ag1$$

    其中$x$为该层的输入向量,$chi$是训练集中用于该层输入的集合。因为$chi$是由网络中的其他层生成的,必然依赖于网络参数$Theta$,这样,就建立了归一化与网络参数之间的关系。

    在梯度下降时,既要考虑Jacobians $$frac{partial{Norm(x, chi)}}{partial{x}}$$,又要考虑$$frac{partial{Norm(x, chi)}}{partial{chi}}$$, 如果不考虑后者,则会出现2.2中所述的问题。

    2.4 Cov的计算

    在这样的框架下,白化的计算开销很大。需要计算协方差矩阵$$Cov[x] = E_{x in chi}[xx^T] - E[x]E[x]^T$$ 和平方根倒数,来产生白化后的结果$$frac{x - E[x]}{Cov[x]^{1/2}}$$

    那么就需要找到一种:可以微分的不需要每次参数更新后都对整个训练集合再次分析的 方法来计算白化后的值。

    3. Normalization via Mini-Batch Statistics

    主要方法就是进行两次缩放

    第一次:根据minibatch数据求norm.

    第二次:神经网络调节参数$gamma, eta$.

    3.1 根据minibatch数据求norm

    这里并没有采用2.4中的求整个协方差矩阵的方法,而是对每一个维度分别求norm.

    对于一个d-dim的输入$x = (x^{(1)}, ... x^{(d)})$, 归一化为:

    $$hat{x}^{(k)} = frac{x^{(k)} - E[x^{(k)}]}{sqrt{Var[x^{(k)}]}} ag2$$

    这样相当于将原来求d * d的协方差矩阵,转换为求该矩阵的对角线的值,计算量大大降低。这也会帮助加速收敛。

    3.2 神经网络调节参数$gamma, eta$

    如果神经网路的每一层的输入都进行3.1中的norm操作,那么它们的分布都是均值为0, 方差为1,显然这会丢失掉训练数据的原本的分布特征。加入$gamma, eta$相当于在一定程度上还原原输入数据分布特征。特别的当$gamma^{(k)} = sqrt{Var[x^{(k)}]}$, $eta^{(k)} = E[x^{(k)}]$时,会完全还原到原数据。

    3.3 利用mini-batch代替整个训练集

    这里的均值和方差采用的是每一个mini-batch数据的。

    没有找到pytorch中的具体实现代码…找到一份不错的tensorflow示例:

    https://blog.csdn.net/qq_25737169/article/details/79048516

     1 def Batchnorm_simple_for_train(x, gamma, beta, bn_param):
     2 """
     3 param:x    : 输入数据,设shape(B,L)
     4 param:gama : 缩放因子  γ
     5 param:beta : 平移因子  β
     6 param:bn_param   : batchnorm所需要的一些参数
     7     eps      : 接近0的数,防止分母出现0
     8     momentum : 动量参数,一般为0.9, 0.99, 0.999
     9     running_mean :滑动平均的方式计算新的均值,训练时计算,为测试数据做准备
    10     running_var  : 滑动平均的方式计算新的方差,训练时计算,为测试数据做准备
    11 """
    12     running_mean = bn_param['running_mean']  #shape = [B]
    13     running_var = bn_param['running_var']    #shape = [B]
    14     results = 0. # 建立一个新的变量
    15     
    16     x_mean=x.mean(axis=0)  # 计算x的均值
    17     x_var=x.var(axis=0)    # 计算方差
    18     x_normalized=(x-x_mean)/np.sqrt(x_var+eps)       # 归一化
    19     results = gamma * x_normalized + beta            # 缩放平移
    20 
    21     running_mean = momentum * running_mean + (1 - momentum) * x_mean
    22     running_var = momentum * running_var + (1 - momentum) * x_var
    23     
    24     #记录新的值
    25     bn_param['running_mean'] = running_mean
    26     bn_param['running_var'] = running_var 
    27     
    28     return results , bn_param

    3.4 训练和测试

    以上代码为训练阶段,每次训练给一个mini-batch,然后计算均值和方差。

    在测试时,每次只输入一张图片,就不用再计算均值和方差了,而是用在训练阶段计算好的mean,var.

    1 running_mean = momentum * running_mean + (1 - momentum) * x_mean
    2 running_var = momentum * running_var + (1 - momentum) * x_var

    测试时:

     1 def Batchnorm_simple_for_test(x, gamma, beta, bn_param):
     2 """
     3 param:x    : 输入数据,设shape(B,L)
     4 param:gama : 缩放因子  γ
     5 param:beta : 平移因子  β
     6 param:bn_param   : batchnorm所需要的一些参数
     7     eps      : 接近0的数,防止分母出现0
     8     momentum : 动量参数,一般为0.9, 0.99, 0.999
     9     running_mean :滑动平均的方式计算新的均值,训练时计算,为测试数据做准备
    10     running_var  : 滑动平均的方式计算新的方差,训练时计算,为测试数据做准备
    11 """
    12     running_mean = bn_param['running_mean']  #shape = [B]
    13     running_var = bn_param['running_var']    #shape = [B]
    14     results = 0. # 建立一个新的变量
    15    
    16     x_normalized=(x-running_mean )/np.sqrt(running_var +eps)       # 归一化
    17     results = gamma * x_normalized + beta            # 缩放平移
    18     
    19     return results , bn_param

    3.5 卷积神经网络中的batchnorm

    这里主要讲了两个问题:1) 对仿射变换后的结果进行norm; 2) 对feature map进行norm

    1) 对仿射变换后的结果进行norm

    $$z = g(x) = g(Wu + b)$$

    其中g为非线性变换。

    在$x = Wu + b$后,需要首先进行$BN(x)$得到x的norm后的值,之后再将$BN(x)$进行$g(BN(x))$。

    这里涉及到是否先对$u$进行BN

    因为u可能值之前非线性变换后的输出,那么在训练中,它的分布也会发生变化,所以即使对其进行BN,也并不会对covariate-shift有很大帮助,所以,只需要对x进行BN即可

    而$x = Wu + b$,这里的b可以忽略,因为这个偏置项可以由$eta$来调节。综上,$z = g(Wu + b)$可以被替换为:

    $$z = g(BN(Wu))$$

    之后,将对x的各个维度进行BN变换,并且每一个维度都有对应的$gamma^{(k)}$, $eta^{(k)}$来训练学习。

    2) 对feature map进行norm

    这里需要考虑如何保留卷积的特性,使得对于同一个feature map中的不同位置的不同元素,都可以以相同的方式进行BN

    对于mini-batch的大小为m, 二维的feature map的大小为$p * q$,将每一个feature map看做一个整体,每一个feature map对应一对$gamma^{(k)}$ 和$eta^{(k)}$。这样做的原因是卷积层包含多个feature map, 而每个feature map是参数共享的,所以将每一个feature map看做一个整体可以体现卷积的思想本质。

    3.6 Batch Normalization enables higher learning rate

    1) Learning rate如果过高会有:梯度爆照或衰减的可能;stuck在局部最小处;

    batchnorm可以避免神经网络层中很小的参数变动在层数加深的过程中会积聚造成很大的影响;

    比如,在饱和激活函数,可以避免输入数据分布在饱和区域,而使得其尽量分布在线性区域;

    2) 对于参数的scale有更强的适应性;

    在学习率很大时,可能使得参数的scale很大,在梯度下降时会放大梯度值,这可能使得模型爆炸;

    对于batchnorm, 对与一个标量a:

    $$BN(Wu) = BN((aW)u)$$

    (1) $$frac{partial{BN((aW)u)}}{partial{u}} = frac{partial{BN(Wu)}}{partial{u}} $$

    (2)  $$frac{partial{BN((aW)u)}}{partial{u}} = frac{1}{a} cdot frac{partial{BN{(Wu)}}}{partial{W}} $$

    所以,大的权值会导致小的梯度,BN会使参数的变化更为稳定。


    3.7 RNN 中应用BN

    注意:在RNN, Transformer模型中,更多使用的是LayerNorm, 一般说来,LayerNorm更适用于序列任务,BatchNorm更适用于图像任务。

    当然还有其他多种norm的算法模型,可以参考 另一篇博客:BatchNormalization、LayerNormalization、InstanceNorm、GroupNorm、SwitchableNorm总结

    https://www.cnblogs.com/shiyublog/p/10823980.html

    RNN 中应用BN有两种方式,一种是在水平方向做,一种在垂直方向做。

    https://blog.csdn.net/malefactor/article/details/51549771

    1) 水平方向

    将RNN在水平方向的展开看作是很深的网络,但实验证明这是不可取的,必须每个时间点神经元各自维护自己的统计量和参数。

    2) 垂直方向

    对垂直方向上叠加的stacked RNN做BN。

    (1) “Batch normalized recurrent neural networks”这个工作是最早尝试将BN引入RNN的,它构建了5层的RNN和LSTM,它的结论是:水平方向的BN对效果有损害作用,垂直方向BN能够加快参数收敛速度,但是相对基准无BN对照组实验看可能存在过拟合问题。但是这个过拟合是由于训练数据规模不够还是模型造成的并无结论。

    (2) “Deep speech 2: End-to-end speech recognition in english and mandarin.”这个工作也尝试将BN引入RNN,也得出了水平方向的BN不可行,垂直方向的BN对加快收敛速度和提升分类效果有帮助,这点和第一个工作结论一致。另外,新的结论是:在训练数据足够大的情况下,如果垂直方向网络深度不深的话,垂直方向的BN效果也会有损害作用,这个其实和工作一的结论基本一致,也说明了这个过拟合不是训练数据不够导致的,而是浅层模型加入BN效果不好。但是如果垂直方向深度足够深,那么加入BN无论是训练速度还是分类效果都获得了提高

    (3)  “Recurrent Batch Normalization”是最新的工作,16年4月份的论文。它的实验结果推翻了上面两个工作的结论。证明了水平方向BN是能够加快训练收敛速度以及同时提升模型泛化能力的。论文作者认为前面两个工作之所以BN在水平方向上不行,很可能主要是BN的Scale参数设置不太合理导致的,他们的结论是:Scale参数要足够小才能获得好的实验效果,如果太大会阻碍信息传播。Scale一般设置为0.1是OK的。


    4. Pytorch BatchNorm2d

    CLASS torch.nn.BatchNorm2d(num_featureseps=1e - 05momentum=0.1affine=Truetrack_running_stats=True)

    Applies Batch Normalization over a 4D input (a mini-batch of 2D inputs with additional channel dimension) .

    Parameters

    • num_features – CC from an expected input of size (N, C, H, W)

    • eps – a value added to the denominator for numerical stability. Default: 1e-5

    • momentum – the value used for the running_mean and running_var computation. Can be set to None for cumulative moving average (i.e. simple average). Default: 0.1.这里的momentum不同于优化器中的momentum. $hat{x_{new}} = (1 - momentum) * hat{x} + momentum * x_t$, 其中$hat{x}$是估计统计量,$x_t$是新观测到的值。

    • affine – a boolean value that when set to True, this module has learnable affine parameters. Default: True. 该参数设置,是否要学习参数$gamma, eta$.

    • track_running_stats – a boolean value that when set to True, this module tracks the running mean and variance, and when set to False, this module does not track such statistics and always uses batch statistics in both training and eval modes. Default: True. 是否要维持running mean and variance,类似于tensorflow中的滑动平均。

    shape:

    • Input: (N, C, H, W)(N,C,H,W)

    • Output: (N, C, H, W)(N,C,H,W) (same shape as input)

  • 相关阅读:
    RubyConf的podcast
    一篇很好的英语学习文章:一个孤独而封闭世界――英语口语
    新浪和搜狐的读书频道
    新想法:个性化的RSS
    代码搜索:Koders
    我看到的Web 2.0: 自组织的大众化参与
    土豆网的后舍男孩挺搞笑的
    可以给pdf加批注的软件VeryPDF PDF Editor
    张海迪写的描写英语学习经验的书《美丽的英语》
    Fowler出来推荐Rake了(基于Ruby的build工具)
  • 原文地址:https://www.cnblogs.com/shiyublog/p/10811584.html
Copyright © 2020-2023  润新知