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)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • dpkg命令的用法

    dpkg命令的用法dpkg命令的用法dpkg是Debianpackage的简写,为”Debian“操作系统专门开发的套件管理系统,用于软件的安装,更新和移除。所有源自"Debian"的Linux的发行版都使用dpkg,  例如"Ubuntu" 阅读目录 安装软件列出与该包先关联的文件显示包的版本移除软件(保留配置)移除软件(不保留配置)查找包的详细信息列出deb包的内容 安装软件命令:dpkg-i&…

    2022年5月11日
    72
  • 怎么查看数据库端口(sqlserver数据库端口查看)

    more$ORACLE_HOME/db/install/portlist.ini 或者使用netmanager

    2022年4月14日
    44
  • MySQL——MySQL 图形化管理工具的介绍[通俗易懂]

    MySQL——MySQL 图形化管理工具的介绍[通俗易懂]文章目录MySQL——MySQL图形化管理工具的介绍1、MySQLWorkbench2、Navicat3、SQLyog4、DBeaver5、DataGripMySQL——MySQL图形化管理工具的介绍MySQL图形化管理工具极大地方便了数据库的操作与管理,常用的图形化管理工具有:MysQLWorkbench、phpMyAdmin、NavicatPreminum、MySQLDumper、SQLyog、dbeaver、MysQLODBcConnector、DataGrip。1、MySQL

    2022年6月30日
    36
  • c语言中putchar的用法举例_putchar和getchar

    c语言中putchar的用法举例_putchar和getcharC语言中getchar()和putchar()的用法getchar()和putchar()是一对字符输入/输出函数.getchar()不带任何参数,他从输入序列中返回下一个字符。例如,下面的语句读取下一个字符输入,并把该字符的值赋给变量ch:ch=getcha();putchar()函数打印它的参数。例如,下面的语句把之前赋给ch的值作为字符打印出来:putchar(ch);由于这两个函数只处理字符,所以他们通常比scanf()和printf()函数更快更便捷。而且,ge

    2022年10月18日
    4
  • win10下使用vs2015编译支持xp系统的libcurl

    win10下使用vs2015编译支持xp系统的libcurl在我的一篇博客中写了编译libcurl的,那种方式编译的curl动态库在win7到win10上可以使用,但是在xp系统里就不能使用了,接下来讲解一种方法可以在xp系统里使用cur。1编译openssl由于在perl官网里提供的perl版本没有dmake,因此使用我提供的perl5.24带dmake的包,下载地址。安装好perl后,可以尝试使用ppminstalldmake命令来进行安装dmake模块,在我电脑里无法安装,因此直接使用dmake离线包。将dmake.exe所在目录添加到系统pat

    2022年7月12日
    18
  • google软件测试之道_gtest测试框架

    google软件测试之道_gtest测试框架gtest提供了一套优秀的C++单元测试解决方案,简单易用,功能完善,非常适合在项目中使用以保证代码质量。

    2022年9月29日
    5

发表回复

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

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