batchnorm原理理解

接触CNN也一段时间了,最近也到了秋招期间,面试的时候可能会问到的一些内容需要做一个整理

CNN-BN层

参考了一个大神的博客,感觉讲的很深入也很好理解。我这里主要是对他的博客做一个自己的归纳整理,主要是为了方便自己去理解,也欢迎大家一起讨论自己的理解。

这里给出大神的博客地址:https://blog.youkuaiyun.com/qq_25737169/article/details/79048516

归纳整理如下:

1:深度神经网络主要学习的是训练数据的分布,并能够在测试集上做很好的fahu泛化。但是数据在经过每层卷积层和relu层计算后,其数据分布也在发生变化,这种现象称之为InternalInternal Covariate ShiftShift(内部协变量移位),也就是说经过每一次迭代更新参数后,上一层网络的输出数据经过这一层网络计算后,数据的分布会发生变化,这就会为下一层的网络学习带来困难

2:在batchnorm产生之前,针对由于分布变化导致学习困难的问题,主要解决办法是使用较小的学习率,和小心的初始化参数,对数据做白化处理,但是治标不治本,不能根本解决问题。

3:所谓数据分布,分为两种情况,一种在输入数据分布不一样,我们叫Covariate ShiftShift,比如训练的数据和测试的数据本身分布就不一样,那么训练后的模型就很难泛化到测试集上。另一种分布不一样是在输入数据经过网络内部计算后,分布发生了变化,这样导致数据变得不稳定,从而导致网络寻找最优解的过程变得缓慢,训练速度会下降。如下图所示:

​​这里写图片描述
​​​​​​
我们知道在网络初始化的时候,初始的w,b一般都很小,略大于0,如果我们将 a 图的数据归一化到 c 图的原点附近,那么,网络拟合y = wx+b时,b就相对很容易就能从初始的位置找到最优值,如果在将 c 图的数据做一个小小的拉伸,转换为 d 图的数据,此时,数据之间的相对差异性变大,拟合y = wx+b这条划分线时,w相对容易从初始位置找到最优值。这样会使训练速度加快。

4:但是归一化有很多种方式,batchnorm只是其中一种,那么现在有一个问题,假如我直接对网络的每一层输入做一个符合正态分布的归一化,然后输入数据的分布本身不是呈正态分布或者不是呈该正态分布,那会这样会容易导致后边的网络学习不到输入数据的分布特征了,因为,费劲心思学习到的特征分布被这么暴力的归一化了,因此直接对每一层做归一化显示不合理。但是稍作修改,加入可训练的参数做归一化,那就是BatchNorm实现的了

5:batchnorm顾名思义是对每batch个数据同时做一个norm,batchnorm是怎么做的,来看下边伪代码:

这里写图片描述

可以看出第一步:先求出此次批量数据 x x 的均值,μβ=1mi=1mxi
第二步:求出此次批量数据的方差, σβ2=1mi=1m(xiμβ)2 σ β 2 = 1 m ∑ i = 1 m ( x i − μ β ) 2
第三步:接下来就是对 x x 做归一化,得到xi
第四步:最重要的一步,引入缩放和平移变量 γ γ β ,计算归一化后的值, yi=γxi+β y i = γ x i − + β
如果不加 γ γ β,直接归一化,是会打乱原有数据的分布,容易导致网络学不到任何东西,但是加入这两个参数后,事情就不一样了。先考虑特殊情况,假设 γ γ 是batch的方差,β是batch的均值,那么 yi=γxi+β y i = γ x i − + β 得到的 yi y i 就是还原到了归一化之前的 x x ,也就是缩放平移到了归一化前的分布,相当于batchnorm没有改变任何分布没有起作用。所以,加入了γ β β 这两个参数后的batchnorm,保证了每一次数据归一化后还保留有之前学习来的特征分布,同时又能完成归一化的操作,加速训练。

看下边batchnorm的一个简单代码:

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

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

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

所以测试的时候是下边这样的:

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

