神经网络基础部件-BN层详解

Posted 嵌入式视觉

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了神经网络基础部件-BN层详解相关的知识,希望对你有一定的参考价值。

一,数学基础

1.1,概率密度函数

随机变量(random variable)是可以随机地取不同值的变量。随机变量可以是离散的或者连续的。简单起见,本文用大写字母 X X X 表示随机变量,小写字母 x x x 表示随机变量能够取到的值。例如, x 1 x_1 x1 x 2 x_2 x2 都是随机变量 X X X 可能的取值。随机变量必须伴随着一个概率分布来指定每个状态的可能性。

概率分布(probability distribution)用来描述随机变量或一簇随机变量在每一个可能取到的状态的可能性大小。我们描述概率分布的方式取决于随机变量是离散的还是连续的。

当我们研究的对象是连续型随机变量时,我们用概率密度函数(probability density function, PDF)而不是概率质量函数来描述它的概率分布。

更多内容请阅读《花书》第三章-概率与信息论,或者我的文章-深度学习数学基础-概率与信息论

1.2,正态分布

当我们不知道数据真实分布时使用正态分布的原因之一是,正态分布拥有最大的熵,我们通过这个假设来施加尽可能少的结构。

实数上最常用的分布就是正态分布(normal distribution),也称为高斯分布 (Gaussian distribution)。

如果随机变量 X X X ,服从位置参数为 μ \\mu μ、尺度参数为 σ \\sigma σ 的概率分布,且其概率密度函数为:

f ( x ) = 1 σ 2 π e − ( x − μ ) 2 2 σ 2 (1) f(x)=\\frac1\\sigma\\sqrt2 \\pi e^- \\frac(x-\\mu)^22\\sigma^2 \\tag1 f(x)=σ2π 1e2σ2(xμ)2(1)
则这个随机变量就称为正态随机变量,正态随机变量服从的概率分布就称为正态分布,记作:
X ∼ N ( μ , σ 2 ) (2) X \\sim N(\\mu,\\sigma^2) \\tag2 XN(μ,σ2)(2)
如果位置参数 μ = 0 \\mu = 0 μ=0,尺度参数 σ = 1 \\sigma = 1 σ=1 时,则称为标准正态分布,记作:
X ∼ N ( 0 , 1 ) (3) X \\sim N(0, 1) \\tag3 XN(0,1)(3)
此时,概率密度函数公式简化为:
f ( x ) = 1 2 π e − x 2 2 (4) f(x)=\\frac1\\sqrt2 \\pi e^- \\fracx^22 \\tag4 f(x)=2π 1e2x2(4)
正态分布的数学期望值或期望值 μ \\mu μ 等于位置参数,决定了分布的位置;其方差 σ 2 \\sigma^2 σ2 的开平方或标准差 σ \\sigma σ 等于尺度参数,决定了分布的幅度。正态分布的概率密度函数曲线呈钟形,常称之为钟形曲线,如下图所示:

可视化正态分布,可直接通过 np.random.normal 函数生成指定均值和标准差的正态分布随机数,然后基于 matplotlib + seabornkdeplot函数绘制概率密度曲线。示例代码如下所示:

import seaborn as sns
x1 = np.random.normal(0, 1, 100)
x2 = np.random.normal(0, 1.5, 100) 
x3 = np.random.normal(2, 1.5, 100) 

plt.figure(dpi = 200)

sns.kdeplot(x1, label="μ=0, σ=1")
sns.kdeplot(x2, label="μ=0, σ=1.5")
sns.kdeplot(x3, label="μ=2, σ=2.5")

#显示图例
plt.legend()
#添加标题
plt.title("Normal distribution")
plt.show()

以上代码直接运行后,输出结果如下图:

当然也可以自己实现正态分布的概率密度函数,代码和程序输出结果如下:

import numpy as np
import matplotlib.pyplot as plt
plt.figure(dpi = 200)
plt.style.use('seaborn-darkgrid') # 主题设置

def nd_func(x, sigma, mu):
  	"""自定义实现正态分布的概率密度函数
  	"""
    a = - (x-mu)**2 / (2*sigma*sigma)
    f = np.exp(a) / (sigma * np.sqrt(2*np.pi))
    return f

if __name__ == '__main__':
    x = np.linspace(-5, 5)
    f = nd_fun(x, 1, 0)
    p1, = plt.plot(x, f)

    f = nd_fun(x, 1.5, 0)
    p2, = plt.plot(x, f)

    f = nd_fun(x, 1.5, 2)
    p3, = plt.plot(x, f)

    plt.legend([p1 ,p2, p3], ["μ=0,σ=1", "μ=0,σ=1.5", "μ=2,σ=1.5"])
    plt.show()

二,背景

训练深度神经网络的复杂性在于,因为前面的层的参数会发生变化导致每层输入的分布在训练过程中会发生变化。这又导致模型需要需要较低的学习率和非常谨慎的参数初始化策略,从而减慢了训练速度,并且具有饱和非线性的模型训练起来也非常困难。

