BatchNormal推导和yolo源码解析

最近一直在看yolo算法,感觉有时候只有看懂各种算法底层的实现,才能真正了解这个算法,这不,结合yolo源码和网上各种算法讲解,各种深度算法不再是模糊的印象了,而这里总结了最近看的BatchNormal

1.batchnormal解决了什么问题

1)梯度消失问题
对于网络任一层l,输入 al1 a l − 1 ,一般都会经历一下过程:
z=wal1+b z = w a l − 1 + b
输出: al=σ(z) a l = σ ( z ) ,
那么问题来了,假设激活函数 σ σ 为逻辑函数:
img
如果某层的z分布在逻辑函数两边的接近水平的地方,也就是梯度接近0的位置,那么网络反向传播这层后,梯度降为接近0,梯度消失,那么参数,也就基本不更新了.
这里写图片描述

2)参数0均值初始化导致的问题
由于初始化的时候,参数一般都是0均值的,因此开始的拟合y=Wx+b,基本过原点附近,如图b红色虚线。因此,网络需要经过多次学习才能逐步达到如紫色实线的拟合,即收敛的比较慢。如果我们对输入数据先作减均值操作,如图c,显然可以加快学习。更进一步的,我们对数据再进行去相关操作,使得数据更加容易区分,这样又会加快训练,如图d。而batchnormal也有这种效果
img

3)内部迁移(Internal Covariate Shift)
数据经过多层神经网络后,数据分布发生变化,导致各层参数需要不到调整适应分布变化,这会让收敛速度变慢,超参数设定也变得比较复杂,这在论文里作者称作Internal Covariate Shift

那么batchnormal具体如何解决上述的呢,首先了解前行传播

注意以下字母皆为矩阵形式,i为batch里面的索引

2.BN前向传播

这里写图片描述
设有 mini m i n i _ batch b a t c h 的batch_size为m,注意这里的i为batch里面的索引,x和a为特征矩阵
由于当前层的输入等于上一层的输出,那么设第l层输出为 al a l ,则第l层的输入 xli=al1i, x i l = a i l − 1 ,   i =0,1,2…m

前向传播过程:
1.全连接则乘权重,卷积层则对x卷积

zli=(wli)Tal1i//(1.1) (1.1) z i l = ( w i l ) T a i l − 1 / / 偏 重 放 后 面 加

2.计算batch_size个z的均值
μlB=1mi=1mzli(1.2) (1.2) μ B l = 1 m ∑ i = 1 m z i l

3)计算batch_size个z的方差:
(D2B)(l)=1mi=1m(zliμlB)2(1.3) (1.3) ( D B 2 ) ( l ) = 1 m ∑ i = 1 m ( z i l − μ B l ) 2

3)将batch_size个z,归一化成均值为0,方差为1的分布:
BN=ẑ li=zliμlB(D2B)(l)+ε(1.4) (1.4) B N = z ^ i l = z i l − μ B l ( D B 2 ) ( l ) + ε

经过这个操作后数据就被分布在0为圆心,1为半径的范围内了,这样以上 问题就被成功解决了问题
这里写图片描述

4)放缩和迁移:

yli=γẑ li+β(1.5) (1.5) y i l = γ z ^ i l + β

这步的作用在于,以逻辑函数为例,经过1.3后,数据主要分布在线性区域,非线性表达能力会受到影响,所以通过对数据放大或缩小和迁移来进入非线性区域范围
5)激活,输出
ali=σ(yli)(1.6) (1.6) a i l = σ ( y i l )

yolo的前行传播源码:

void forward_batchnorm_layer(layer l, network net)
{
    //如果是batchnormal层,则直接输出等于输入
    if(l.type == BATCHNORM) copy_cpu(l.outputs*l.batch, net.input, 1, l.output, 1);
    //全链接层,看成通道数为l.outputs,特征图长宽为1
    if(l.type == CONNECTED){
        l.out_c = l.outputs;
        l.out_h = l.out_w = 1;
    }

    //l.x=l.output,如果按方差求导最终的化简等于0,来计算,l.x后面就用不到了
    copy_cpu(l.outputs*l.batch, l.output, 1, l.x, 1);

    //训练状态
    if(net.train){
        //求当前batch的均值,对应公式1.2
        mean_cpu(l.output, l.batch, l.out_c, l.out_h*l.out_w, l.mean);
        //求当前batch的方差,对应公式1.3
        variance_cpu(l.output, l.mean, l.batch, l.out_c, l.out_h*l.out_w, l.variance);
        //求均值的滚动平均,预测时,均值的就是这个值,什么是滚动平均,见下面注
        scal_cpu(l.out_c, .99, l.rolling_mean, 1);
        axpy_cpu(l.out_c, .01, l.mean, 1, l.rolling_mean, 1);

        //求方差的滚动平均,预测时,方差用的就是这个值,可以看非训练状态时normalize_cpu()函数的实现和参数
        scal_cpu(l.out_c, .99, l.rolling_variance, 1);
        axpy_cpu(l.out_c, .01, l.variance, 1, l.rolling_variance, 1);

        //对应公式1.4
        normalize_cpu(l.output, l.mean, l.variance, l.batch, l.out_c, l.out_h*l.out_w);   

        //将1.4式子的结果保存到l.x_norm,用于反向传播时相关参数梯度的计算
        copy_cpu(l.outputs*l.batch, l.output, 1, l.x_norm, 1);
    } 
    //非训练状态,如预测时
    else {
        //对应公式1.4
        normalize_cpu(l.output, l.rolling_mean, l.rolling_variance, l.batch, l.out_c, l.out_h*l.out_w);
    }
    //这两步,对应公式1.5,这里l.scale对应gamma,l.biases对应beta
    scale_bias(l.output, l.scales, l.batch, l.out_c, l.out_h*l.out_w);
    add_bias(l.output, l.biases, l.batch, l.out_c, l.out_h*l.out_w);
}

注:
滚动平均:

x¯n=x1+x2++xmm=x1+x2++xm1+xmm=x1+x2++xm1m+xmm=(x1+x2++xm1)(m1)m(m1)+xmm=m1mx¯m1+xmm=(11m)x¯m1+1mxm x ¯ n = x 1 + x 2 + ⋅ ⋅ ⋅ + x m m = x 1 + x 2 + ⋅ ⋅ ⋅ + x m − 1 + x m m = x 1 + x 2 + ⋅ ⋅ ⋅ + x m − 1 m + x m m = ( x 1 + x 2 + ⋅ ⋅ ⋅ + x m − 1 ) ( m − 1 ) m ( m − 1 ) + x m m = m − 1 m x ¯ m − 1 + x m m = ( 1 − 1 m ) x ¯ m − 1 + 1 m x m

这里 x¯n x ¯ n 表示前n个数据的平均值
yolo在这里直接将 1n=0.1 1 n = 0.1 ,简化计算,
至于为什么可以用这个均值来近似代替整个数据集的分分布,见后面的预测解释,推导

3.BN反向传播过程

首先推导几个值,为后面链式求导用:
设最终的损失函数为 C C
对方差求导:

C(DB2)l=i=1m(Cz^ilz^il(DB2)l)=i=1m{12Czil+1zil+1ailailyilyilz^il(zilμBl)[(DB2)(l)+ε]32}=i=1m{12[δl+1zil+1ail(σ(yil)γil)](zilμBl)[(DB2)(l)+ε]32}(2.1)=γli=1m{12[δl+1zil+1ailσ(yil)](zilμBl)[(DB2)(l)+ε]32}

对均值求导:
这里需用到一个复合函数求导的方法:
这里写图片描述

所以按这个方法求导:

