之前花费了比较长的时间在论文阅读上,导致最近的博客都没有产出~~
参考论文:Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift
可以理解为对深层神经网络每个隐层神经元的激活值做简化版本的白化操作
1. 深度网络的训练速度慢,这是由于Internal Covariate Shift所导致的
Internal Convariate Shift:多层网络训练中,后面层次的神经网络接受到的节点受到前面层次的网络参数变化导致该层输入数据的分布发生了变化.泛指,在多层数据网络中各层之间参数变化引起的数据分布发生变化的现象, 发生在深层网络的隐层.
Internal Convariate Shift导致越到网络深处而导致梯度越小,且梯度下降的方向是东拐拐,西拐拐,从而导致到达最优点的时间越长,爬到山顶越慢。
2. 参数不好调, 其中一个参数就是学习率,我们可能要尝试很多次,才能找到合适的学习率,学习率太小,步子太小,训练速度会变慢,学习率过大,又会导致错过最优点。
而Batch Normation的出现就是为了解决1,2问题的,但是问题2出现的本质其实还是由于Internal Convariate Shift所导致的,所以Batch Normation的核心思想其实就是在每一个Layer都进行归一化,将其归一到标准正态分布上。
再说Batch Normation 之前,首先说一下问题1出现的前因后果。
在做非线形操作之前,在每一层的每个结点会计算x=wu+b,其中,u表示输入, 可以表示输入层,也可以表示上一层的输出作为这一层的输入。
假设x符合矩阵为均值为-2,方差为1的正态分布, 而经过BN(x)后会将其分布拉回到均值为0,方差为1的标准正态分布,为什么BN要将其拉回到标准正态分布呢?
下图是标准正态分布:
从上图可以看出,若x符合标准正态分布, 那么x有64%的概率其值落在[-1,1]的范围内,在两个标准差范围内,也就是说95%的概率其值落在了[-2,2]的范围内,x是激活前的值, 假设激活函数为sigmoid(x),sigmoid函数如下图:
x的值有95%的概率落在[-2,2]之间,sigmoid(x)的导数为:G’=f(x)*(1-f(x)),因为f(x)=sigmoid(x)在0到1之间,所以G’在0到0.25之间。但是假设x不符合标准正态分布,符合均值为-6,方差是1的正态分布,那么意味着95%的值落在了[-8,-4]之间,那么对应的Sigmoid(x)函数的值明显接近于0,这是典型的梯度饱和区,在这个区域里梯度变化很慢,为什么是梯度饱和区?
请看下sigmoid(x)如果取值接近0或者接近于1的时候对应导数函数取值,接近于0,意味着梯度变化很小甚至消失。而假设经过BN后,均值是0,方差是1,那么意味着95%的x值落在了[-2,2]区间内,很明显这一段是sigmoid(x)函数接近于线性变换的区域,意味着x的小变化会导致非线性函数值较大的变化,也即是梯度变化较大,对应导数函数图中明显大于0的区域,就是梯度非饱和区。
下图为sigmoid的导数图
经过BN后,目前大部分Activation的值落入非线性函数的线性区内,其对应的导数远离导数饱和区,这样来加速训练收敛过程。
但是
如果都通过BN,那么不就跟把非线性函数替换成线性函数效果相同了?这意味着如果是多层的线性函数变换其实这个深层是没有意义的,因为多层线性网络跟一层线性网络是等价的。这意味着网络的表达能力下降了,这也意味着深度的意义就没有了。所以BN为了保证非线性的获得,对变换后的满足均值为0方差为1的x又进行了scale加上shift操作(y=scale*x+shift),每个神经元增加了两个参数scale和shift参数,这两个参数是通过训练学习到的,意思是通过scale和shift把这个值从标准正态分布左移或者右移一点并长胖一点或者变瘦一点,每个实例挪动的程度不一样,这样等价于非线性函数的值从正中心周围的线性区往非线性区动了动。
核心思想应该是想找到一个线性和非线性的较好平衡点,既能享受非线性的较强表达能力的好处,又避免太靠非线性区两头使得网络收敛速度太慢。
根据上面的解释,我们只需要在每层激活前的值都加入一个BN层就可以了,所以,我们要计算均值,方差这两个统计量,然后在进行normalize就可以了,Batch Normalization统计数据就在batch上。假设mini-batch-size为m, 那么m个样本在经过l隐层的第j个结点时,可以产生m个xi(i = 1, 2···m),利用下面两个公式可计算m个样本的均值和统计量,算法1给出了Batch Normalizion
整个训练过程如下:
在训练阶段,可以训练出,其中是训练参数,在训练完成后测试阶段是已知的,在测试阶段,直接将对应的x和相关的统计量代入即可。
在测试阶段,没有batch的概念,在计算均值和方差时用的是整个测试集的均值和方差,测试集中的样本共享均值和方差。
直接将测试集的x代入
BN在原文章放置在x = wu+b之后,即BN(x=wu),偏置是不需要bn的。另外一种方式放在激活值之后,即BN(g(x)),g为激活函数。有实验表明,第二种方式的效果更好
1. 加快训练速度,调参更容易,可以将学习率调大些。
2. 起到了drop out的作用,类似一种正则表达式。
从上面的介绍能明显感受到BN存在的问题,
1. BN依赖于Batch的大小,如果Batch偏小,会导致基于batch计算的两个统计量是不准确的,当batch size = 1时BN无法起作用,这也导致batch normalization不能应用online learning tasks(参见layer normalization)。
2. 每次迭代中引进了随机性,使梯度增加了noise,这一点使得BN对于generative model(例如lstm网络)或者reinforcement learning(例如DQN)都是不适用的。