网络层输入数据分布发生变化的这种现象称为内部协变量转移,BN 就是来解决这个问题。

2.1,如何理解 Internal Covariate Shift

在深度神经网络训练的过程中,由于网络中参数变化而引起网络中间层数据分布发生变化的这一过程被称在论文中称之为内部协变量偏移(Internal Covariate Shift)。

那么,为什么网络中间层数据分布会发生变化呢?

在深度神经网络中,我们可以将每一层视为对输入的信号做了一次变换(暂时不考虑激活,因为激活函数不会改变输入数据的分布):
Z = W ⋅ X + B (5) Z = W \\cdot X + B \\tag5 Z=WX+B(5)
其中 W W W B B B 是模型学习的参数,这个公式涵盖了全连接层和卷积层。

随着 SGD 算法更新参数,和网络的每一层的输入数据经过公式5的运算后,其 Z Z Z分布一直在变化,因此网络的每一层都需要不断适应新的分布,这一过程就被叫做 Internal Covariate Shift。

而深度神经网络训练的复杂性在于每层的输入受到前面所有层的参数的影响—因此当网络变得更深时,网络参数的微小变化就会被放大。

2.2,Internal Covariate Shift 带来的问题

  1. 网络层需要不断适应新的分布,导致网络学习速度的降低

  2. 网络层输入数据容易陷入到非线性的饱和状态并减慢网络收敛,这个影响随着网络深度的增加而放大。

    随着网络层的加深,后面网络输入 x x x 越来越大,而如果我们又采用 Sigmoid 型激活函数,那么每层的输入很容易移动到非线性饱和区域,此时梯度会变得很小甚至接近于 0 0 0,导致参数的更新速度就会减慢,进而又会放慢网络的收敛速度。

饱和问题和由此产生的梯度消失通常通过使用修正线性单元激活( R e L U ( x ) = m a x ( x , 0 ) ReLU(x)=max(x,0) ReLU(x)=max(x,0)),更好的参数初始化方法和小的学习率来解决。然而,如果我们能保证非线性输入的分布在网络训练时保持更稳定,那么优化器将不太可能陷入饱和状态,进而训练也将加速。

2.3,减少 Internal Covariate Shift 的一些尝试

  1. 白化(Whitening): 即输入线性变换为具有零均值和单位方差,并去相关。

    白化过程由于改变了网络每一层的分布,因而改变了网络层中本身数据的表达能力。底层网络学习到的参数信息会被白化操作丢失掉,而且白化计算成本也高。

  2. 标准化(normalization)

    Normalization 操作虽然缓解了 ICS 问题,让每一层网络的输入数据分布都变得稳定,但却导致了数据表达能力的缺失。

三,批量归一化(BN)

3.1,BN 的前向计算

论文中给出的 Batch Normalizing Transform 算法计算过程如下图所示。其中输入是一个考虑一个大小为 m m m 的小批量数据 B \\cal B B

论文中的公式不太清晰,下面我给出更为清晰的 Batch Normalizing Transform 算法计算过程。

m m m 表示 batch_size 的大小, n n n 表示 features 数量,即样本特征值数量。在训练过程中,针对每一个 batch 数据,BN 过程进行的操作是,将这组数据 normalization,之后对其进行线性变换,具体算法步骤如下:

μ B = 1 m ∑ 1 m x i σ B 2 = 1 m ∑ 1 m ( x i − μ B ) 2 n i = x i − μ B σ B 2 + ϵ z i = γ n i + β = γ σ B 2 + ϵ x i + ( β − γ μ B σ B 2 + ϵ ) (6) \\beginaligned \\mu_B = \\frac1m\\sum_1^m x_i \\\\ \\sigma^2_B = \\frac1m \\sum_1^m (x_i-\\mu_B)^2 \\\\ n_i = \\fracx_i-\\mu_B\\sqrt\\sigma^2_B + \\epsilon \\\\ z_i = \\gamma n_i + \\beta = \\frac\\gamma\\sqrt\\sigma^2_B + \\epsilonx_i + (\\beta - \\frac\\gamma\\mu_B\\sqrt\\sigma^2_B + \\epsilon) \\tag6\\\\ \\endaligned μB=m11mxiσB2=m11m(xiμB)2ni=σB2+ϵ xi以上是关于神经网络基础部件-BN层详解的主要内容,如果未能解决你的问题,请参考以下文章

神经网络推理加速: 合并卷积和BN层运算原理及实验

神经网络推理加速: 合并卷积和BN层运算原理及实验

TF之BN:BN算法对多层中的每层神经网络加快学习QuadraticFunction_InputData+Histogram+BN的Error_curve

卷积神经网络对BN层的解释

深度学习BN与神经网络调优

TensorRT8 使用手记(1)模型测试 Conv+BN+Relu 结构融合