©PaperWeekly 原创 · 作者 | 海晨威
研究方向 | 自然语言处理
Norm,也即 Normalization,已经是深度神经网络模型中非常常规的操作了,但它背后的实现,原理和作用等,其实我们可以理解的更细致,本文会以最常用的 BatchNorm 和 LayerNorm 为例(其他 Norm 方法大同小异),通过 Q&A 的形式,去深入理解关于 Norm 的细节知识点。
  1. BN 在训练和测试时的差异
  2. BN 中的移动平均 Moving Average 是怎么做的?
  3. 移动平均中 Momentum 参数的影响
  4. Norm 中的标准化、平移和缩放的作用
  5. 不同 Norm 方法中都有哪些参数要保存?
  6. BN 和 LN 有哪些差异?
  7. 为什么 BERT 使用 LN,而不使用 BN?
  8. 如何去理解在哪一个维度做 Norm?
BN在训练和测试时的差异
对于 BN,在训练时,是对每一个 batch 的训练数据进行归一化,也即用每一批数据的均值和方差。
而在测试时,比如进行一个样本的预测,就并没有 batch 的概念,因此,这个时候用的均值和方差是在训练过程中通过滑动平均得到的均值和方差,这个会和模型权重一起,在训练完成后一并保存下来。
对于 BN,是对每一批数据进行归一化到一个相同的分布,而每一批数据的均值和方差会有一定的差别,而不是用固定的值,这个差别实际上也能够增加模型的鲁棒性,并会在一定程度上减少过拟合。
但是一批数据和全量数据的均值和方差相差太多,又无法较好地代表训练集的分布,因此,BN 一般要求将训练集完全打乱,并用一个较大的 batch 值,去缩小与全量数据的差别。
BN中的移动平均Moving Average是怎么做的?
训练过程中的每一个 batch 都会进行移动平均的计算 [1] 
moving_mean
 = moving_mean * momentum + batch_mean * (
1
 - momentum)

moving_var
 = moving_var * momentum + batch_var * (
1
 - momentum)
式中的 momentum 为动量参数,在 TF/Keras 中,该值为 0.99,在 Pytorch 中,这个值为 0.9 初始值,moving_mean=0,moving_var=1,相当于标准正态分布。
在实际的代码中,滑动平均的计算会以下面这种更高效的方式,但实际上是等价的:
moving_mean
 -= (moving_mean - batch_mean) * (
1
 - momentum)

moving_var -= (moving_var - batch_var) * (
1
 - momentum)

