pytorch实现卷积层和BN层融合

批归一化

数据的规范化也即 ( x − m e a n ( x ) / v a r ( x ) ) (x-mean(x)/var(x)) (xmean(x)/var(x)) 可以将数据的不同特征规范到均值为0,方差相同的标准正态分布,这就可以避免大数造成得数据溢出。可以使网络以更高得学习率更快得收敛。
如果仅仅对输入的图片进行归一化,后面其余网络层的输出不进行相关操作,那么随着网络深度得增加,数据得分布会逐渐偏离标准正态分布,由于梯度得反向传播是不同层得梯度不断相乘得结果,数据过小或过大容易造成梯度消失或梯度爆炸。
为了解决上述问题,有人提出了对网络层的中间输出进行规范化得方法,也就是批规范化(Batch Normalization),对某个网络层的批规范化,通常是在加权和与激活函数运算之间进行的。也就是:
s i g m o i d ( B N ( x W + b ) ) = s i g m o i d ( B N ( z ) ) sigmoid(BN(xW+b))=sigmoid(BN(z)) sigmoid(BN(xW+b))=sigmoid(BN(z)) 但是如果将 z z z 规范到 N ( 0 , 1 ) N(0,1) N(0,1) 的标准正态分布,那么无论前面的网络如何变化,经过这个这个层后都将服从标准正态分布,批规范化引入了可学习的参数 β 、 γ \beta、 \gamma βγ ,将规范到 N ( 0 , 1 ) N(0,1) N(0,1) 的标准正态分布变换到 N ( β , γ ) N(\beta,\gamma) N(β,γ) 由于 β 、 γ \beta、 \gamma βγ 是可学习的,所以就避免了模型能力降低的问题:
x ^ i = γ x i − μ σ 2 + ϵ + β \hat{x}_{i}=\gamma \frac{x_{i}-\mu}{\sqrt{\sigma^{2}+\epsilon}}+\beta x^i=γσ2+ϵ xiμ+β其中 μ = 1 n ∑ x i σ 2 = 1 n ∑ ( x i − μ ) 2 \begin{array}{c} \mu=\frac{1}{n} \sum x_{i} \\ \sigma^{2}=\frac{1}{n} \sum\left(x_{i}-\mu\right)^{2} \end{array} μ=n1xiσ2=n1(xiμ)2

BN和卷积层融合

在图像处理过程中,BN经常用于卷积之后,可以考虑将两个融合在在一起 ,简单点就是将是合二为一(融合层通常用测试阶段)。那么合二为一新的网络层的网络权重和偏置该怎么初始化呢?根据卷积层和BN层的相关参数进行计算得到新的权重和偏置,公式入下: y i = x i − μ σ 2 + ϵ ⋅ γ + β = w ∗ x i + b − μ σ 2 + ϵ ⋅ γ + β = w ∗ γ σ 2 + ϵ ⋅ x + ( b − μ σ 2 + ϵ ⋅ γ + β ) ( 1 ) \begin{aligned} y_{i} &=\frac{x_{i}-\mu}{\sqrt{\sigma^{2}+\epsilon}} \cdot \gamma+\beta \\ &=\frac{w * x_{i}+b-\mu}{\sqrt{\sigma^{2}+\epsilon}} \cdot \gamma+\beta \\ &=\frac{w * \gamma}{\sqrt{\sigma^{2}+\epsilon}} \cdot x+\left(\frac{b-\mu}{\sqrt{\sigma^{2}+\epsilon}} \cdot \gamma+\beta\right) \end{aligned} (1) yi=σ2+ϵ xiμγ+β=σ2+ϵ wxi+bμγ+β=σ2+ϵ wγx+(σ2+ϵ bμγ+β)(1)
简化公式结构为 y i = w n e w x + b n e w y_{i} = w_{new}x+b_{new} yi=wnewx+bnew.
w new  = w ∗ γ σ 2 + ϵ b new  = ( b − μ σ 2 + ϵ ⋅ γ + β ) ( 2 ) \begin{array}{l} w_{\text {new }}=\frac{w * \gamma}{\sqrt{\sigma^{2}+\epsilon}} \\ b_{\text {new }}=\left(\frac{b-\mu}{\sqrt{\sigma^{2}+\epsilon}} \cdot \gamma+\beta\right) \end{array} (2) wnew =σ2+ϵ wγbnew =(σ2+ϵ bμγ+β)(2) 其中 w , b w,b wb为卷积层的权重和偏置, γ 、 β , μ 、 σ 2 \gamma、\beta,\mu、\sigma^{2} γβμσ2 B N BN BN层的权重、偏置、 m e a n 、 v a r mean、var meanvar对应的参数