CμlB=i=1m(Czi^zi^μlB+CD2BD2BμlB)=i=1m(Czi^1(D2B)l+ε)+C(D2B)l2mim(zliμlB)=i=1m(Czi^1(D2B)l+ε)=i=1m(Czl+1izl+1ialialiyliyliẑ li1(D2B)l+ε)=γli=1m([δl+1zl+1ialiσ(yli)]1(D2B)l+ε)(2.2) ∂ C ∂ μ B l = ∑ i = 1 m ( ∂ C ∂ z i ^ z i ^ ∂ μ B l + ∂ C ∂ D B 2 ∂ D B 2 ∂ μ B l ) = ∑ i = 1 m ( ∂ C ∂ z i ^ − 1 ( D B 2 ) l + ε ) + ∂ C ∂ ( D B 2 ) l ⋅ − 2 m ⋅ ∑ i m ( z i l − μ B l ) = ∑ i = 1 m ( ∂ C ∂ z i ^ − 1 ( D B 2 ) l + ε ) = ∑ i = 1 m ( ∂ C ∂ z i l + 1 ∂ z i l + 1 ∂ a i l ∂ a i l ∂ y i l ∂ y i l ∂ z ^ i l − 1 ( D B 2 ) l + ε ) (2.2) = γ l ⨀ ∑ i = 1 m ( [ δ l + 1 ∂ z i l + 1 ∂ a i l ⨀ σ ( y i l ) ′ ] − 1 ( D B 2 ) l + ε )


δli=Czli=Cẑ liẑ lizli+CD2BD2Bzli+CμBμBzli=Cẑ li1D2B+ε+CD2B2m(zliμB)+CμB1m=Czl+1izl+1ialialiyliyliẑ li1D2B+ε+CD2B2m(zliμB)+CμB1m=[δl+1zl+1iali(σ(yli)γli)]1D2B+ε+CD2B2m(zliμB)+CμB1m(2.3) δ i l = ∂ C ∂ z i l = ∂ C ∂ z ^ i l ∂ z ^ i l ∂ z i l + ∂ C ∂ D B 2 ∂ D B 2 ∂ z i l + ∂ C ∂ μ B ∂ μ B ∂ z i l = ∂ C ∂ z ^ i l 1 D B 2 + ε + ∂ C ∂ D B 2 ⋅ 2 m ⋅ ( z i l − μ B ) + ∂ C ∂ μ B ⋅ 1 m = ∂ C ∂ z i l + 1 ∂ z i l + 1 ∂ a i l ∂ a i l ∂ y i l ∂ y i l ∂ z ^ i l 1 D B 2 + ε + ∂ C ∂ D B 2 ⋅ 2 m ⋅ ( z i l − μ B ) + ∂ C ∂ μ B ⋅ 1 m (2.3) = [ δ l + 1 ∂ z i l + 1 ∂ a i l ⨀ ( σ ( y i l ) ′ ⨀ γ i l ) ] 1 D B 2 + ε + ∂ C ∂ D B 2 ⋅ 2 m ⋅ ( z i l − μ B ) + ∂ C ∂ μ B ⋅ 1 m

则,以全连接层为例,求权重和偏差梯度:

Cwl=i=1mCzlizliwl=i=1m(al1)Tδl(2.4) (2.4) ∂ C ∂ w l = ∑ i = 1 m ∂ C ∂ z i l ∂ z i l ∂ w l = ∑ i = 1 m ( a l − 1 ) T δ l

Cβl=i=1mCzl+1izl+1ialialiyliyliβl=i=1mδl+1izl+1iali[σ(yli))ylβl]=i=1mδl+1izl+1ialiσ(yli)(2.5) (2.5) ∂ C ∂ β l = ∑ i = 1 m ∂ C ∂ z i l + 1 ∂ z i l + 1 ∂ a i l ∂ a i l ∂ y i l ∂ y i l ∂ β l = ∑ i = 1 m δ i l + 1 ∂ z i l + 1 ∂ a i l ⨀ [ σ ( y i l ) ′ ) ∂ y l ∂ β l ] = ∑ i = 1 m δ i l + 1 ∂ z i l + 1 ∂ a i l ⨀ σ ( y i l ) ′

同理:

Cγl=i=1mCzl+1izl+1ialialiyliyliγl=i=1mδl+1izl+1iali[σ(yli)ẑ li](2.6) (2.6) ∂ C ∂ γ l = ∑ i = 1 m ∂ C ∂ z i l + 1 ∂ z i l + 1 ∂ a i l ∂ a i l ∂ y i l ∂ y i l ∂ γ l = ∑ i = 1 m δ i l + 1 ∂ z i l + 1 ∂ a i l ⨀ [ σ ( y i l ) ′ ⨀ z ^ i l ]

