网络优化(四)——逐层归一化

1. 逐层归一化概述

逐层归一化是将传统机器学习中的数据归一化方法应用到深度神经网络中,对神经网络中隐藏层的输入进行归一化,从而使得网络更容易训练。这里的逐层归一化方法是指可以应用在深度神经网络中的任何一个中间层.实际上并不需要对所有层进行归一化。
逐层归一化可以有效提高训练效率的原因有以下几个方面:

  1. 更好的尺度不变性:在深度神经网络中,一个神经层的输入是之前神经层的输出。给定一个神经层 𝑙 𝑙 l,它之前的神经层 ( 1 , ⋯ , 𝑙 − 1 ) (1, ⋯ , 𝑙 − 1) (1,,l1) 的参数变化会导致其输入的分布发生较大的改变。当使用随机梯度下降来训练网络时,每次参数更新都会导致该神经层的输入分布发生改变。越高的层,其输入分布会改变得越明显.就像一栋高楼,低楼层发生一个较小的偏移,可能会导致高楼层较大的偏移。从机器学习角度来看,如果一个神经层的输入分布发生了改变,那么其参数需要重新学习,这种现象叫作内部协变量偏移。为了缓解这个问题,我们可以对每一个神经层的输入进行归一化操作,使其分布保持稳定。

  2. 更平滑的优化地形:逐层归一化一方面可以使得大部分神经层的输入处于不饱和区域,从而让梯度变大,避免梯度消失问题;另一方面还可以使得神经网络的优化地形更加平滑,以及使梯度变得更加稳定,从而允许我们使用更大的学习率,并提高收敛速度。

下面介绍几种比较常用的逐层归一化方法:批量归一化、层归一化、权重归一化和局部响应归一化。

2. 批量归一化

对于一个深度神经网络,令第 𝑙 𝑙 l 层的净输入为 z ( 𝑙 ) z^{(𝑙)} z(l),神经元的输出为 a ( 𝑙 ) a^{(𝑙)} a(l),即
a ( l ) = f ( z ( l ) ) = f ( W a ( l − 1 ) + b ) a^{(l)}=f(z^{(l)})=f(Wa^{(l-1)}+b) a(l)=f(z(l))=f(Wa(l1)+b)其中 f ( ⋅ ) f(\cdot) f() 为激活函数。

为了提高优化效率,就要使得净输入𝒛(𝑙) 的分布一致,比如都归一化到标准正态分布。虽然归一化操作也可以应用在输入 a ( 𝑙 − 1 ) a^{(𝑙-1)} a(l1) 上,但归一化 z ( 𝑙 ) z^{(𝑙)} z(l) 更加有利于优化。因此,在实践中归一化操作一般应用在仿射变换 W a ( l − 1 ) + b Wa^{(l-1)}+b Wa(l1)+b 之后、激活函数之前。

为了提高归一化效率,一般使用标准化将净输入 z ( 𝑙 ) z^{(𝑙)} z(l) 的每一维都归一到标准正态分布,相关的公式如下所示:
z ^ ( 𝑙 ) = z ( 𝑙 ) − E [ z ( 𝑙 ) ] v a r ( z ( 𝑙 ) ) + ϵ \hat{z}^{(𝑙)}=\frac{z^{(𝑙)}-E[z^{(𝑙)}]}{\sqrt{var(z^{(𝑙)})+\epsilon}} z^(l)=var(z(l))+ϵ z(l)E[z(l)]其中, ϵ \epsilon ϵ 为保持数值稳定设置的较小的常数,一般为 1 0 − 7 10^{-7} 107 左右。

给定一个包含 𝐾 𝐾 K 个样本的小批量样本集合,第 𝑙 𝑙 l 层神经元的净输入 z ( 1 , 𝑙 ) , ⋯   , z ( K , 𝑙 ) z^{(1,𝑙)}, \cdots, z^{(K,𝑙)} z(1,l),,z(K,l)的均值和方差为
μ B = 1 K ∑ k = 1 K z ( k , l ) σ B 2 = 1 K ∑ k = 1 K ( z ( k , l ) − μ B ) 2 \begin{aligned} \mu_B&=\frac{1}{K}\sum_{k=1}^Kz^{(k,l)} \\ \sigma^2_B&=\frac{1}{K}\sum_{k=1}^K(z^{(k,l)}-\mu_B)^2 \end{aligned} μBσB2=K1k=1Kz(k,l)=K1k=1K(z(k,l)μB)2对净输入 z ( 𝑙 ) z^{(𝑙)} z(l) 的标准归一化会使得其取值集中到 0 0 0 附近,,如果使用 S i g m o i d Sigmoid Sigmoid 型激活函数时,这个取值区间刚好是接近线性变换的区间,减弱了神经网络的非线性性质.因此,为了使得归一化不对网络的表示能力造成负面影响,可以通过一个附加的缩放和平移变换改变取值区间,于是得到:
z ^ ( 𝑙 ) = z ( 𝑙 ) − μ B σ B 2 + ϵ γ + β = B N γ , β ( z ( 𝑙 ) ) \begin{aligned} \hat{z}^{(𝑙)}&=\frac{z^{(𝑙)}-\mu_B}{\sqrt{\sigma^2_B+\epsilon}}\gamma+\beta \\ &=BN_{\gamma,\beta}(z^{(𝑙)}) \end{aligned} z^(l)=σB2+ϵ z(l)μBγ+β=BNγ,β(z(l))其中 γ \gamma γ β \beta β 分别代表缩放和平移的参数向量。

