复习笔记——BN基础”回炉“,我所熟悉的BN知识

复习笔记——BN基础算法”回炉“

复习笔记——基础为王,回顾基础,搞扎实是很有必要的,今天我抱着复习的目的和大家分享我理解的BN层,如有错误,欢迎批评指正以及补充.



文章目录

  • 复习笔记——BN基础算法”回炉“
  • BN
  • 一、Batch Normalization
  • 二、默写下公式和代码
    • 1. 手写公式
    • 2.torch 的BN
    • 3.跨卡BN问题
    • 4.关于我知道的BN trick
  • 三、总结BN效果


BN

Batch Normalization是CNN中必备操作,在代码中是耦合于卷积和激活函数操作的,那么它的出现是为了解决深层网络中存在的训练问题,避免梯度消失和爆炸且还有正则化缓解过拟合的作用。实际使用中:BN是可以加快收敛和提高模型精度的。

一、Batch Normalization

本篇以复习为基础:模型训练之前,需对数据做归一化处理,使其分布一致。在深度神经网络训练过程中,通常一次训练是一个batch,而非全体数据。每个batch具有不同的分布产生了internal covarivate shift问题——在训练过程中,数据分布会发生变化,对下一层网络的学习带来困难。

  1. BN就是数据拉回到均值为0,方差为1的正太分布上,提高模型的非线性表达能力。
  2. 使得数据分布一致,那么梯度就会稳定,避免梯度消失和爆炸,加速收敛,使得更深的网络可以稳定训练

二、默写下公式和代码

1. 手写公式

当然对于工作麻木的我来说,应试笔试这种东西我大概率是不如应届生的,所以提前声明献丑了:
复习笔记——BN基础”回炉“,我所熟悉的BN知识_第1张图片

这里两个参数缩放因子和平移因子分别调整方差和均值。
那么在实际的模型运作中:
1.训练时,均值、方差是1个batch内的计算得到,是变化的。
2.推理时,均值和方差是基于所有batch计算得到的,是固定的,需要补充一个EMA指数加权。
而这两个参数如何更新呢?
就是梯度下降:一般是SGD或者ADAM迭代。

2.torch 的BN

这里以个人相对熟悉的torch中的BN简单举例,因为在实际的代码中,通常会对基本原理的算法进行补充等优化:
在BN的类中,有如下参数:

def __init__(self, num_features, eps=1e-5, momentum=0.1, affine=True,track_running_stats=True):

momentum :设置为 None 时,使用 num_batches_tracked 计算每一轮更新的动量
eps:避免分母为0
num_features:输入通道数
affine:true就更新训练的两个因子,否则不更新
track_running_stats:true为:统计训练时输入的均值和方差

先根据上面公式和给的一些参数,代码如下:

x=torch.rand(8,3,10,10)
def bn_my(x,eps, weight, bias, momentum=0.1, mean=None, var=None, affine=True, track_running_stats=True):
    if(track_running_stats):
        mean=x.mean([0,2,3])
        #print('mean shape:',mean.shape)
        var=x.var([0,2,3],unbiased=False)#默认算无偏估计,因此需要手动设置unbiased=False
    x = x - mean[None, ..., None, None]
    x = x /torch.sqrt(var[None, ..., None, None]+eps)
    out= x * weight[..., None, None] + bias[..., None, None]
   # print('out:',out.shape)
    return out
bn_my()    
    

但实际中,其实我们只需要调包:nn.BatchNorm2d(num_features=3)就可以了哈
这里momentum参数没有用上,是因为它需要多次迭代多个Mini-batch更新动态的mean和var,当然在troch下还有一些比例参数的计算优化,这里不多介绍了,主要是复习下基础和一些概念原理的延伸,补充2个点:

  1. 在测试阶段,使用mode.eval(),则会直接计算输入的均值和方差,那么更新的mean和var则停止更新统计 ,使用之前更新好的去计算。
  2. 当然在torch下存在一个参数track_running_stats,如果为false,则会开启计算mean和var,即使在eval()模式下。

3.跨卡BN问题

BN越大往往我们的模型精度就会提升,但是显存是个硬伤,那么SYNCBN就是用来解决这个问题的,当你具备多个显卡可以满足使用较大BN去训练,这里说下我的理解:
多卡就是需要求所有卡对应的均值和方差,难点在于如何高效同步多个GPU的结果:
先说前向传播:

  1. 计算每张卡的mean和var
  2. 同步各个卡,得到全局mean和var,再计算,然后迭代更新实时的runing mean和var。
    反向传播:
  3. 在反向传播中,需要作多个进程之间的通信,这里是通过pytroch的函数实现,累计梯度和后,取均值回传。

4.关于我知道的BN trick

之前我在transformer系列的导读篇中介绍过BN和LN的区别,过多不介绍了,有兴趣的朋友自己看把,下面分享下在没有多卡情况下,我们需要用些trcik来进行优化:

  1. CBN && CMBN
    变相扩大BN的思路:
    CBN:在YOLO中通过前向传播batch/min-batch次,然后在一段时刻内统一更新梯度,包含可学习的参数:权重和BN的两个个因子,在计算每个迭代时刻统计量时候会考虑前batch/min-batch-1个时刻的统计量,然后在每个mini batch内部正常计算BN流程。
    CMBN:YOLOV4中看到的技巧,(将CBN的统计区间更改了范围,在某时刻统计BN时候,CMBN不会考虑前面minibatch统计量,顾名思义,以mini-batch为单位基准,只计算n=4个Minibatch内部。这样其实就是减少了内存的消耗,训练的相对较快。

三、总结BN效果

提示:这里对文章进行总结:
大概过了下脑子里的BN概念,可能不够全面和精准,但是从机器学习的角度概括来说,BN其实就是网络逐层的调整数据的输出分布,其实验证明了和激活函数成了绝配,这里再总结下BN的作用:

  1. 在网络深度达到一定程度基础上,数据分布本身影响着训练,在经过卷积和激活函数输出后,有的输出无论本身如何大,激活函数计算后,量级就会被压缩,这样导致网络已经逐渐失去了敏感度,说白了就是网络对于任意的输出过早饱和了,特别在深层的网络中会逐渐导致梯度消失和性能下降,如果用的是sigmoid还会产生梯度爆炸,这也时说明了一个合适的激活函数在隐藏层的重要作用。
  2. 因此BN在每一层添加处理,这里经验性地说下结论,在通过BN消融对比,在加入BN后的激活函数出会使得输出分布值不会过小或者饱和,这样自然会提升网络的性能,缓解过拟合,同样一个样本的输出不再仅仅取决于样本的本身,也取决于跟这个样本同属一个batch的其他样本,而每次网络都是随机取batch。
  3. BN后的数据通过缩放参数和平移参数,使得网络学习这个缩放和平移的参数,这样好处很明显:说简单点就是更有灵性,归一化之后会被限制在正态分布下,使得网络过于饱和,其实我的理解是,加强非线性表征能力。

你可能感兴趣的:(复习笔记系列,深度学习)