对应yolo代码:

void backward_batchnorm_layer(layer l, network net)
{
    //非训练状态
    if(!net.train){
        l.mean = l.rolling_mean;
        l.variance = l.rolling_variance;
    }
    //求偏差beta的梯度,对应公式2.5
    backward_bias(l.bias_updates, l.delta, l.batch, l.out_c, l.out_w*l.out_h);

    //求gamma梯度,对应公式2.6
    backward_scale_cpu(l.x_norm, l.delta, l.batch, l.out_c, l.out_w*l.out_h, l.scale_updates);


     //先计算公式2.3里面的公共项gamma
    scale_bias(l.delta, l.scales, l.batch, l.out_c, l.out_h*l.out_w);

    //求y对均值的导数,对应公式2.2
    mean_delta_cpu(l.delta, l.variance, l.batch, l.out_c, l.out_w*l.out_h, l.mean_delta);

    //求y对方差的导数,对应公式2.1,这里按上面化简后的公式,若激活函数为relu应该直接等于0
    variance_delta_cpu(l.x, l.delta, l.mean, l.variance, l.batch, l.out_c, l.out_w*l.out_h, l.variance_delta);

    //求权重的误差度,对应公式2.3
    normalize_delta_cpu(l.x, l.mean, l.variance, l.mean_delta, l.variance_delta, l.batch, l.out_c, l.out_w*l.out_h, l.delta);

     //对于BATCHNORM层,直接输出等于输入
    if(l.type == BATCHNORM) copy_cpu(l.outputs*l.batch, l.delta, 1, net.delta, 1);
}

4.预测:

预测时,计算总体的均值和方差是不实际的,也是无法实现的,因为无法采样到所有样本。用总采样来估计总体的均值和方差呢?也是需要大量计算的,在训练过程中的batch下的均值uB和方差σB,可以加以利用来估计总体
具体推导如下:

E(Xi).D(Xi) E ( X i ) . D ( X i ) :可以理解为 Xi X i 所在分布的期望值

样本均值:

μA=X¯=1mi=1mXi(3.1) (3.1) μ A = X ¯ = 1 m ∑ i = 1 m X i

因为抽样和样本同分布,所以:
样本期望:
μ=E(Xi)=E(μA)=E(x)(3.2) (3.2) μ = E ( X i ) = E ( μ A ) = E ( x )

μA=E(XAi)E(Xi)(3.3) (3.3) μ A = E ( X A i ) ≠ E ( X i )

样本方差: σ2=D(Xi)=D(x)(3.4) (3.4) σ 2 = D ( X i ) = D ( x ) : 所 以 : μ=1nni=1E(Xi)(3.5) (3.5) μ = 1 n ∑ i = 1 n E ( X i ) $


均值和方差:

E(μA)=E(1mi=1mXi)=1zmni=1mnXi=E(x)(3.6) (3.6) E ( μ A ) = E ( 1 m ∑ i = 1 m X i ) = 1 z m n ∑ i = 1 m n X i = E ( x )

D(μA)=D(1mi=1mXi)=1m2i=1mD(Xi)=1mD(x)(3.7) (3.7) D ( μ A ) = D ( 1 m ∑ i = 1 m X i ) = 1 m 2 ∑ i = 1 m D ( X i ) = 1 m D ( x )