pytorch代码

pytorch 中 Conv2 和 BN 层中参数的的具体意义
pytorch参数详解

import torch
def fuse_conv_and_bn(conv, bn):
	# 初始化
	fusedconv = torch.nn.Conv2d(
		conv.in_channels,
		conv.out_channels,
		kernel_size=conv.kernel_size,
		stride=conv.stride,
		padding=conv.padding,
		bias=True
	)
	w_conv = conv.weight.clone().view(conv.out_channels, -1)
	w_bn = torch.diag(bn.weight.div(torch.sqrt(bn.eps+bn.running_var)))
	# 融合层的权重初始化(W_bn*w_conv(卷积的权重))
	fusedconv.weight.copy_( torch.mm(w_bn, w_conv).view(fusedconv.weight.size()) )

	if conv.bias is not None:
		b_conv = conv.bias
	else:
		b_conv = torch.zeros( conv.weight.size(0) )
	b_bn = bn.bias - bn.weight.mul(bn.running_mean).div(torch.sqrt(bn.running_var + bn.eps))
	# 融合层偏差的设置
	fusedconv.bias.copy_( torch.matmul(w_bn, b_conv) + b_bn )

	return fusedconv

那么,融合层计算出来的结果和分层计算出来的结果有多大差距呢??从下面代码可以看到用人家预训练好的相关参数进行融合,融合层和分层计算得出的结果所差无几嘛。

import torchvision
torch.set_grad_enabled(False)
x = torch.randn(32, 3, 256, 256)   # batch,channel,w,h
rn18 = torchvision.models.resnet18(pretrained=True)
rn18.eval()
net = torch.nn.Sequential(
    rn18.conv1,
    rn18.bn1
)
y1 = net.forward(x)
fusedconv = fuse_conv_and_bn(net[0], net[1])
y2 = fusedconv.forward(x)
# torch.norm()是求范数,没有指定维度则是对所有元素求二范数
d = (y1 - y2).norm().div(y1.norm()).item()
print("error: %.8f" % d)

>>>error: 0.00000022

那么问题又出来了,既然所查无几,为什么要费劲的去融合他呢,那我们再想想深度网络运行时间是不是我们担心的问题呢,走起,去验证一下时间上有无改进吧。
这里就假设一次输入32张图片,测试了300次。可以看出,在速度提升上还是蛮大的。

def cal_time(fun):
    def inner(*args,**kwargs):
        t1 = time.time()
        fun(*args,**kwargs)
        t2 = time.time()
        print(f"运行时间:{t2-t1}")    
    return inner

@cal_time
def main(net,x):
    for i in tqdm(range(300)): # 假设一次输入32张图片,测试了300次。
        y = net.forward(x)
        
main(net,x)
main(fusedconv,x)

>>>93.39383339
>>>69.93862175

那么事在训练的时候融合还是测试的时候融合呢,那肯定是用在测试阶段,前面说过融合层的权重和偏置要根据训练好的卷积、BN层的参数确定。这样测试阶段使用融合后的网络进行推理就可以加快推理速度。对于有些实时检测场所,速度越快岂不是更好。
参考链接: Fusing batch normalization and convolution in runtime
小白一枚,如果有问题,希望大家提出来。谢谢。

  • 0
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值