batchnorm原理及代码详解

本文深入浅出地介绍了BatchNorm算法的基本原理,包括其解决的问题、工作原理、优点及其实现代码。BatchNorm能有效缓解Internal Covariate Shift问题,加速神经网络训练并提高稳定性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转自:https://blog.youkuaiyun.com/qq_25737169/article/details/79048516

Batchnorm原理详解

前言:Batchnorm是深度网络中经常用到的加速神经网络训练,加速收敛速度及稳定性的算法,可以说是目前深度网络必不可少的一部分。 
本文旨在用通俗易懂的语言,对深度学习的常用算法–batchnorm的原理及其代码实现做一个详细的解读。本文主要包括以下几个部分。

  • Batchnorm主要解决的问题
  • Batchnorm原理解读
  • Batchnorm的优点
  • Batchnorm的源码解读

第一节:Batchnorm主要解决的问题


首先,此部分也即是讲为什么深度网络会需要batchnormbatchnorm,我们都知道,深度学习的话尤其是在CV上都需要对数据做归一化,因为深度神经网络主要就是为了学习训练数据的分布,并在测试集上达到很好的泛化效果,但是,如果我们每一个batch输入的数据都具有不同的分布,显然会给网络的训练带来困难。另一方面,数据经过一层层网络计算后,其数据分布也在发生着变化,此现象称为InternalInternal CovariateCovariate ShiftShift,接下来会详细解释,会给下一层的网络学习带来困难。batchnormbatchnorm直译过来就是批规范化,就是为了解决这个分布变化问题。

1.1 Internal Covariate Shift

InternalInternal CovariateCovariate ShiftShift :此术语是google小组在论文BatchBatch NormalizatoinNormalizatoin 中提出来的,其主要描述的是:训练深度网络的时候经常发生训练困难的问题,因为,每一次参数迭代更新后,上一层网络的输出数据经过这一层网络计算后,数据的分布会发生变化,为下一层网络的学习带来困难(神经网络本来就是要学习数据的分布,要是分布一直在变,学习就很难了),此现象称之为InternalInternal CovariateCovariate ShiftShift

BatchBatch NormalizatoinNormalizatoin 之前的解决方案就是使用较小的学习率,和小心的初始化参数,对数据做白化处理,但是显然治标不治本。

1.2 covariate shift

InternalInternal CovariateCovariate ShiftShift 和CovariateCovariate ShiftShift具有相似性,但并不是一个东西,前者发生在神经网络的内部,所以是InternalInternal,后者发生在输入数据上。CovariateCovariate ShiftShift主要描述的是由于训练数据和测试数据存在分布的差异性,给网络的泛化性和训练速度带来了影响,我们经常使用的方法是做归一化或者白化。想要直观感受的话,看下图:

图片来自网络

举个简单线性分类栗子,假设我们的数据分布如a所示,参数初始化一般是0均值,和较小的方差,此时拟合的y=wx+by=wx+b如b图中的橘色线,经过多次迭代后,达到紫色线,此时具有很好的分类效果,但是如果我们将其归一化到0点附近,显然会加快训练速度,如此我们更进一步的通过变换拉大数据之间的相对差异性,那么就更容易区分了。

CovariateCovariate ShiftShift 就是描述的输入数据分布不一致的现象,对数据做归一化当然可以加快训练速度,能对数据做去相关性,突出它们之间的分布相对差异就更好了。BatchnormBatchnorm做到了,前文已说过,BatchnormBatchnorm是归一化的一种手段,极限来说,这种方式会减小图像之间的绝对差异,突出相对差异,加快训练速度。所以说,并不是在深度学习的所有领域都可以使用BatchNormBatchNorm,下文会写到其不适用的情况。

第二节:Batchnorm 原理解读


本部分主要结合原论文部分,排除一些复杂的数学公式,对BatchNormBatchNorm的原理做尽可能详细的解释。

之前就说过,为了减小InternalInternal CovariateCovariate ShiftShift,对神经网络的每一层做归一化不就可以了,假设将每一层输出后的数据都归一化到0均值,1方差,满足正太分布,但是,此时有一个问题,每一层的数据分布都是标准正太分布,导致其完全学习不到输入数据的特征,因为,费劲心思学习到的特征分布被归一化了,因此,直接对每一层做归一化显然是不合理的。 
但是如果稍作修改,加入可训练的参数做归一化,那就是BatchNormBatchNorm实现的了,接下来结合下图的伪代码做详细的分析: 
这里写图片描述