批量归一化操作可以看作一个特殊的神经层,加在每一层非线性激活函数之前,即
a ( l ) = f ( B N γ , β ( z ( 𝑙 ) ) ) = f ( B N γ , β ( W a l − 1 ) ) a^{(l)}=f(BN_{\gamma,\beta}(z^{(𝑙)}))=f(BN_{\gamma,\beta}(Wa^{l-1})) a(l)=f(BNγ,β(z(l)))=f(BNγ,β(Wal1))

逐层归一化不但可以提高优化效率,还可以作为一种隐形的正则化方法.在训练时,神经网络对一个样本的预测不仅和该样本自身相关,也和同一批次中的其他样本相关。由于在选取批次时具有随机性,因此使得神经网络不会“过拟合”到某个特定样本,从而提高网络的泛化能力。

Pytorch实现

逐层归一化在pytorch中的实现如下所示:

torch.nn.BatchNorm1d(num_features, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)

参数解释如下:
num_features: 特征维度
eps:为数值稳定性而加到分母上的值。
momentum :移动平均的动量值。
affine :一个布尔值,当设置为真时,此模块具有可学习的仿射参数。

模型例子

直接在需要归一化的层后面添加即可,在模型中使用的例子如下:

net = nn.Sequential(
    nn.Conv2d(1, 6, kernel_size=5), nn.BatchNorm2d(6), nn.Sigmoid(),
    nn.AvgPool2d(kernel_size=2, stride=2),
    nn.Conv2d(6, 16, kernel_size=5), nn.BatchNorm2d(16), nn.Sigmoid(),
    nn.AvgPool2d(kernel_size=2, stride=2), nn.Flatten(),
    nn.Linear(256, 120), nn.BatchNorm1d(120), nn.Sigmoid(),
    nn.Linear(120, 84), nn.BatchNorm1d(84), nn.Sigmoid(),
    nn.Linear(84, 10))

3. 层归一化

批量归一化是对一个中间层的单个神经元进行归一化操作,因此要求小批量样本的数量不能太小,否则难以计算单个神经元的统计信息。此外,如果一个神经元的净输入的分布在神经网络中是动态变化的,比如循环神经网络,那么就无法应用批量归一化操作。

层归一化是和批量归一化非常类似的方法。和批量归一化不同的是,层归一化是对一个中间层的所有神经元进行归一化。

给定一个网络,第 𝑙 𝑙 l 层的净输入 z ( 𝑙 ) z^{(𝑙)} z(l)的均值和方差为
μ ( l ) = 1 M l ∑ k = 1 K z ( l ) σ ( l ) 2 = 1 M l ∑ k = 1 K ( z ( l ) − μ ( l ) ) 2 \begin{aligned} \mu^{(l)}&=\frac{1}{M_l}\sum_{k=1}^Kz^{(l)} \\ \sigma^{(l)^2}&=\frac{1}{M_l}\sum_{k=1}^K(z^{(l)}-\mu^{(l)})^2 \end{aligned} μ(l)σ(l)2=Ml1k=1Kz(l)=Ml1k=1K(z(l)μ(l))2其中 M l M_l Ml 为第 l l l 层神经元的数量。

层归一化定义如下:
z ^ ( 𝑙 ) = z ( 𝑙 ) − μ B σ B 2 + ϵ γ + β = L N γ , β ( z ( 𝑙 ) ) \begin{aligned} \hat{z}^{(𝑙)}&=\frac{z^{(𝑙)}-\mu_B}{\sqrt{\sigma^2_B+\epsilon}}\gamma+\beta \\ &=LN_{\gamma,\beta}(z^{(𝑙)}) \end{aligned} z^(l)=σB2+ϵ z(l)μBγ+β=LNγ,β(z(l))其中 γ \gamma γ β \beta β 分别代表缩放和平移的参数向量。

层归一化可以应用在循环神经网络中,对循环神经层进行归一化操作。在标准循环神经网络中,循环神经层的净输入一般会随着时间慢慢变大或变小,从而导致梯度爆炸或消失.而层归一化的循环神经网络可以有效地缓解这种状况。

