这几天正在看BN的论文,最近也经常看到关于BN理解的文章,在目前研究的课题中BN也是极其重要的,所以就抽出一段时间认真地研究了一下Batch Normalization的原理,以下为参考网上的几篇文章总结得出的。
文章目录
-
论文原文:Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift - 原文
-
tensorflow中的函数解析——tf.nn.batch_normalization()函数解析
-
tensorflow实现代码——【超分辨率】TensorFlow中批归一化的实现——tf.layers.batch_normalization()函数
一、初识BN
我们今天的主角:批归一化,或者说是批标准化,英文名字 “”,出自一篇非常值得学习的文献————Sergey Ioffe,Christian Szegedy在2015年发表的论文《Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift》也就是网络中使用的一种,常用缩写为。
近年来深度学习异常火爆,并且在视觉、语音和其他诸多领域屡创佳绩,随机梯度下降()成了训练深度神经网络的主流方法。虽然对于训练深度神经网络来说是简单高效的,但它被人诟病的地方是,需要对模型的超参数进行仔细地选择,比如:权重衰减系数、比例、特别是优化中使用的学习率以及模型的参数初始化。由于每个层的输入都受到前面所有层的参数影响,因此,当网络变得更深时,网络参数的微小变化也会被逐渐放大,这使得训练变得越来越复杂,收敛越来越慢。这是一个深度学习领域的接近本质的问题,已经有很多论文提出了解决方法,比如:激活函数,残差网络等等。本质上也是其中一种目前被大量使用的解决方法。
是一个深度神经网络的训练技巧,它不仅可以加快了模型的收敛速度,而且更重要的是在一定程度缓解了深层网络中“梯度弥散”的问题,从而使得深层网络模型的训练更加容易和稳定。
在开始讲解算法之前,我们先来思考几个问题:
我们都知道在神经网络训练开始之前,都需要对输入数据做一个归一化处理,那么为什么要归一化呢?BN的作用到底是什么?
-
深度神经网络的学习过程的本质是什么呢,这个问题现阶段没办法给出准确的答案,知乎上曾经有人讨论过这个问题,《关于深度学习的本质和优缺点大家说下自己的理解?》,有兴趣的童鞋可以去看一看,个人比较赞成这位仁兄的看法。
在我目前的水平看来,深度学习的本质上就是刻画问题的内部复杂结构特征,进行任意的非线性变换,简单来说可以看成是学习数据的分布(个人看法,勿喷)。机器学习领域有个很重要的假设:独立同分布假设【数据的独立同分布(Independent Identically Distributed)】————假设训练数据和测试数据是满足相同分布的,这是通过训练数据获得的模型能够在测试集获得好的效果的一个基本保障。如果训练数据与测试数据的分布不同,那么网络的泛化能力就会大大降低。另一方面,如果每批训练数据的分布各不相同,网络就要在每次迭代时都去学习适应不同的分布,那么网络的训练速度就会大大降低,这就是为什么我们需要对输入数据做一个归一化处理。 -
那的作用到底是什么呢?根据刚才提到的机器学习领域的独立同分布假设,我们可以发现的作用就是在深度神经网络训练过程中使得每一层神经网络的输入均保持相同分布。
二、与BN相知
1、“Internal Covariate Shift”问题
从论文的名字《Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift》中我们可以看出是提出的一种解决“”问题的算法,那么什么是“”?
我们在最开始的时候提到过,论文也是首先提到了相对于的两个优点:梯度更新方向更准确;并行计算速度快。为什么要说,因为我们的是基于的一种解决方法,的训练缺点是:超参数调试困难。(那是不是说如果和结合就perfect了(✧◡✧))
大家都知道在统计机器学习中的一个经典假设是“源空间(source domain)和目标空间(target domain)的数据分布(distribution)是一致的”。如果不一致,那么就出现了新的机器学习问题,如,transfer learning/domain adaptation等。而就是分布不一致假设之下的一个分支问题,它是指源空间和目标空间的条件概率是一致的,但是其边缘概率不同,即:对所有,
但是,
大家细想便会发现,的确,如果深度神经网络开始训练,那么各个参数就都要发生更新,除了输入层的数据外(因为我们已经人为地为输入层数据的每个样本都进行了归一化),后面网络每一层的输入数据的分布是不停地在发生变化的,因为在训练的时候,前面层训练参数的更新将导致后面层输入数据的分布产生变化。对于深度神经网络这种包含许多隐层的网络结构来说,在训练过程中,由于各层参数在不停地变化,所以每个隐层都会面临的问题,由于是对隐层间的信号的分析,也即是“”的来由。也就是说在训练过程中,隐层的输入分布老是变来变去,这就是所谓的“”。
2、BN的基本思想
算法的基本思想其实相当地直观:因为深层神经网络在做非线性变换前的激活输入值(就是那个,U是输入),随着网络结构深度的增加,在训练过程中其分布逐渐发生偏移或者变动。训练速度变慢的原因,一般是整体分布逐渐往非线性函数的取值区间的上下限两端靠近(对于函数来说,意味着激活输入值是绝对值大的正值或负值,如下图两个红色剪头的指向),所以这导致反向传播时低层神经网络的梯度消失(数学导数),这是训练深层神经网络收敛越来越慢的本质原因,
而就是通过一定的规范化手段,把每层神经网络任意神经元这个输入值的分布强行拉回到均值为0方差为1的标准正态分布,其实就是把越来越偏的分布强制拉回比较标准的分布,这样使得激活输入值落在非线性函数对输入比较敏感的区域,这样输入的小变化就会导致损失函数较大的变化,意思是这样让梯度变大,避免梯度消失问题产生,而且梯度变大意味着学习收敛速度快,能大大加快训练速度。
简单来说就是说,对于每个隐层神经元,把逐渐向非线性函数映射后向取值区间极限饱和区靠拢的输入分布强制拉回到均值为0方差为1的比较标准的正态分布,使得非线性变换函数的输入值落入对输入比较敏感的区域,以此避免梯度消失问题。保持梯度在一直比较大的位置,很明显这样的参数调整效率更高,也就是损失函数最优化的步子更大,速度更快。(这也是在很多情况下为什么比好一些的原因,因为大于0时的梯度一直是1)
下面我们通过数学方法来分析这种调整有什么意义。
上图对应几个正态分布。
假设某个隐层神经元原先的激活输入,符合均值是-2,方差是0.5的正态分布(在上图中对应最左端的浅绿色曲线),通过后转换为均值为0,方差是1的正态分布(在上图中对应的深红色图形)。
这意味着什么?意味着输入x的正态分布整体向右平移2个单位(对应正态分布中均值平移的变化),图形曲线更扁平了(对应正态分布中方差增大的变化)。通过这个图我们可以看出,其实就是把每个隐层神经元的激活输入分布从非【均值为0方差为1的正态分布】通过平移(增大/减小均值)、压缩/拉伸(增大/减小方差)曲线尖锐程度,调整为【均值为0方差为1的正态分布】。
那么把激活输入调整到这个【均值为0方差为1的正态分布】有什么用嘛?首先我们看下均值为0,方差为1的标准正态分布代表什么含义:
上图对应均值为0方差为1的标准正态分布图。
这意味着68%概率分布在 [-1,1] 的范围内,95%概率分布在 [-2,2] 的范围。假设非线性函数是,那么看下其函数图像:
导数具体的推导过程如下:
因为在0到1之间,所以的导数在0到0.25之间,其对应的函数图像如下:
现在我们假设没有经过调整前x的原先正态分布均值是6,方差是1,在 [均值 - 方差2,均值 + 方差2] 范围内的概率是95%,那么也就意味着落在 [4,8] 之间概率是95%,那么对应的函数的值明显接近于1,在上面我们说过这个位置是典型的梯度饱和区,所以在这个区域里梯度变化很慢。
为什么这个位置是梯度饱和区?请看下函数图像,如果横坐标(函数值)取值接近0或者接近于1的时候对应纵坐标(导数函数值)取值,接近于0,意味着梯度变化很小甚至消失。而假设经过后,均值是0,方差是1,那么意味着95%的值落在了 [-2,2] 区间内,很明显这一段是函数接近于线性变换的区域,意味着的小变化会导致非线性函数值产生较大的变化,也即是梯度变化较大,对应导数函数图中明显大于0的区域,就是梯度非饱和区。
从上面提到的过程中学到了什么呢?BN的作用其实简单来说就是把隐层神经元激活输入从各种奇怪的非【均值为0方差为1的正态分布】拉回到【均值为0方差为1的正态分布】。
但是又出现了一个问题,如果都通过实现归一化的话,不就是把非线性函数替换成线性函数了?这意味着什么呢?这就要说到我们深度学习的学习过程是怎么实现的了,即通过多层非线性实现一个泛化的过程。如果是多层的线性函数其实这个深层就没有任何的意义,因为多层线性网络和一层线性网络是等价的。这意味着网络的表达能力下降了,也就是意味着深度的意义就没有了。所以为了保证非线性的获得,对变换后的满足【均值为0方差为1的正态分布】的x又进行了操作。每个神经元增加了两个参数和,这两个参数通过训练学习到的,关键就是在线性和非线性之间找到一个比较好的平衡点,既能享受非线性的较强表达能力的好处,又避免太靠非线性区两头使得网络收敛速度太慢。最不济的情况,这两个参数可以等效成最开始的状态,也就是训练数据集本来的特征,这只是需要简单的计算就可以实现。
3、预处理操作
论文中提到了需要对神经网络输入数据进行预处理操作,众所周知最好的算法莫过于白化预处理。如果对网络的输入进行白化预处理,网络的训练将会收敛地更快——即输入线性变换为具有零均值和单位方差,并且去除了相关性。然而白化计算量太大了,代价昂贵,还有就是白化不是处处可微的,所以在深度学习中,其实很少用到白化,因此使用了下面的公式进行预处理,可以理解为对深层神经网络每个隐层神经元的激活值做简化版本的白化操作。
要注意,这里某一层的某个神经元的x(k)不是指该层的原始输入,即不是上一层每个神经元的输出,而是该层这个神经元的线性激活,这里的才是上一层神经元的输出。变换的意思是:某个神经元对应的原始的激活x通过减去内个实例获得的个激活x求得的均值并除以开方求得的方差来进行转换。
上文说过经过这个变换后某个神经元的激活x形成了均值为0,方差为1的正态分布,目的是把值往后续要进行的非线性变换的线性区拉动,增大导数值,增强反向传播信息流动性,加快训练收敛速度。
4、BN算法的实现
这个时候你可能会觉得真是好简单,不就是在网络中间对数据做一个归一化处理?然而其实并不是那么简单的。如果是仅仅使用上面的归一化公式,对网络某一层A的输出数据做归一化,然后送入网络下一层B,这样是会影响到本层网络A所学习到的特征的,这样会导致网络表达能力下降。比如:网络中间某一层A学习到特征数据本身就分布在S型激活函数的两侧,你强制把它给归一化处理了、标准差也限制在了1,把数据变换成分布于s函数的中间部分,这样就相当于搞坏了这一层网络所学习到的特征分布。为了防止这一点,论文提出了一个解决方法,在每个神经元上增加两个调节参数(和),这两个参数是通过训练来学习到的,用来对变换后的激活反变换,使得网络表达能力增强,即对变换后的激活进行如下的和操作,这其实是变换的反操作:
每一个神经元都会有这么一对这样的参数:
网络层的前向传导过程公式就是:
上面的公式中的ϵ是一个为了数值稳定,加到小批量数据方差上的常量,指的是。
在训练过程中我们需要通过这个变换反向传播损失 的梯度,以及计算关于变换参数的梯度。我们使用的链式法则如下(简化之前):
5、BN的推理过程
算法在训练时的操作就如我们上面所说,首先提取每次迭代时的每个的平均值和方差进行归一化,再通过两个可学习的变量恢复要学习的特征。 但是在实际应用时就没有了,那么算法怎样进行归一化呢?实际上在测试的过程中,算法的参数就已经固定好了,首先进行归一化时的平均值和方差分别为:
有了均值和方差,每个隐层神经元也已经有对应训练好的参数和参数,就可以在推导的时候对每个神经元的激活数据计算进行变换了,在推理过程中进行BN采取如下方式:
可以看的出这个公式其实和训练时
是等价的。
三、BN的相惜
BN优点:
-
神经网络本质是学习数据分布,如果寻来你数据与测试数据分布不同,网络的泛化能力将降低,就是通过对每一层的计算做和的方法,通过规范化手段,把每层神经网络任意神经元这个输入值的分布强行拉回到均值为0、方差为1的分布,减小其影响,让模型更加健壮。
-
可以使用更高的学习率。如果每层的不一致,实际上每层需要的学习率是不一样的,同一层不同维度的往往也需要不同大小的学习率,通常需要使用最小的那个学习率才能保证损失函数有效下降,将每层、每维的保持一致,那么我们就可以直接使用较高的学习率进行优化。
-
移除或使用较低的。 是常用的防止的方法,而导致的位置往往在数据边界处,如果初始化权重就已经落在数据内部,现象就可以得到一定的缓解。论文中最后的模型分别使用10%、5%和0%的训练模型,与之前的40%-50%相比,可以大大提高训练速度。
-
取消层。 由于使用了一种,再使用就显得没那么必要了。而且实际上也没那么。
-
减少图像扭曲的使用。 由于现在训练数降低,所以要对输入数据少做一些扭曲,让神经网络多看看真实的数据。
四、不如常相忆BN
这一章是看了知乎大佬文章后的总结。
所以最后的最后到底什么是?
在文中描述的非常清晰,即在每次时,通过来对相应的做归一化操作,使得结果(输出信号各个维度)的均值为0,方差为1。而最后的“”操作则是为了让因训练所需而“刻意”加入的能够有可能还原最初的输入,从而保证整个的。
有关的解释:实际上可以看作是在原模型上加入的“新操作”,这个新操作很大可能会改变某层原来的输入。当然也可能不改变,不改变的时候就是“还原原来输入”。如此一来,既可以改变同时也可以保持原输入,那么模型的容纳能力()就提升了。
如果想要更多的资源,欢迎关注 @我是管小亮,文字强迫症MAX~
回复【福利】即可获取我为你准备的大礼,包括C++,编程四大件,NLP,深度学习等等的资料。
想看更多文(段)章(子),欢迎关注微信公众号「程序员管小亮」~