参数优化之Batch Normalization

一、备注

        最近我在研究深度学习和神经网络,看了一些比较经典的神经网络论文,有些地方不是很懂,于是简单地记录下来,一方面是供小伙伴们学习和讨论,另一方面也是通过写博客来激励自己学习。

       神经网络的训练是一个玄学问题,参数调的不好的话训练速度会很慢。Batch Normalization是2015年提出的,主要是用于加快神经网络的训练,提高模型的泛化能力,而且实际测试的效果不错。我看完以后还是有很多不懂的地方,看了网上很多大佬的分析,才慢慢有所领悟,也想将我的一些想法记录下来。

       关于深度学习这一块,初学者还是应该多读论文,多写代码多实践,至少目前来看,我觉得深度学习还是比较依赖个人经验的,很多想法都是在大量实践中得出来的。

二、原理

    1.数据分布问题

        Batch Normalization是针对mini-batch sgd设计的,文章开头提到了数据的分布问题。原始训练数据通过输入层以后会被激活,在这个过程中数据的分布会发生变化,而且这种变化会随着神经网络层数的增加而加剧。当输入层的分布发生变化时,训练速度会减慢,因为模型需要适应这种分布的变化,由一种数据分布迁移到另一种数据分布,这不是DNN考虑的问题。数据分布的变化,文章将其称为covariate shift(应该翻译为协变吧)。

        对于多层神经网络来说,上一层的输出可以看作下一层的输入,于是,数据的分布在经过上一层的激活后,分布会发生变化,也就是说,数据的分布在神经网络内部发生了变化,作者把这称为Internal Covariate Shift(内部协变),这个内部,指的是神经网络内部。

        两层的神经网络可以这样表示,

        l =F_2(F_1(u,\theta_1),\theta_2)

        令x=F_1(\mu,\theta_1), 则l =F_2(x,\theta_2)F_1的输出是F_2的输入,利用梯度下降更新\theta_2,

        \theta_2 \leftarrow \theta_2 -\frac{\alpha}{m} \sum_{i=1}^{m} \frac{\partial F_2(x_i,\theta_2)}{ \partial \theta_2}

        m是batch size, \alpha是学习率。

        作者将F_2看成子网络,而该网络的输入是上一层的输出,这样便于分析。

        当训练数据的分布和测试数据相同的时候,训练效率会更高,这个很容易理解,\theta_2不用再去适应另一个分布了。

        分布的变化还有另外一个问题,那就是梯度的消失,当激活函数为sigmoid是,输入数据经过激活后可能分布可能会变换到sigmoid梯度弥散的部分,导致模型的收敛减慢。

       

        

        sigmoid函数图像如图,当|x|较大时,函数的导数会趋于0,导致模型训练速度会减慢,解决办法是通过变换,将x的分布尽量变换到0附近,此处的导数值较大,有利于模型的训练。

    2.Batch normalization

       为了解决上述问题,作者提出了Batch Normalization操作。

       之前的研究表明,白化操作可以加快模型的训练。白化就是利用线性变换将x的均值变为0,方差变为1.

       x^*= \frac{x-\mu}{\sigma}

       在更新的过程中还要考虑激活的过程,否则会出现问题,文章举了一个例子。

       假设有一个网络层,对于输入\mu, 输出x=\mu + b,  对x进行归一化,有\hat x=x-E[x], 对b进行更新,则

       b \leftarrow b + \Delta b ,  \frac {\partial l}{\partial b} \propto \frac {\partial l}{\partial x}, 于是,\Delta b \propto -\frac{\partial l}{\partial x},  更新以后,x^{'} =\mu +b - \Delta b, 按照白化操作更新x^'

       x^*'=\mu +(b +\Delta b)-E(\mu +(b +\Delta b))=u+b-E(u+b)=x- E(x)

       标准化以后与x^*一样,也就是说,激活后的x与激活前一样,这样,下一层的输入就没有变化,训练的误差也不会有变化,这个原因是因为x的期望与参数b有关。

       为了解决这个问题,在标准化的过程中还要考虑训练集。

       假设模型参数为\Thetax为输入,\chi为训练集,那么标准化应该是\hat x=Norm(x,\chi) ,反向传播的过程中,需要计算一下两个雅可比矩阵

       \frac{\partial Norm(x,\chi)}{\partial x}和 \frac{\partial Norm(x,\chi)}{\partial \chi}

       如果不考虑后者的话就会出现上文提到的损失不更新的问题。

       但是上述操作代价比较大,于是作者寻找了一种替代方案,首先,并不对输入和输出同时尽行标准化,而是对输入向量中的每一个特征进行标准化,操作如下:

       \hat x^{(k)}= \frac { x^{k}-E[ x^{(k)}]}{\sqrt {Var[x^{(k)}]}} , x=(x^{1}... x^{d} )

       其次,标准化以后,隐藏层的表示会发生变化,为了解决这个问题,作者又加入了一个仿射变化,可以将激活后的数据恢复出来,具体操作如下:

       y^{(k)} = \gamma ^{(k)} \hat x^{(k)} +\beta ^{(k)}\gamma\beta 都是需要学习的。

       利用仿射变换,能够恢复出原隐藏层的表示。

       网上关于这个讨论挺多的,目前说法不一,我也不太懂,目前的操作流程是这样的:1.标准化,2.激活,3.仿射恢复。

       我们先标准化,激活以后利用仿射变换恢复出原来的分布,这样看起来好像是做了无用功,因为分布最后还是会恢复成原来的分布,那为什么还要多此一举呢?网上的解释是这种操作增强了模型的表示能力,看了很多博客以后我想谈谈自己的看法:

       1.我们标准化的目的是为了加快模型的训练,因为分布不一致会减慢模型的训练,降低泛化能力,标准化以后数据的分布处于激活函数的导数绝对值大的地方,这样反向传播的时候浅层神经网络可以得到训练,避免梯度消失。

       2.标准化的副作用是隐藏层的表示发生了变化,会影响到下一层的训练,所以我们需要恢复出原来的分布,这样就不会对神经网络的下一层产生影响。

       总结起来就是,利用标准化和仿射变换,使得当前层的训练加快,又不会影响下一层的训练,于是,模型的训练被加快,模型的表示能力得到强化。

       标准化的过程依赖于整个训练集,但是当使用随机梯度优化的时候不会用到整个训练集,于是,利用每次训练的小批样本来进行标准化,具体操纵如下:

       B=\{x_1,x_2,...,x_m\}, B为mini-batch, 大小为m。

       

       \epsilon是一个常量,用来弥补\sigma _B ^2 过小而带来的影响。

       利用反向传播算法推导损失,如图

    3.利用Batch Normalization来进行训练和测试

       

        训练的过程中,先利用标准化操作得到的新数据训练BN网络,同时还要优化BN层中的scale和shift参数,然后将训练好的BN网络用于inference, inference的时候利用mini-batch中的数据的方差和期望代替测试数据的方差与期望。

        BN还可以用于卷积神经网络,只不过原先mini-batch(大小为m)会加入大小为p*q的feature map, 所以,新的mini-batch变为m*p*q,每一组feature map都会有新的\gamma\beta参数。

        BN的好处:

        1.加快训练。BN层可以使用更高的学习率。

        2.防止过拟合。BN层可以避免网络模型的过拟合问题。

三、效果

        我用简单的三层神经网络测试了一下,效果还是很不错的。

        无BN层时,

       有BN层时,

        优化方式都是sgd,采用的是简单的三层神经网络模型。不添加BN层时优化的很慢,难以收敛,加入BN层以后优化得更稳定,而且准确率更高。

 

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值