之所以称之为batchnorm是因为所norm的数据是一个batch的,假设输入数据是β=x1...mβ=x1...m共m个数据,输出是yi=BN(x)yi=BN(x)batchnormbatchnorm的步骤如下:

1.先求出此次批量数据xx的均值,μβ=1mmi=1xiμβ=1m∑i=1mxi 
2.求出此次batch的方差,σ2β=1mi=1m(xiμβ)2σβ2=1m∑i=1m(xi−μβ)2 
3.接下来就是对xx做归一化,得到xixi− 
4.最重要的一步,引入缩放和平移变量γγββ ,计算归一化后的值,yi=γxiyi=γxi− +β

接下来详细介绍一下这额外的两个参数,之前也说过如果直接做归一化不做其他处理,神经网络是学不到任何东西的,但是加入这两个参数后,事情就不一样了,先考虑特殊情况下,如果γγββ分别等于此batch的方差和均值,那么yiyi不就还原到归一化前的xx了吗,也即是缩放平移到了归一化前的分布,相当于batchnormbatchnorm没有起作用,ββ 和γγ分别称之为 平移参数和缩放参数 。这样就保证了每一次数据经过归一化后还保留的有学习来的特征,同时又能完成归一化这个操作,加速训练。

先用一个简单的代码举个小栗子:

def Batchnorm_simple_for_train(x, gamma, beta, bn_param):
"""
param:x    : 输入数据,设shape(B,L)
param:gama : 缩放因子  γ
param:beta : 平移因子  β
param:bn_param   : batchnorm所需要的一些参数
    eps      : 接近0的数,防止分母出现0
    momentum : 动量参数,一般为0.9, 0.99, 0.999
    running_mean :滑动平均的方式计算新的均值,训练时计算,为测试数据做准备
    running_var  : 滑动平均的方式计算新的方差,训练时计算,为测试数据做准备
"""
    running_mean = bn_param['running_mean']  #shape = [B]
    running_var = bn_param['running_var']    #shape = [B]
    results = 0. # 建立一个新的变量

    x_mean=x.mean(axis=0)  # 计算x的均值
    x_var=x.var(axis=0)    # 计算方差
    x_normalized=(x-x_mean)/np.sqrt(x_var+eps)       # 归一化
    results = gamma * x_normalized + beta            # 缩放平移

    running_mean = momentum * running_mean + (1 - momentum) * x_mean
    running_var = momentum * running_var + (1 - momentum) * x_var

    #记录新的值
    bn_param['running_mean'] = running_mean
    bn_param['running_var'] = running_var 

    return results , bn_param
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

看完这个代码是不是对batchnorm有了一个清晰的理解,首先计算均值和方差,然后归一化,然后缩放和平移,完事!但是这是在训练中完成的任务,每次训练给一个批量,然后计算批量的均值方差,但是在测试的时候可不是这样,测试的时候每次只输入一张图片,这怎么计算批量的均值和方差,于是,就有了代码中下面两行,在训练的时候实现计算好meanmeanvarvar测试的时候直接拿来用就可以了,不用计算均值和方差。

running_mean = momentum * running_mean + (1 - momentum) * x_mean
running_var = momentum * running_var + (1 - momentum) * x_var
  • 1
  • 2

所以,测试的时候是这样的:

def Batchnorm_simple_for_test(x, gamma, beta, bn_param):
"""
param:x    : 输入数据,设shape(B,L)
param:gama : 缩放因子  γ
param:beta : 平移因子  β
param:bn_param   : batchnorm所需要的一些参数
    eps      : 接近0的数,防止分母出现0
    momentum : 动量参数,一般为0.9, 0.99, 0.999
    running_mean :滑动平均的方式计算新的均值,训练时计算,为测试数据做准备
    running_var  : 滑动平均的方式计算新的方差,训练时计算,为测试数据做准备
"""
    running_mean = bn_param['running_mean']  #shape = [B]
    running_var = bn_param['running_var']    #shape = [B]
    results = 0. # 建立一个新的变量

    x_normalized=(x-running_mean )/np.sqrt(running_var +eps)       # 归一化
    results = gamma * x_normalized + beta            # 缩放平移

    return results , bn_param
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

你是否理解了呢?如果还没有理解的话,欢迎再多看几遍。

第三节:Batchnorm源码解读


本节主要讲解一段tensorflow中BatchnormBatchnorm的可以使用的代码33,如下: 
代码来自知乎,这里加入注释帮助阅读。