下边附上tensorflow BatchNorm的一段源码,代码来源于知乎,这里加入注释帮助阅读。

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

上边倒数第二行的函数:tf.nn.batch_normalization()就是计算batchnorm的过程啦,定义如下所示:

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)

这个函数的功能就是计算γ(xμ)σ+β

BatchNorm的优点总结:

  • 没有它之前,需要小心的调整学习率和权重初始化,但是有了BN可以放心的使用大学习率,但是使用了BN,就不用小心的调参了,较大的学习率极大的提高了学习速度;
  • Batchnorm本身上也是一种正则的方式,可以代替其他正则方式如dropout等;
  • 另外,个人认为,batchnorm降低了数据之间的绝对差异,有一个去相关的性质,更多的考虑相对差异性,因此在分类任务上具有更好的效果。
### BatchNorm 可视化图表绘制方法 在深度学习中,Batch Normalization (BN) 是一种广泛使用的技巧,它有助于加速训练并提高模型性能。为了更好地理解和调试 BN 层的效果,可以通过可视化手段展示其工作原理。 #### 使用TensorBoard进行BatchNorm可视化 TensorBoard是一个强大的工具,仅可以监控训练进度,还可以用来观察BN层的行为。具体来说: - **直方图**:可以显示每一批次输入经过BN变换前后的分布情况[^1]。 ```python import tensorflow as tf from tensorflow.keras.layers import Dense, Flatten, Conv2D, BatchNormalization from tensorflow.keras import Model class MyModel(Model): def __init__(self): super(MyModel, self).__init__() self.conv1 = Conv2D(32, 3, activation='relu') self.bn1 = BatchNormalization() self.flatten = Flatten() self.d1 = Dense(128, activation='relu') self.d2 = Dense(10) def call(self, x): x = self.conv1(x) x = self.bn1(x) x = self.flatten(x) x = self.d1(x) return self.d2(x) model = MyModel() # 设置tensorboard回调函数 log_dir="logs/gradient_tape" summary_writer = tf.summary.create_file_writer(log_dir) with summary_writer.as_default(): tf.summary.histogram("bn1/gamma", model.bn1.gamma, step=epoch) tf.summary.histogram("bn1/beta", model.bn1.beta, step=epoch) ``` 这段代码展示了如何利用TensorFlow内置的支持,在每次迭代时记录BN参数的变化,并将其保存下来以便后续通过TensorBoard查看。 #### 利用Matplotlib自定义绘图 除了借助现成的工具外,也可以采用编程方式创建更个性化的图形表示形式。例如,下面的例子说明了怎样使用matplotlib来画出单个批次内各通道均值随时间变化的趋势图。 ```python import matplotlib.pyplot as plt import numpy as np def plot_batchnorm_stats(mean_values, var_values=None): fig, ax = plt.subplots(figsize=(10,6)) epochs = range(len(mean_values)) colors = ['b', 'g', 'r'] labels = ["Channel {}".format(i+1) for i in range(len(colors))] for idx, color in enumerate(colors[:len(labels)]): ax.plot(epochs, mean_values[:,idx], label=f'Mean {labels[idx]}', c=color) if var_values is not None: upper_bound = mean_values[:,idx]+np.sqrt(var_values[:,idx]) lower_bound = mean_values[:,idx]-np.sqrt(var_values[:,idx]) ax.fill_between(epochs, lower_bound, upper_bound, alpha=.15, facecolor=color) ax.set_xlabel('Epochs') ax.set_ylabel('Mean Value') ax.legend(loc='best') plt.show() # 假设mean_values是从某一层获取到的一系列batch norm统计信息 fake_mean_data = np.random.rand(100,3)*0.5 + 0.75 # 随机生成一些测试数据 plot_batchnorm_stats(fake_mean_data) ``` 此脚本会生成一张折线图,其中同颜色代表同的特征映射(channel),阴影区域则反映了标准差范围内的波动程度。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HaoRenkk123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值