E(σ2A)=E[i=1m(XiμA)21m]=1mE{i=1m[XiE(x)+E(x)μA]2}=1mE{i=1m[(XiE(x))22(XiE(x)(μAE(x)+(μAE(x))2]}=1mE{i=1m[(XiE(x))2]2m(μAE(x))2+m(μAE(x))2}=1mE{i=1m[(XiE(x))2]m(μAE(x))2}= D(x)E{[μAE(μA)]2}=D(x)D(μA)=m1mD(x)(1) E ( σ A 2 ) = E [ ∑ i = 1 m ( X i − μ A ) 2 ⋅ 1 m ] = 1 m ⋅ E { ∑ i = 1 m [ X i − E ( x ) + E ( x ) − μ A ] 2 } = 1 m ⋅ E { ∑ i = 1 m [ ( X i − E ( x ) ) 2 − 2 ( X i − E ( x ) ( μ A − E ( x ) + ( μ A − E ( x ) ) 2 ] } = 1 m ⋅ E { ∑ i = 1 m [ ( X i − E ( x ) ) 2 ] − 2 m ( μ A − E ( x ) ) 2 + m ( μ A − E ( x ) ) 2 } = 1 m ⋅ E { ∑ i = 1 m [ ( X i − E ( x ) ) 2 ] − m ( μ A − E ( x ) ) 2 } =   D ( x ) − E { [ μ A − E ( μ A ) ] 2 } = D ( x ) − D ( μ A ) (1) = m − 1 m D ( x )

即: D(x)=mm1E(σ2A)(3.8) (3.8) D ( x ) = m m − 1 E ( σ A 2 )

所以最终结果:
μ=E(x)=E(μB)=1KKBμB(K) (K为总迭代次数) μ = E ( x ) = E ( μ B ) = 1 K ∑ B K μ B
D(x)=mm1E(σ2A)(3.8) (3.8) D ( x ) = m m − 1 E ( σ A 2 ) tag{m为batch_size}$

虽然理论上是这样的,但yolo里面好像用滚动平均值来算,而不是上面的计算方法这部分计算对应前行传播函数forward_batchnorm_layer()里的:

scal_cpu(l.out_c, .99, l.rolling_mean, 1);
axpy_cpu(l.out_c, .01, l.mean, 1, l.rolling_mean, 1);
scal_cpu(l.out_c, .99, l.rolling_variance, 1);
axpy_cpu(l.out_c, .01, l.variance, 1, l.rolling_variance, 1);

Bacthnormal优化办法

1)增大学习率.
BN能减少每层的梯度变化幅度,使梯度稳定在理想的变化范围内,所以大学习率一般不会导致梯度消失
另外大学习率,训练一次,一般会导致参数,如权重变大,假设变大了n倍,即 W=nW W ′ = n W ,其中W’为目前权重,W’为上次训练的权重

设上一次训练,均值为:
μB=1mmi=1Wal1 μ B = 1 m ∑ i = 1 m W a l − 1
方差:
D2B=1mmi=1(Wal1μB)2 D B 2 = 1 m ∑ i = 1 m ( W a l − 1 − μ B ) 2

则本次均值:
μB=1mmi=1nWal1=nμB μ B ′ = 1 m ∑ i = 1 m n W a l − 1 = n μ B
(D2B)=1mmi=1(nWal1μB)2=n2D2B ( D B 2 ) ′ = 1 m ∑ i = 1 m ( n W a l − 1 − μ B ′ ) 2 = n 2 D B 2

所以:
BN(Wal1)=BN(nWal1) B N ( W a l − 1 ) = B N ( n W a l − 1 )
BN(nWal1)(nW)=1nBN(Wal1)W ∂ B N ( n W a l − 1 ) ∂ ( n W ) = 1 n ∂ B N ( W a l − 1 ) ∂ W
即:
BN(Wal1)(W)=1nBN(Wal1)W ∂ B N ( W ′ a l − 1 ) ∂ ( W ′ ) = 1 n ∂ B N ( W a l − 1 ) ∂ W
可以看出,当因上一次训练的大学习率导致权重W变大n倍后,只会让本次训练的梯度更小,这样一点程度上,便避免了过大学习率导致的梯度爆炸.

2)去掉Dropout_
3)减少L2正则项_,这里不是很理解,难道两者冲突了?
4)提高学习率衰减速度.由于BN收敛速度快,在相同迭代次数,使用BN算法的网络,比未使用相同BN算法的网络,会更快到达相应的衰减点
5)更彻底随机化训练数据._
6)减少图片扭曲_

看了好久,终于略懂一二 :D
主要参考:
论文
Batch Normalize的几点说明
https://zhuanlan.zhihu.com/p/27938792