移动平均中Momentum参数的影响
整个训练阶段滑动平均的过程,(moving_mean, moving_var)参数实际上是从正态分布,向训练集真实分布靠拢的一个过程。
理论上,训练步数越长是会越靠近真实分布的,实际上,因为每个 batch 并不能代表整个训练集的分布,所以最后的值是在真实分布附近波动。
一个更小的 momentum 值,意味着更大的更新步长,对应着滑动平均值更快的变化,能更快地向真实值靠拢,但也意味着更大的波动性,更大的 momentum 值则相反。
训练阶段使用的是(batch_mean, batch_var),所以滑动平均并不会影响训练阶段的结果,而是影响预测阶段的效果 。
如果训练步数很短,一个大的 momentum 值可能会导致(moving_mean, moving_var)还没有靠拢到真实分布就停止了,这样对预测阶段的影响是很大的,也会是欠拟合的一个状态。如果训练步数足够,一个大的 momentum 值对应小的更新步长,最后的滑动平均的值是会更接近真实值的。
如果 batch size 比较小,那单个 batch 的(batch_mean, batch_var)和真实分布会比较大,此时滑动平均单次更新的步长就不应过大,适用一个大的 momentum 值,反之可类比分析。
Norm中的标准化、平移和缩放的作用
对于一般的神经网络,它有这样一些问题 [2] 
其一,上层参数需要不断适应新的输入数据分布,降低学习速度。
其二,下层输入的变化可能趋向于变大或者变小,导致上层落入饱和区,使得学习过早停止。
其三,每层的更新都会影响到其它层,因此每层的参数更新策略需要尽可能的谨慎。
Norm 的使用,可以很好的改善这些问题,它包括两个操作:标准化 + 平移和缩放(注:本文中所有的 Norm 也都代指这两个操作)。
首先,抛开神经网络的背景,标准化这个操作,可以消除不同数据之间由于量纲/数量级带来的差异,而且它是一种线性变换,并不会改变原始数据的数值排序关系。
经过标准化,每层神经网络的输出变成了均值为 0,方差为 1 的标准分布,不再受到下层神经网络的影响(注意是数据分布不受影响,但具体的值肯定还是依赖下层神经网络的输出),可以有效改善上述的几个问题。
那平移和缩放的作用在哪里呢?主要还是为了:保证模型的表达能力不因为标准化而下降
在 Norm 中引入的两个新参数 γ 和 β,可以表示旧参数作为输入的同一族函数,但是新参数有不同的学习动态。在旧参数中,x 的均值和方差取决于下层神经网络的复杂关联;但在新参数中,x 的均值和方差仅由  γ 和 β 来确定,去除了与下层计算的密切耦合。新参数很容易通过梯度下降来学习,简化了神经网络的训练。
如果直接只做标准化不做其他处理,神经网络是学不到任何东西的,因为标准化之后都是标准分布了,但是加入这两个参数后就不一样了。
先考虑特殊情况,如果 γ 和 β 分别等于此 batch 的标准差和均值,那么 y 不就还原到标准化前的 x 了吗,也即是缩放平移到了标准化前的分布,相当于 Norm 没有起作用。这样就保证了每一次数据经过 Norm 后还能保留学习来的特征,同时又能完成标准化这个操作,从而使当前神经元的分布摆脱了对下层神经元的依赖。
注:该问题主要参考 [2],讲解非常清晰和系统,非常值得一看。
不同Norm方法中都有哪些参数要保存?
BN 的参数包括:
1)每个神经元在训练过程中得到的均值和方差,通过移动平均得到
2)每个神经元上的缩放参数 γ 和平移参数 β
LN 只包括上面的第 2 部分参数,因为它和 batch 无关,无需记录第 1 部分参数。
BN和LN有哪些差异?
1)两者做 Norm 的维度不一样,BN 是在 Batch 维,而 LN 一般是在最后一维。
2)BN 需要在训练过程中,滑动平均累积每个神经元的均值和方差,并保存在模型文件中用于推理过程,而 LN 不需要。
3)因为 Norm 维度的差异,使得它们适用的领域也有差异,BN 更多用于 CV 领域,LN 更多用于 NLP 领域。
为什么Transformer/BERT使用LN,而不使用BN?
最直白的原因,还是因为用 LN 的效果更好,这个在 Transformer 论文中作者有说到。但背后更深层次的原因,这个在知乎上有广泛的讨论 [3],不过并没有一个统一的结论,这里我摘录两个我相对认可的回答 [4][5]
图像数据是自然界客观存在的,像素的组织形式已经包含了“信息”,而 NLP 数据不一样,网络对 NLP 数据学习的真正开端是从'embedding'开始的,而这个'embedding'并不是客观存在,它也是通过网络学习出来的。
下面只从直观理解上从两个方面说说个人看法: 
1. layer normalization 有助于得到一个球体空间中符合 0 均值 1 方差高斯分布的 embedding,batch  normalization不具备这个功能;
2. layer normalization 可以对 transformer 学习过程中由于多词条 embedding 累加可能带来的“尺度”问题施加约束,相当于对表达每个词一词多义的空间施加了约束,有效降低模型方差。batch normalization 也不具备这个功能。 
emmbedding 并不存在一个客观的分布,那我们需要考虑的是:我们希望得到一个符合什么样分布的 embedding? 
很好理解,通过 layer normalization 得到的 embedding 是以坐标原点为中心,1 为标准差,越往外越稀疏的球体空间中。
说简单点,其实深度学习里的正则化方法就是“通过把一部分不重要的复杂信息损失掉,以此来降低拟合难度以及过拟合的风险,从而加速了模型的收敛”。Normalization 目的就是让分布稳定下来(降低各维度数据的方差)。 
不同正则化方法的区别只是操作的信息维度不同,即选择损失信息的维度不同。 
在 CV 中常常使用 BN,它是在 NHW 维度进行了归一化,而 Channel 维度的信息原封不动,因为可以认为在 CV 应用场景中,数据在不同 channel 中的信息很重要,如果对其进行归一化将会损失不同 channel 的差异信息。 
而 NLP 中不同 batch 样本的信息关联性不大,而且由于不同的句子长度不同,强行归一化会损失不同样本间的差异信息,所以就没在 batch 维度进行归一化,而是选择 LN,只考虑的句子内部维度的归一化。可以认为 NLP 应用场景中一个样本内部维度间是有关联的,所以在信息归一化时,对样本内部差异信息进行一些损失,反而能降低方差。 
总结一下:选择什么样的归一化方式,取决于你关注数据的哪部分信息。如果某个维度信息的差异性很重要,需要被拟合,那就别在那个维度进行归一化。
如何去理解在哪一个维度做Norm?
以 BERT 每一层 bert_tensor 的维度:[batch_size, seq_len, hidden_size] 为例:
BN 是在 batch_size 维做 Norm,则:
for i in range(seq_len):

    for j in range(hidden_size):

        Norm([
bert_tensor[k
][
i
][
j
] for k in range(batch_size)])
LN是在 hidden_size 维做 Norm,则:
for i in range(batch_size):

    for j in range(seq_len):

        Norm([
bert_tensor[i
][
j
][
k
] for k in range(hidden_size)])

