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降低了数据之间的绝对差异,有一个去相关的性质,更多的考虑相对差异性,因此在分类任务上具有更好的效果。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • 没有网线情况下使用树莓派连接WiFi

    没有网线情况下使用树莓派连接WiFi没有网线情况下使用树莓派连接WiFi烧录系统后,在boot/文件夹下创建wpa_supplicant.conf文件添加代码:country=CNctrl_interface=DIR=/var/run/wpa_supplicantGROUP=netdevupdate_config=1在尾部添加network={ssid=”你无线的名字”//无线名称psk=”你无线的密码”//无线密码}树莓派开机,可自动连接WiFi…

  • DataGrip2021.1激活码破解方法

    DataGrip2021.1激活码破解方法,https://javaforall.cn/100143.html。详细ieda激活码不妨到全栈程序员必看教程网一起来了解一下吧!

  • 图片压缩最优方案

    图片压缩最优方案

  • 知乎奇文!一本关于程序员的修真小说–分享我读到的一篇好小说

    终有一天我手中的编译器将成为我灵魂的一部分,这世界在我的眼中将被代码重构,我将看到山川无尽银河生灵都汇成二进制的数字河流,过往英雄都在我脑海眼前一一浮现,而我听到无数码农跪倒在我的程序面前呼喊。他们叫我代码之神。到那个时候,我想我一定可以找回你。一这是一个属于代码的修真世界。这世界里的每一个人,每一个东西,包括高山大海,刀剑风云,其本质都是一串数字流。打个比方,如果你知道一块石头…

  • rst markdown_r语言markdown导出

    rst markdown_r语言markdown导出markdown文件转RST文件时遇到的一些问题最近需要把一些markdown文件转成RST文件,第一次接触RST文件,使用中会有一些语法问题需要注意。在这里做个记录。转化工具我们可以先采用工具对整体做个初步的转换。因为转换工具不能做到完美转换,在初步转换完成后再根据实际的显示情况进行下一步的调整。这里推荐一个网站:MD在线转换成RST如果不注册的话,每天最多可以转化10个文件。后续调整转换后的大体格式是正确的,包括不同级别的标题,一些加粗斜体显示等,但是很多细节还是要自己调整的。1.网页链

  • rsyslog是什么_errorlog是什么

    rsyslog是什么_errorlog是什么早期及现在的大部分嵌入式系统使用的是klogd+syslogd组合,现在大多数发行版都使用rsyslogd或者syslogd-ng。rsyslogd是syslogd的升级版,其配置语法与syslogd的配置文件一致。ubuntu-gnome-16.04使用的是rsyslogd。…

发表回复

您的电子邮箱地址不会被公开。

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