神经网络中归一化技术汇总

1 局部响应归一化(Local Response Normalization)[1]

1.1 概述

Local Response Normalization (LRN)是AlexNet中首次引入的归一化方法,但是在BatchNorm之后就很少使用这种方法了。

1.2 目的

对局部的值进行归一化操作,使其中比较大的值变得相对更大,增强了局部的对比度,在AlexNet中有1.2个百分比左右的提升

1.3 方法

LRN是一个non-trainable layer(非训练层),对局部区域的特征进行了平方归一化操作,在AlexNet中的公式如下:

\(b_{x,y}^i=a_{x,y}^i/(k+\alpha \sum_{j=max(0,i-n/2)}^{min(N-1,i+n/2)}(a_{x,y}^j)^2)^{\beta}\)

参数说明:需要手动设置的超参维\((k, \alpha, \beta, n)\)

  • i 表示第 i 个通道
  • x, y 表示要进行归一化的这个值的位置坐标
  • k 的作用是防止发生除0的情况
  • α 和 β 为常数
  • n 表示邻域的范围,边界情况用0补齐

1.4 样例

LRN根据归一化方向不同有两种形式,分别为 Inter-Channel LRN 和 Intra-Channel LRN

Inter-Channel LRN Example:

Inter-Channel LRN是AlexNet中使用的LRN形式,其中的邻域是定义在Channel维度上的

假设在以上的例子中我们的超参数设置为 (k,α,β,n)=(0,1,1,2),n=2表示当我们在对一个元素进行归一化的时候,只考虑到沿channel维度上(也就是一维的情况),这个元素的前一个元素后一个元素如果遇到边界情况,则用0补全。这意味着,我们在计算(i,x,y)这个点归一化后的值时,只需要考虑(i-1,x,y),(i,x,y)和(i+1,x,y)的值即可,如果超过边界则默认为0。

在以上例子中,不同颜色表示不同的通道,以第一个feature map中左上角的点为例,在归一化前它的值为1,归一化后为0.5,在计算这个值的时候我们要考虑到其前后的值,由于(i-1,x,y)是没有值的,也就是边界情况,所以我们默认为0,(i+1,x,y)为橙色feature map的左上角值=1。因此我们代入公式,可以得到归一化后的值为: \(b=1/(0+1*(0^2+1^2+1^2))^1=0.5\),以此类推。

Intra-Channel LRN Example:

我们采用同样的设置(k,α,β,n)=(0,1,1,2),在Intra-Channel LRN,由于是二维的情况,因此我们考虑的邻域范围为 (n+1)∗(n+1) ,当n=2的时候,在归一化时考虑的范围大小为一个3*3的size。可以代入公式计算得到上图中两个例子的值,分别为:

\(b=1/(0+1*(1^2+3^2+2^2+4^2)) \approx 0.03\) \(b=5/(0+1*(3^2+2^2+1^2+3^2+5^2+3^2+3^2+4^2+5^2)) \approx 0.05\)

Pytorch 源码中实现了LRN的操作,可以看到与Alexnet论文中的方法略有不同,α所乘的项由所有数值的平方和变成了所有数值的均值,但无论是使用均值还是平方和,两者都起到了 lateral inhibition(横向抑制)的作用[2]