也就是说,对哪个维度做 Norm,就在其他维度不动的情况下,基于该维度下的所有元素计算均值和方差,然后再做 Norm。
对于矩阵中维度还有疑惑的可以参考 [6] 
Norm,一个简单的操作,让神经网络的训练更加的高效。了解它背后更多的实现,原理和作用,能让你使用的更加 知其所以然。
参考文献
[1] https://jiafulow.github.io/blog/2021/01/29/moving-average-in-batch-normalization/
[2] https://zhuanlan.zhihu.com/p/33173246
[3] https://www.zhihu.com/question/395811291
[4] https://www.zhihu.com/question/395811291/answer/1260290120
[5] https://www.zhihu.com/question/395811291/answer/1251829041
[6] https://zhuanlan.zhihu.com/p/242086547
更多阅读
#投 稿 通 道#
 让你的文字被更多人看到 
如何才能让更多的优质内容以更短路径到达读者群体,缩短读者寻找优质内容的成本呢?答案就是:你不认识的人。
总有一些你不认识的人,知道你想知道的东西。PaperWeekly 或许可以成为一座桥梁,促使不同背景、不同方向的学者和学术灵感相互碰撞,迸发出更多的可能性。 
PaperWeekly 鼓励高校实验室或个人,在我们的平台上分享各类优质内容,可以是最新论文解读,也可以是学术热点剖析科研心得竞赛经验讲解等。我们的目的只有一个,让知识真正流动起来。
📝 稿件基本要求:
• 文章确系个人原创作品,未曾在公开渠道发表,如为其他平台已发表或待发表的文章,请明确标注 
• 稿件建议以 markdown 格式撰写,文中配图以附件形式发送,要求图片清晰,无版权问题
• PaperWeekly 尊重原作者署名权,并将为每篇被采纳的原创首发稿件,提供业内具有竞争力稿酬,具体依据文章阅读量和文章质量阶梯制结算
📬 投稿通道:
• 投稿邮箱:[email protected] 
• 来稿请备注即时联系方式(微信),以便我们在稿件选用的第一时间联系作者
• 您也可以直接添加小编微信(pwbot02)快速投稿,备注:姓名-投稿
△长按添加PaperWeekly小编
🔍
现在,在「知乎」也能找到我们了
进入知乎首页搜索「PaperWeekly」
点击「关注」订阅我们的专栏吧
·
继续阅读
阅读原文