def batch_norm_layer(x, train_phase, scope_bn):
    with tf.variable_scope(scope_bn):
        # 新建两个变量,平移、缩放因子
        beta = tf.Variable(tf.constant(0.0, shape=[x.shape[-1]]), name='beta', trainable=True)
        gamma = tf.Variable(tf.constant(1.0, shape=[x.shape[-1]]), name='gamma', trainable=True)

        # 计算此次批量的均值和方差
        axises = np.arange(len(x.shape) - 1)
        batch_mean, batch_var = tf.nn.moments(x, axises, name='moments')

        # 滑动平均做衰减
        ema = tf.train.ExponentialMovingAverage(decay=0.5)

        def mean_var_with_update():
            ema_apply_op = ema.apply([batch_mean, batch_var])
            with tf.control_dependencies([ema_apply_op]):
                return tf.identity(batch_mean), tf.identity(batch_var)
        # train_phase 训练还是测试的flag
        # 训练阶段计算runing_mean和runing_var,使用mean_var_with_update()函数
        # 测试的时候直接把之前计算的拿去用 ema.average(batch_mean)
        mean, var = tf.cond(train_phase, mean_var_with_update,
                            lambda: (ema.average(batch_mean), ema.average(batch_var)))
        normed = tf.nn.batch_normalization(x, mean, var, beta, gamma, 1e-3)
    return normed
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

至于此行代码tf.nn.batch_normalization()就是简单的计算batchnorm过程啦,代码如下: 
这个函数所实现的功能就如此公式:γ(xμ)σ+βγ(x−μ)σ+β

def batch_normalization(x,
                        mean,
                        variance,
                        offset,
                        scale,
                        variance_epsilon,
                        name=None):

    with ops.name_scope(name, "batchnorm", [x, mean, variance, scale, offset]):
        inv = math_ops.rsqrt(variance + variance_epsilon)
        if scale is not None:
            inv *= scale
        return x * inv + (offset - mean * inv
                      if offset is not None else -mean * inv)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

第四节:Batchnorm的优点

主要部分说完了,接下来对BatchNorm做一个总结:

  • 没有它之前,需要小心的调整学习率和权重初始化,但是有了BN可以放心的使用大学习率,但是使用了BN,就不用小心的调参了,较大的学习率极大的提高了学习速度,
  • Batchnorm本身上也是一种正则的方式,可以代替其他正则方式如dropout等
  • 另外,个人认为,batchnorm降低了数据之间的绝对差异,有一个去相关的性质,更多的考虑相对差异性,因此在分类任务上具有更好的效果。

注:或许大家都知道了,韩国团队在2017NTIRE图像超分辨率中取得了top1的成绩,主要原因竟是去掉了网络中的batchnorm层,由此可见,BN并不是适用于所有任务的,在image-to-image这样的任务中,尤其是超分辨率上,图像的绝对差异显得尤为重要,所以batchnorm的scale并不适合。

参考文献: 
【1】http://blog.youkuaiyun.com/zhikangfu/article/details/53391840 
【2】http://geek.youkuaiyun.com/news/detail/160906 
【3】 https://www.zhihu.com/question/53133249


