BN-tmp

问题:GN论文里面LN的 C的问题

所有以4D数据 [ B b a t c h = 128 , C c h a n n e l = 3 , H h e i g h t = 384 , W w i d t h = 384 ] [B_{batch}=128,C_{channel}=3,H_{height}=384,W_{width}=384] [Bbatch=128,Cchannel=3,Hheight=384,Wwidth=384]为例

LN以3D数据 [ B b a t c h = 128 , W w o r d = 512 , E e m b e d d i n g _ d i m = 768 ] [B_{batch}=128,W_{word}=512,E_{embedding\_dim}=768] [Bbatch=128,Wword=512,Eembedding_dim=768]在bert实现再举例:

名称(来源) normalized_shape,归一化维度(对哪些数据做统计) 统计值个数(组,每组两个) 仿射参数个数(如果开启)(组,每组两个) 备注 pytorch实现 TF实现
LRN-2012 a c ( k + α n ∑ c ′ = max ⁡ ( 0 , c − n / 2 ) min ⁡ ( C − 1 , c + n / 2 ) a c ′ 2 ) − β \displaystyle a_{c}\left(k + \frac{\alpha}{n} \sum_{c'=\max(0, c-n/2)}^{\min(C-1,c+n/2)}a_{c'}^2\right)^{-\beta} ack+nαc=max(0,cn/2)min(C1,c+n/2)ac2β 每个点都算一遍 B C H W BCHW BCHW 被[VGG-2015]怼了
大致是featuer-map每个点的临近n个通道做平滑
BN-2015 B H W BHW BHW(这个部分没有B,预测和训练才不需要分离,下同) C C C C C C(架构决定不能与B相关,下同) 只有BN能变成恒等
因为normalized_shape和B有关,所以预测和训练分离,以下均不分离
TF采用广播,变成元素积scale, offset = _broadcast(self.gamma), _broadcast(self.beta)
LN-2016 C H W CHW CHW
[ E ] , 固 定 , 一 个 样 本 [E],固定,一个样本 [E]
B B B
[ B W ] , R N N 里 面 这 俩 都 不 固 定 [BW],RNN里面这俩都不固定 [BW]RNN
C H W CHW CHW
[ E ] [E] [E]
mean和std计算是根据normalized_shape. γ 和 β \gamma和\beta γβ也是normalized_shape
所以并不能恒等,以下同理
pytorch源码 TF实现,注意是元素积
IN-2017 H W HW HW B C BC BC C C C 以2D函数举例(数据是4D:BCHW) pytorch源码
GN-2018 C G HW \frac{\text{C} }{\text{G}}\text{HW} GCHW B G BG BG C C C G为通道内group个数
G为1则LN;
G为C则IN
有些实现并不能实现这个转换(因为affine维度不同)
pytorch源码

LRN

LRN-2012:ImageNet Classification with Deep ConvolutionalNeural Networks

什么是 Local Response Normalization(LRN,局部响应归一化)

我们都知道,Normalization是深度神经网络中一项最常用也是最终要的技术之一。早在Alexnet时代,Normalization便已经被应用到了视觉分类任务中,也就是本节所要介绍的LRN。虽然后来的BN的出现,逐渐替代了LRN,但是在这里也简单地介绍以下。说到为什么要使用LRN就不得不提到神经生物学中的一个概念叫做 lateral inhibition(横向抑制),简单来讲就是兴奋的神经细胞抑制周围神经细胞的能力。应用到深度神经网络中,这种横向抑制的目的是进行局部对比度增强,以便将局部特征在下一层得到表达。

公式如下,参考pytorch1.7源码(torch.nn.LocalResponseNorm和torch.nn.functional.local_response_norm):

        b_{c} = a_{c}\left(k + \frac{\alpha}{n}
        \sum_{c'=\max(0, c-n/2)}^{\min(N-1,c+n/2)}a_{c'}^2\right)^{-\beta}

Pytorch 源码中实现了LRN的操作,可以看到与Alexnet论文中的方法略有不同,α所乘的项由所有数值的平方和变成了所有数值的平方均值,但无论是使用平方的均值还是平方和,两者都起到了 lateral inhibition(横向抑制)的作用,也就相差常数倍,且n为超参数,所以本质没有不同。

那么使用LRN对我们的卷积神经网络有多大的提升呢。Alexnet论文中有这么一段话:在Imagenet数据集上获得了%1.4左右的提升,在CIFAR数据集上获得了2%的提升。提升效果还是比较明显。

附:AlexNet将LeNet的思想发扬光大,把CNN的基本原理应用到了很深很宽的网络中。AlexNet主要使用到的新技术点如下。

(1)成功使用ReLU作为CNN的激活函数,并验证其效果在较深的网络超过了Sigmoid,成功解决了Sigmoid在网络较深时的梯度弥散问题。虽然ReLU激活函数在很久之前就被提出了,但是直到AlexNet的出现才将其发扬光大。