层归一化和批量归一化整体上是十分类似的,差别在于归一化的方法不同.对于𝐾 个样本的一个小批量集合 Z ( l ) = [ z ( 1 , l ) , ⋯   , z ( K , l ) ] Z^{(l)}=[z^{(1,l)},\cdots,z^{(K,l)}] Z(l)=[z(1,l),,z(K,l)],层归一化是对矩阵 Z ( l ) Z^{(l)} Z(l) 的每一列进行归一化,而批量归一化是对 Z ( l ) Z^{(l)} Z(l) 每一行进行归一化。一般而言,批量归一化是一种更好的选择。当小批量样本数量比较小时,可以选择层归一化。

pytorch中的层归一化实现如下:

nn.LayerNorm(normalized_shape, eps=1e-05, elementwise_affine=True, device=None, dtype=None)

参数解释如下:
normalized_shape:归一化的维度
eps:加在方差上的数字,避免分母为0
elementwise_affine:bool,True的话会有一个默认的affine参数

elementwise_affine 就是公式中的 γ \gamma γ β \beta β,前者开始为 1 1 1,后者为 0 0 0,二者均可学习随着训练过程而变化。

其具体使用方法与上面的批量归一化一致,改个函数名即可,这里就不展示实例了。

4. 权重归一化

权重归一化是对神经网络的连接权重进行归一化,通过再参数化方法,将连接权重分解为长度和方向两种参数.假设第𝑙 层神经元 a ( 𝑙 ) = 𝑓 ( w a ( 𝑙 − 1 ) + b ) a^{(𝑙)} = 𝑓(wa^{(𝑙−1)} + b) a(l)=f(wa(l1)+b),我们将 W W W 再参数化为
W i = g i ∥ v i ∥ v i , 1 ≤ i ≤ M l W_i=\frac{g_i}{\Vert v_i \Vert}v_i,1 \le i \le M_l Wi=vigivi,1iMl其中 W i W_i Wi 表示权重 W W W 的第 i i i 行, M l M_l Ml 为神经元的数量,新引入的参数 g i g_i gi 为标量, v i v_i vi a l − 1 a^{l-1} al1 维数相同。

由于在神经网络中权重经常是共享的,权重数量往往比神经元数量要少,因此权重归一化的开销会比较小。

Pytorch实现

pytorch中的权重归一化实现如下:

torch.nn.utils.weight_norm(module, name='weight', dim=0)

参数解释如下:
module:需要权重归一化的模块
name:权重参数的名称
dim:归一化的维度

实例

import torch.nn as nn
import torch.nn.functional as F

# 以一个简单的单隐层的网络为例
class Model(nn.Module):
    def __init__(self, input_dim, output_dim, hidden_size):
        super(Model, self).__init__()
        # weight_norm
        self.dense1 = nn.utils.weight_norm(nn.Linear(input_dim, hidden_size))
        self.dense2 = nn.utils.weight_norm(nn.Linear(hidden_size, output_dim))
    
    def forward(self, x):
        x = self.dense1(x)
        x = F.leaky_relu(x)
        x = self.dense2(x)
        return x

5. 局部响应归一化

局部响应归一化是一种受生物学启发的归一化方法,通常用在基于卷积的图像处理上。假设一个卷积层的输出特征映射 Y ∈ R M × N × P Y \in ℝ^{M \times N \times P} YRM×N×P ,,其中每个切片矩阵 Y p ∈ R M × N Y^p \in ℝ^{M \times N} YpRM×N 为一个输出特征映射, 1 ≤ p ≤ P 1 \le p \le P 1pP

局部响应归一化是对邻近的特征映射进行局部归一化。
Y ^ p = Y p / ( k + α ∑ j = max ⁡ ( 1 , p − n 2 ) min ⁡ ( P , p + n 2 ) ( Y j ) 2 ) β = L R N n , k , α , β ( Y p ) \begin{aligned} \hat{Y}^p&=Y^p / \Big ( k+\alpha \sum_{j=\max(1,p-\frac{n}{2})}^{\min(P,p+\frac{n}{2})} (Y^j)^2 \Big)^{\beta} \\ &= LRN_{n,k,\alpha,\beta}(Y^p) \end{aligned} Y^p=Yp/(k+αj=max(1,p2n)min(P,p+2n)(Yj)2)β=LRNn,k,α,β(Yp)其中 n , k , α , β n,k,\alpha,\beta n,k,α,β 都为超参数。

局部响应归一化和层归一化都是对同层的神经元进行归一化.不同的是,局部响应归一化应用在激活函数之后,只是对邻近的神经元进行局部归一化,并且不减去均值。

局部响应归一化和生物神经元中的侧抑制现象比较类似,即活跃神经元对相邻神经元具有抑制作用。当使用 R e L U ReLU ReLU 作为激活函数时,神经元的活性值是没有限制的,局部响应归一化可以起到平衡和约束作用.如果一个神经元的活性值非常大,那么和它邻近的神经元就近似地归一化为 0 0 0,从而起到抑制作用,增强模型的泛化能力。最大汇聚也具有侧抑制作用.但最大汇聚是对同一个特征映射中的邻近位置中的神经元进行抑制,而局部响应归一化是对同一个位置的邻近特征映射中的神经元进行抑制。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值