<think>嗯,我现在要回答用户关于Batch Normalization(批归一化)的问题。首先,用户想了解它的概念和用法。我需要先回忆一下BN的基本原理,然后结合提供的引用内容来组织答案。 根据引用[1]和[5],BN主要是用来减少内部协变量偏移,加速深度网络训练。它的核心思想是对每一层的输入进行归一化处理,使得输入分布更稳定。接下来,我需要解释具体怎么做,比如在训练时对每个小批量进行标准化,计算均值和方差,然后用可学习的参数γ和β进行调整。这部分内容在引用[4]里也有提到,训练时使用batch的均值和标准差,而推理时使用整个数据集的统计量。 然后,我需要提到BN的步骤,可能分点说明。比如归一化、缩放和平移。这部分可以结合引用[2]中的内容,说明任何之前接收x的层现在接收BN(x),也就是在激活函数之前应用BN。 还要讲BN的优点,如加速训练、允许更大的学习率、减少对初始化的依赖,以及正则化效果。引用[5]提到BN可以替代Dropout,并减少其他正则化的需要。另外,引用[4]指出在推理时使用固定的均值和方差,确保输出确定性。 然后,我需要给出一个算法流程或公式,可能需要使用LaTeX。根据引用[4],训练时的均值和方差是每个batch计算的,而推理时则是移动平均。公式部分要注意用户要求行内数学表达式用$...$,独立公式用$$...$$。例如,归一化的公式应该是:$$ \hat{x}^{(k)} = \frac{x^{(k)} - \mu_{\mathcal{B}}^{(k)}}{\sqrt{{\sigma_{\mathcal{B}}^{(k)}}^2 + \epsilon}} $$,然后缩放和平移是$y^{(k)} = \gamma^{(k)} \hat{x}^{(k)} + \beta^{(k)}$。 接着是代码示例,用户给的示例里有Python代码,所以可能用类似的框架,比如PyTorch或TensorFlow的实现。比如在PyTorch中,用nn.BatchNorm2d,但需要简单说明参数的含义,如num_features对应通道数,eps是ε,momentum用于移动平均等。 最后生成相关问题,参考用户给的示例,问题应该围绕BN的原理、优点、实现、与其他技术对比等。比如,BN如何解决梯度消失、训练和推理阶段的区别、参数γ和β的作用,或者BN与其他归一化方法的比较。 需要注意的是,所有引用要正确标注,比如引用[1]和[5]都是原始论文,引用[4]解释训练和推理阶段的处理,引用[2]提到训练时使用小批量,推理时使用确定性输出。同时,确保LaTeX格式正确,行内公式用$,独立公式用$$,并且单独成段。 现在整理结构:先介绍概念,然后详细步骤,接着优点,再给出算法公式,代码示例,最后相关问题。确保每个部分都有对应的引用支持,并且自然标注引用标识。</think>### Batch Normalization(批归一化)详解 Batch Normalization(BN)是深度学习中用于加速训练、提升模型稳定性的关键技术,通过规范化神经网络中间层的输入分布,缓解**内部协变量偏移**(Internal Covariate Shift)问题[^1][^5]。其核心思想是对每个小批量(mini-batch)的数据进行标准化处理,并引入可学习的参数进行缩放和平移,从而保留网络的表达能力。 --- #### **核心概念与实现步骤** 1. **标准化计算** 对于每一层的输入$X = \{x^{(1)}, x^{(2)}, ..., x^{(m)}\}$(m为batch大小),按特征维度计算均值$\mu_{\mathcal{B}}$和方差$\sigma_{\mathcal{B}}^2$: $$ \mu_{\mathcal{B}}^{(k)} = \frac{1}{m}\sum_{i=1}^{m}x_i^{(k)}, \quad {\sigma_{\mathcal{B}}^{(k)}}^2 = \frac{1}{m}\sum_{i=1}^{m}(x_i^{(k)}-\mu_{\mathcal{B}}^{(k)})^2 $$ 其中$k$表示第$k$个神经元,$\epsilon$是为防止除零的小常数。 2. **归一化与线性变换** 标准化后,通过可学习的参数$\gamma^{(k)}$(缩放)和$\beta^{(k)}$(平移)恢复数据的表达能力: $$ \hat{x}^{(k)} = \frac{x^{(k)} - \mu_{\mathcal{B}}^{(k)}}{\sqrt{{\sigma_{\mathcal{B}}^{(k)}}^2 + \epsilon}}, \quad y^{(k)} = \gamma^{(k)}\hat{x}^{(k)} + \beta^{(k)} $$ 这一操作确保了网络既能享受归一化的稳定性,又能通过$\gamma$和$\beta$调整分布[^4]。 3. **推理阶段的调整** 训练时使用移动平均统计全局均值$\mu_{\text{global}}$和方差$\sigma_{\text{global}}^2$,推理时直接应用: $$ y^{(k)} = \gamma^{(k)}\frac{x^{(k)} - \mu_{\text{global}}^{(k)}}{\sqrt{\sigma_{\text{global}}^{(k)2} + \epsilon}} + \beta^{(k)} $$ 这保证了推理结果的确定性[^2][^4]。 --- #### **BN的主要优势** 1. **加速收敛** 通过稳定输入分布,允许使用更大的学习率,减少梯度消失/爆炸问题[^5]。 2. **降低对初始化的敏感度** 输入归一化后,权重初始化范围的影响减小。 3. **正则化效果** 小批量统计带来的噪声类似Dropout,减少过拟合。 4. **替代其他正则化方法** 实验表明,BN可部分替代L2正则化或Dropout。 --- #### **代码实现示例(PyTorch)** ```python import torch.nn as nn # 定义包含BN的卷积层 model = nn.Sequential( nn.Conv2d(in_channels=3, out_channels=64, kernel_size=3), nn.BatchNorm2d(num_features=64, eps=1e-5, momentum=0.1), # num_features需与输入通道一致 nn.ReLU(), nn.MaxPool2d(kernel_size=2) ) ``` - `num_features`:输入数据的通道数(或全连接层的神经元数) - `eps`:公式中的$\epsilon$,默认`1e-5` - `momentum`:更新全局统计量时的动量参数 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值