batchnorm原理理解「建议收藏」

batchnorm原理理解「建议收藏」接触CNN也一段时间了,最近也到了秋招期间,面试的时候可能会问到的一些内容需要做一个整理CNN-BN层参考了一个大神的博客,感觉讲的很深入也很好理解。我这里主要是对他的博客做一个自己的归纳整理,主要是为了方便自己去理解,也欢迎大家一起讨论自己的理解。这里给出大神的博客地址:https://blog.csdn.net/qq_25737169/article/details/79048…

大家好,又见面了,我是你们的朋友全栈君。

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

CNN-BN层

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

这里给出大神的博客地址:https://blog.csdn.net/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 的均值,
μβ=1mmi=1xi

μ β = 1 m i = 1 m x i

第二步:求出此次批量数据的方差, σβ2=1mi=1m(xiμβ)2 σ β 2 = 1 m ∑ i = 1 m ( x i − μ β ) 2
第三步:接下来就是对 x x 做归一化,得到
xi

x i

第四步:最重要的一步,引入缩放和平移变量 γ γ
β

β
,计算归一化后的值, 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μ)σ+β

γ ( x μ ) σ + β

BatchNorm的优点总结:

  • 没有它之前,需要小心的调整学习率和权重初始化,但是有了BN可以放心的使用大学习率,但是使用了BN,就不用小心的调参了,较大的学习率极大的提高了学习速度;
  • Batchnorm本身上也是一种正则的方式,可以代替其他正则方式如dropout等;
  • 另外,个人认为,batchnorm降低了数据之间的绝对差异,有一个去相关的性质,更多的考虑相对差异性,因此在分类任务上具有更好的效果。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/145959.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • java的web开发中URLEncoder.encode方法

    java的web开发中URLEncoder.encode方法一:我们先看2个编码的情况Stringname=java.net.URLEncoder.encode(“测试”,”UTF-8″);System.out.println(name);name=java.net.URLEncoder.encode(name,”UTF-8″);System.o…

    2022年6月15日
    145
  • Python 能做什么?「建议收藏」

    Python 能做什么?「建议收藏」Python能做什么?Python具有简单、易学、免费、开源、可移植、可扩展、可嵌入、面向对象等优点,我所关注的是网络爬虫方面,2018即将到来,我准备步入python的世界。Hello,wor

    2022年7月3日
    21
  • 提升苹果电脑速度的10个小技巧[通俗易懂]

    提升苹果电脑速度的10个小技巧[通俗易懂]众所周知,随着时间的流逝,包括Mac在内的所有计算机的速度都会降低。除了换电脑,还是有许多简单的调整可以提高计算机的性能并加快运行速度较慢的Mac,而且这些调整不会花费一分钱。1.升级macOS许多人仍然相信操作系统升级的神话总是会降低计算机的速度。尽管有时它们在旧Mac可能会出现性能问题,但这些更新通常弊大于利。它们包括错误修复,修补程序和改进,这些改进通常会提高Mac的速度。这些操作系统更新文件可能很大。因此,如果硬盘驱动器空间不足,则可能需要先释放硬盘空间。2.释放硬盘空间当您的存储驱动器达到其

    2022年5月26日
    50
  • python操作mysql数据库第三方库_python与数据库连接的代码

    python操作mysql数据库第三方库_python与数据库连接的代码MySQL系列–4.使用Python3访问数据库

    2022年4月21日
    44
  • linux rwx 权限「建议收藏」

    linux rwx 权限「建议收藏」@linuxrwx权限欢迎使用Markdown编辑器你好!这是你第一次使用Markdown编辑器所展示的欢迎页。如果你想学习如何使用Markdown编辑器,可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。新的改变我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:全新的界面设计,将会带来全新的写作体验;在创作中心设置你喜爱的代码高亮样式,Markdown将代码片显示选择的高亮样

    2022年5月2日
    55
  • 点云语义分割_语义分割研究内容

    点云语义分割_语义分割研究内容SGPN[CVPR2018]:点云的实例分割与物体检测。(SGPN:SimilarityGroupProposalNetworkfor3DPointCloudInstanceSegmentation。RSNet[CVPR2018]:点云的语义分割。(RecurrentSliceNetworksfor3DSegmentationonPointCloud…

    2022年8月23日
    4

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号