def local_response_norm(input, size, alpha=1e-4, beta=0.75, k=1.):
    # type: (Tensor, int, float, float, float) -> Tensor
    r"""Applies local response normalization over an input signal composed of
    several input planes, where channels occupy the second dimension.
    Applies normalization across channels.

    See :class:`~torch.nn.LocalResponseNorm` for details.
    """
    dim = input.dim()
    if dim < 3:
        raise ValueError('Expected 3D or higher dimensionality \
                         input (got {} dimensions)'.format(dim))
    div = input.mul(input).unsqueeze(1)
    if dim == 3:
        div = pad(div, (0, 0, size // 2, (size - 1) // 2))
        div = avg_pool2d(div, (size, 1), stride=1).squeeze(1)
    else:
        sizes = input.size()
        div = div.view(sizes[0], 1, sizes[1], sizes[2], -1)
        div = pad(div, (0, 0, 0, 0, size // 2, (size - 1) // 2))
        div = avg_pool3d(div, (size, 1, 1), stride=1).squeeze(1)
        div = div.view(sizes)
    div = div.mul(alpha).add(k).pow(beta)
    return input / div

2 批归一化(Batch Normalization)[3,4]

Batch Normalization是2015年一篇论文中提出的数据归一化方法,往往用在深度神经网络中激活层之前。其作用可以加快模型训练时的收敛速度,使得模型训练过程更加稳定,避免梯度爆炸或者梯度消失。并且起到一定的正则化作用,几乎代替了Dropout。

2.1 BN核心公式

\(Input: B = \{ x_{1 \cdots m} \}; \gamma, \beta(parameters \quad to \quad be \quad learned) \\ Output: \{ y_i = BN_{\gamma, \beta} (x_i) \} \\ \mu_B \leftarrow \frac{1}{m} \sum_{i=1}^m x_i \\ \sigma_B^2 \leftarrow \frac{1}{m} \sum_{i=1}^m (x_i – \mu_B)^2 \\ \widetilde{x}_i \leftarrow \frac{x_i – \mu_B}{\sqrt{\sigma_B^2 + \epsilon}} \\ y_i \leftarrow \gamma \widetilde{x}_i + \beta \)

上述公式解释:

  • 输入为数值集合( B ),可训练参数 \(\gamma, \beta\)
  • BN的具体操作为:先计算 B 的均值和方差,然后执行\(\widetilde{x}_i \leftarrow \frac{x_i – \mu_B}{\sqrt{\sigma_B^2 + \epsilon}}\),最后将 B 中每个元素乘以 γ 再加 β ,输出。γ,β 是可训练参数,参与整个网络
  • 归一化的目的:将数据规整到统一区间,减少数据的发散程度,降低网络的学习难度。BN的精髓在于归一之后,使用 γ、β 作为还原参数,在一定程度上保留原数据的分布。

2.2 BN中均值、方差通过哪些维度计算得到?

神经网络中传递的张量数据,其维度通常记为[N, H, W, C],其中N是batch_size,H、W是行、列,C是通道数。那么上式中BN的输入集合 B 就是下图中蓝色的部分。

均值的计算,就是在一个批次内,将每个通道中的数字单独加起来,再除以 N×H×W 。举个例子:该批次内有10张图片,每张图片有三个通道RBG,每张图片的高、宽是H、W,那么均值就是计算10张图片R通道的像素数值总和除以10×H×W ,再计算B通道全部像素值总和除以10×H×W,最后计算G通道的像素值总和除以10×H×W。方差的计算类似。

可训练参数 、γ、β 的维度等于张量的通道数,在上述例子中,RBG三个通道分别需要一个 γ 和一个 β ,所以\(\overrightarrow{\gamma}, \overrightarrow{\beta}\)的维度等于3。

2.3 训练与推理时BN中的均值、方差分别是什么?

训练时,均值、方差分别是该批次内数据相应维度的均值与方差。

推理时,均值、方差是基于所有批次的期望计算所得。

2.4 在tensorflow中如何实现BN?

下述代码是基于tf.nn.batch_normalization封装好的BN函数:

import tensorflow as tf
from tensorflow.python.training import moving_averages


def batch_normalization(input, is_training, name="BN", 
                        moving_decay=0.999, eps=1e-5):
    input_shape = input.get_shape()
    params_shape = input_shape[-1]
    axis = list(range(len(input_shape) - 1))
    with tf.variable_scope(name, reuse=tf.AUTO_REUSE) as scope:
        beta = tf.get_variable('beta',
                               params_shape,
                               initializer=tf.zeros_initializer)
        gamma = tf.get_variable('gamma',
                                params_shape,
                                initializer=tf.ones_initializer)
        moving_mean = tf.get_variable('moving_mean',
                                      params_shape,
                                      initializer=tf.zeros_initializer,
                                      trainable=False
                                      )
        moving_var = tf.get_variable('moving_var',
                                     params_shape,
                                     initializer=tf.ones_initializer,
                                     trainable=False
                                     )
        def train():
            # These ops will only be preformed when training.
            mean, var = tf.nn.moments(input, axis)
            update_moving_mean = moving_averages.assign_moving_average(moving_mean,
                                                                       mean, 
                                                                       moving_decay)
            update_moving_var = moving_averages.assign_moving_average(
                moving_var, var, moving_decay)
            return tf.identity(mean), tf.identity(var)

        mean, var = tf.cond(tf.equal(is_training, True), train,
                                 lambda: (moving_mean, moving_var))

        return tf.nn.batch_normalization(input, mean, var, beta, gamma, eps)

在代码实现中有一个技巧,如果训练几百万个Batch,那么是不是要将其均值方差全部储存,最后再计算推理时所用的均值和方差?这样显然太过笨拙,占用内存随着训练次数不断上升。为了避免该问题,上述代码使用了滑动平均,储存固定个数Batch的均值和方差,不断迭代更新推理时需要的 E(x) 与 Var(x) 。

注意到代码中:

  1. beta、gamma在训练状态下,是可训练参数,在推理状态下,直接加载训练好的数值。
  2. moving_mean、moving_var在训练、推理中都是不可训练参数,只根据滑动平均计算公式更新数值,不会随着网络的训练而改变数值;在推理时,直接加载储存计算好的滑动平均之后的数值,作为推理时的均值和方差。

2.5 在网络中使用了BN,效果如何?

BN目前仍然是一个黑盒,论文中有一定的理论推导,但是普遍还是通过对比提升来验证BN的效果。原作者给出的效果如下图:

上图可见BN的两大收益:

  • 收敛速率增加
  • 可以达到更好的精度

在目标检测算法中,BN已经成为了标配。比如Yolov3引入了BN后,mAP提升了两个百分点。在更多实验中可以看到,BN同时起到了正则化作用,防止模型在训练集上过拟合,通常有BN的网络不再需要Dropout层。

来源

1.十指透兮的阳光. https://zhuanlan.zhihu.com/p/434773836. 知乎.

2.HAN Bing: 深入理解Batch Normalization

3.Algernon. 【基础算法】六问透彻理解BN(Batch Normalization). 知乎

4.论文链接:Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift

看完说谢谢了吗?哈哈哈~
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