Skip to content

Batch Normalization

对于深层的神经网络, 一次可能会同时更新好几个层

例如一个每层只有一个单元的神经网络, 输出为

\[y=x w_1w_2\cdots w_l\]

即每一层的边权相乘

这样如果使用 \(w\leftarrow w-\epsilon g\) 更新每一层, 更新后的结果为

\[y=x(w_1-\epsilon g_1)(w_2-\epsilon g_2)\cdots (w_l-\epsilon g_l)\]

其中 \(g_i=\nabla_{w_i}y=\prod_{j\not=i}w_j\)

可以看到新的 \(y\) 中, 如果 \(x\) 变化为 \(1\), 只包含一个 \(g_i\) 的项为 \(\epsilon g_i\times \prod_{j\not=i}w_j=\epsilon g_i^2\)

所以总的变化量为 \(\epsilon g^Tg\), 其中 \(g=[g_1,\cdots,g_l]\)

所以要想让 \(y\) 也变化 \(1\), 只需让 \(\epsilon=\frac 1{g^Tg}\)

但是对于包含 \(2\) 个及以上个 \(g_i\) 的项, 其中也含有 \(\prod w_i\)

但是这些项可能由于 \(w_i<1\) 变得极小, 或者 \(w_i>1\) 变得极大, 导致梯度消失 / 爆炸

所以学习率不好选择

同时由于梯度 \(g_i=\prod_{j\not=i}w_j\), 所以更新后的参数 \(w_i'\) 与其他参数产生耦合, 会比较麻烦

定义一个 design matrix \(H\) 中, 每个神经元的输出组成一行, 一共 \(m\) 行, \(m\) 为 mini-batch 的大小

我们对其进行归一化:

\[H'=\frac{H-\mu}{\sigma}\]

\[H_{i,j}=\frac{H_{i,j}-\mu_j }{\sigma_j}\]

其中 \(\mu\) 是一个包含所有神经元输出平均值的向量, \(\sigma\) 是包含所有神经元输出标准差的向量

具体地

\[\mu_j=\frac 1m\sum_{i=1}^mH_{i,j}\]
\[\sigma_j=\sqrt{\delta + \frac{1}{m}\sum_{i=1}^m(H_{i,j}-\mu_j)^2}\]

这样能够让每一层输出都服从平均值为 \(0\), 方差为 \(1\)

但这样的归一化可能导致模型表达能力下降

我们可以用 \(\gamma H'+\beta\) 代替 \(H'\) 作为每一层的输出

参数 \(\gamma, \beta\) 可以让输出的平均值与方差为任意值

这种先归一化再改变平均值和方差的操作其实不是冗余的

归一化消除了平均值与方差对其他层的依赖, 然后再用参数来提高模型表达能力

新的输出的均值与方差仅由 \(\gamma, \beta\) 控制, 使得其更容易学习

有一个问题, 我们应该对输入 \(x\)\(BN\), 还是对 \(Wx+b\), 即激活值做 \(BN\)?

答案是后者更好

因为输入 \(x\) 通常是某个非线性激活函数的输出, 即 \(\text{sigmoid,ReLU}\)

所以其统计特性更不符合高斯分布, 也不容易用线性变换标准化

具体地, 我们对激活函数传入的是 \(BN(Wx)\)

省略了 \(b\), 因为 \(b\) 的效果与 \(\beta\) 相同

同时, 在推理时, 由于推理没有 batch, 所以 BN 需要对每一层维护一个 running average

定义 \(\mu_{i,running}, \sigma_{i,running}\)

对于每一层 \(i\), 每一批次 \(B\)\(\mu_{i,B},\sigma_{i,B}\), 我们有

\[\mu_{i,running}=\lambda \mu_{i,running} + (1-\lambda) \mu_{i,B}\]
\[\sigma_{i,running}^2=\lambda \sigma_{i,running}^2 + (1-\lambda) \sigma_{i,B}^2\]

其中 \(\lambda\) 为动量参数, 通常取 \(0.9-0.99\)

这样推理时每一层直接用 \(\mu_{i,running}, \sigma_{i,running}\) 来计算

Weight Normalization

对于神经网络 \(y=\phi(w\cdot x + b)\)

\[w=\frac{g}{||v||}v\]

在只有一层的神经网络中:

\[y=v\cdot x\]

此时于 BN 相同, 对应了 \(\mu=0,\sigma = ||v||\)

重点是将权重的大小与方向解耦, 使得训练时可以分别调整 \(g,v\), 加速收敛

同时由于不依赖批次, 可以用于在线学习, 如 RNN

缺点是需要更细致的参数初始化

同时 BN 解决了内部协变量偏移 (internal covariate shift)

WN 没有这个特性

Layer Normalization

上面说的 Batch Normalization 在前馈神经网络中效果很好

在前馈神经网络中层数固定, 比较方便对于每一层维护 running average

但如果是 RNN, 层数相当于时间步数, 依赖于序列长度

所以需要维护变化数量的独立的 running average

同时 BN 不能用于在线学习

因为在线学习一般 batch = 1

而且需要训练的同时实时推理, 用 running average 可能更新较慢, 需要等一个 batch 结束后才更新

所以 LN 对这一层的所有神经元输出做标准化

对于上面的输出 \(H_{i,j}\):

\[\mu_i=\frac 1n\sum_{i=1}^nH_{i,j}\]
\[\sigma_i=\sqrt{\delta + \frac{1}{n}\sum_{i=1}^n(H_{i,j}-\mu_i)^2}\]

其中 \(n\) 是神经元数量, 即输入 \(x\) 的维数

对于 RNN, 更新隐藏状态的式子:

\[h_t=\sigma(\alpha_t+\beta)\]

其中 \(\alpha_t=W_h\cdot h_{t-1}+W_x\cdot x_t\)

变成:

\[h_t=\sigma(\gamma \frac{\alpha_t-\mu_t}{\sigma_t}+\beta)\]

其中 \(\gamma, \beta\) 与 BN 作用相同

invariant

alt text


下面介绍两个在图像处理里的 Normalization

Instance Normalization

Group Normalization

alt text

图中 \(N\) 是 batch 大小, \(C\) 是通道数, \(H,W\) 是图像大小

所以这个图本来应该是 \(4\) 维的, 不过 \(H,W\) 可以平铺成一维, 表示图像数据

在 instance group 中的一个柱代表了数据的某一维, 即一个特征

对应之前的简单 FFN 来说, 就是一个神经元的输入输出现在都是长为 \(H\times W\) 的向量

一层有 \(C\) 个神经元, 传入 \(N\) 组数据 (一个 batch)

所以图中 BN 对所有 \(N\) 方向的数据做标准化, 即同一批次

LN 对所有 \(C\) 方向标准化, 即同一层

IN 对某一批次的某一维度标准化

GN 对某一批次的同一层数据分组标准化


参考:

  1. Deep Learning (Ian Goodfellow and Yoshua Bengio and Aaron Courville)
  2. Batch Normalization
  3. Layer Normalization

  4. Weight Normalization: A Simple Reparameterization to Accelerate Training of Deep Neural Networks