(2)训练时使用Dropout随机忽略一部分神经元,以避免模型过拟合。Dropout虽有单独的论文论述,但是AlexNet将其实用化,通过实践证实了它的效果。在AlexNet中主要是最后几个全连接层使用了Dropout。

(3)在CNN中使用重叠的最大池化。此前CNN中普遍使用平均池化,AlexNet全部使用最大池化,避免平均池化的模糊化效果。并且AlexNet中提出让步长比池化核的尺寸小,这样池化层的输出之间会有重叠和覆盖,提升了特征的丰富性。

(4)提出了LRN层,对局部神经元的活动创建竞争机制,使得其中响应比较大的值变得相对更大,并抑制其他反馈较小的神经元,增强了模型的泛化能力。

后期争议

在2015年 [VGG-2015]Very Deep Convolutional Networks for Large-Scale Image Recognition的&2.1部分提到LRN基本没什么用。

All hidden layers are equipped with the rectification (ReLU (Krizhevsky et al., 2012)) non-linearity.
We note that none of our networks (except for one) contain Local Response Normalisation
(LRN) normalisation (Krizhevsky et al., 2012): as will be shown in Sect. 4, such normalisation
does not improve the performance on the ILSVRC dataset, but leads to increased memory consumption and computation time. Where applicable, the parameters for the LRN layer are those
of (Krizhevsky et al., 2012).

局部响应归一化在AlexNet那篇论文里效果也不是很明显,精度提升非常有限。并且后面也提到了增加了计算开销,目前主流算法都不用了

BN

BN-2015:Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift

顾名思义,batch normalization嘛,就是“批规范化”。

正向计算流程

BN-tmp_第1张图片

反向计算图

BN-tmp_第2张图片

手推

BN-tmp_第3张图片

class BatchNormalization:
    """
    http://arxiv.org/abs/1502.03167
    本例子只论述2维情况下的BN,并且进行推导
    节选自《深度学习入门:基于Python的理论与实现》【鱼书】的第6章的代码
    https://blog.csdn.net/qq_37174526/article/details/83590474
    """
    def __init__(self, gamma, beta, momentum=0.9, running_mean=None, running_var=None):
        self.gamma = gamma
        self.beta = beta
        self.momentum = momentum
        self.input_shape = None # Conv层的情况下为4维,全连接层的情况下为2维  

        # 测试时使用的平均值和方差
        self.running_mean = running_mean
        self.running_var = running_var  
        
        # backward时使用的中间数据
        self.batch_size = None
        self.xc = None
        self.std = None
        self.dgamma = None
        self.dbeta = None

    def forward(self, x, train_flg=True):
        self.input_shape = x.shape
        if x.ndim != 2:
            N, C, H, W = x.shape
            x = x.reshape(N, -1)

        out = self.__forward(x, train_flg)
        
        return out.reshape(*self.input_shape)
            
    def __forward(self, x, train_flg):
        if self.running_mean is None:
            N, D = x.shape
            self.running_mean = np.zeros(D)
            self.running_var = np.zeros(D)
                        
        if train_flg:
            mu = x.mean(axis=0)
            xc = x - mu
            var = np.mean(xc**2, axis=0)
            std = np.sqrt(var + 10e-7)
            xn = xc / std
            
            self.batch_size = x.shape[0]
            self.xc = xc
            self.xn = xn
            self.std = std
            self.running_mean = self.momentum * self.running_mean + (1-self.momentum) * mu
            self.running_var = self.momentum * self.running_var + (1-self.momentum) * var            
        else:
            xc = x - self.running_mean
            xn = xc / ((np.sqrt(self.running_var + 10e-7)))
            
        out = self.gamma * xn + self.beta 
        return out

    def backward(self, dout):
        if dout.ndim != 2:
            N, C, H, W = dout.shape
            dout = dout.reshape(N, -1)

        dx = self.__backward(dout)

        dx = dx.reshape(*self.input_shape)
        return dx

    def __backward(self, dout):
        dbeta = dout.sum(axis=0)
        dgamma = np.sum(self.xn * dout, axis=0)
        dxn = self.gamma * dout
        dxc = dxn / self.std
        dstd = -np.sum((dxn * self.xc) / (self.std * self.std), axis=0)
        dvar = 0.5 * dstd / self.std
        dxc += (2.0 / self.batch_size) * self.xc * dvar
        dmu = np.sum(dxc, axis=0)
        dx = dxc - dmu / self.batch_size
        
        self.dgamma = dgamma
        self.dbeta = dbeta
        
        return dx

后期争议

[BN-2018]NeurIPS-2018-how-does-batch-normalization-help-optimization-Paper
论文提出:否定了BN的背后原理是因为其减少了ICS的问题。在这篇文章中,作者通过两个实验验证了ICS和BN的关系非常小的观点。
具体还可以看:https://spaces.ac.cn/archives/6992/comment-page-1
BN主要作用是使得整个损失函数的landscape更为平滑,从而使得我们可以更平稳地进行训练

你可能感兴趣的:(AI,other)