### 回答1: YOLO(You Only Look Once)是一种实时目标检测算法,它以较快的速度在图像或视频中检测和定位物体。YOLO算法采用了全卷积神经网络结构,将图像分割为网格,并在每个网格中预测多个边界框和类别得分。 YOLO源码解析PDF是指对YOLO算法的实现细节进行分析和解读的文档。这个文档可能包含YOLO算法的整体结构、网络架构以及训练和测试过程的详细说明。 在解析YOLO源码时,可能会介绍YOLO的网络结构,如何进行前向传播和反向传播,以及如何计算损失函数。此外,文档还可能讨论YOLO算法中使用的各种技巧和改进,例如使用Anchor Box、多尺度检测和类别平衡等。 在解析YOLO源码的过程中,还可能讲解YOLO算法中一些关键的模块和技术,如Darknet网络结构、卷积层、池化层以及非极大值抑制等。 了解YOLO源码的设计和实现细节,有助于我们深入理解YOLO算法的原理和优缺点,以及在实际应用中如何进行参数调整和算法优化。 需要注意的是,YOLO算法源码解析可能比较复杂,需要具备一定的计算机视觉和深度学习知识才能进行理解和分析。因此,对于初学者来说,可能需要花费一定的时间和精力才能完全理解和掌握。 ### 回答2: "YOLO源码解析"是一本关于YOLO(You Only Look Once)目标检测算法的PDF书籍。YOLO是一种非常流行的实时目标检测算法,具有快速和高准确率的特点。 该书籍涵盖了YOLO算法的完整源码解析过程,包括算法的核心思想、实现细节和技术原理。通过学习这本书籍,读者可以深入了解YOLO算法的设计目标、算法流程、网络结构和训练过程。 书籍首先介绍了YOLO算法的基本原理,即将目标检测问题转化为一个回归问题,并使用单个神经网络来同时进行目标分类和边界框回归。然后详细解释了YOLO算法中所使用的网络结构和各个组件的作用,包括卷积层、池化层、全连接层等。 接下来,该书籍对YOLO算法的具体实现进行了解析。它详细介绍了如何对输入图像进行预处理和数据增强,以及如何训练网络模型和优化损失函数。此外,书籍还讨论了如何处理不同尺度和不同类别的目标,并如何自适应地调整检测框的大小和位置。 除了算法的实现细节,该书籍还涉及了YOLO算法的一些改进和扩展,如YOLOv2和YOLOv3。它介绍了这些改进算法的设计思路和性能提升,并给出了实验结果和比较分析。 总的来说,“YOLO源码解析”这本PDF书籍是一本深入解析YOLO目标检测算法的权威指南。通过阅读此书,读者可以系统地了解YOLO算法的原理、源码实现和改进方法,为进一步的研究和应用打下坚实的基础。 ### 回答3: YOLO源码解析PDF是一份解析YOLO算法代码的文件,目的是帮助读者深入理解YOLO算法的原理和实现细节。 首先,YOLO(You Only Look Once)是一种实时目标检测算法,它的主要特点是将目标检测任务转化为一个回归问题,通过一个神经网络模型直接在图像上预测目标的位置和类别。 该PDF文件首先会介绍YOLO算法的整体结构和工作原理,包括输入图像的预处理、网络的构建以及输出结果的解码过程。它会详细解释YOLO网络是如何通过卷积和池化层来提取图像的特征,并将这些特征映射到不同尺度的特征图上。同时,该文件还会讲解如何使用anchors来回归预测框的位置。 另外,该PDF还会对YOLO源码的实现细节进行深入解析,包括网络的结构定义、前向传播过程、损失函数的定义和反向传播过程。它会讲解YOLO如何通过多个尺度的特征图来检测不同尺寸的目标,并如何利用置信度来判断预测框的置信度。 此外,该文件还会介绍YOLO源码中一些重要的技术细节,比如数据增强、类别的处理、非极大值抑制等。这些细节对于理解算法的性能提升和调优具有重要意义。 通过对YOLO源码的深入解析,读者可以更全面地理解该算法的原理和实现方法,并有助于读者在实际应用中根据自身需求进行算法改进和优化。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值