第二十四章 原理篇:DBB

news2024/9/29 19:26:52

上班真的好累哦!
理论上应该从RepVGG开始写重参化的,而且上星期就打算写来着!
但是上班真的好累哦完全提不起精神在周末打字看论文!

参考教程:
https://arxiv.org/pdf/2103.13425.pdf
https://github.com/DingXiaoH/DiverseBranchBlock


文章目录

  • 介绍
  • 原理
    • conv-bn => conv
    • branch addition => conv
    • sequential conv => conv
    • depth concatenation=>conv
    • avgpooling => conv
    • multi-scale conv=>conv
  • 代码实现
    • transforms
      • conv-bn => conv
      • branch addition => conv
      • sequential conv => conv
      • depth concatenation=>conv
      • avgpooling => conv
      • multi-scale conv=>conv
    • DiverseBranchBlock
      • init
      • get_equivalent_kernel_bias

介绍

diverse branch block,就如同它的名称一样,是一个block形式的设计,这个block有多种分支组合,比如卷积、多尺度卷积、平均池化等。在训练阶段是正常的每个分支都会用到,在inference阶段则会将多分支合并到一起,变成一个卷积层来进行部署,从而实现相对于单卷积层的模型,在参数量不增加的情况下,带来明显的效果提升。

早在inception net时期就已经揭示过多分支结构和不同尺度复杂度支路的组合给模型的特征提取能带来明显效果提升。但是这种复杂的结构,在inference阶段就会显得有点累赘,因为小操作太多了,与gpu的并行计算能力不太匹配,从而速度上会有所下降。

由于业务需要或硬件限制,我们并不能无限地提升我们要训练的卷积网络的大小。在评价一个网络的水平时,一般也会对它的表现、时间损耗和计算量等多方面进行评估。

作者使用重参数化的方法,在卷积网络中插入复杂的模块来提高它的表现能力,同时保证原来的预测时间不会增加。通过“在训练阶段复杂化模型并在预测阶段将它还原”的方法解耦训练时间和预测时间,基于这一目的,对模型修改后的结构做出了以下两个要求:

  1. 能够有效提升模型的表现。
  2. 能够成功被还原为原结构。
    在这里插入图片描述
    上图是一个dbb结构的示意图,在途中dbbblock使用了四种分支,其中分别包括了1x1卷积,1x1卷积+kxk卷积,1x1卷积+avg池化,kxk卷积。在测试阶段,这四个分支被合并到一起,组成一个kxk的卷积,从而在保证结果的情况下减少参数量。

原理

dbb实现的核心原理是卷积计算的同质性和可加性。

⨂ \bigotimes 符号代表卷积,那么在两个相同结构的卷积操作上有:
I ⨂ ( p F ) = p ( I ⨂ F ) I ⨂ F ( 1 ) + I ⨂ F ( 2 ) = I ⨂ ( F ( 1 ) + F ( 2 ) ) I \bigotimes(pF) = p(I\bigotimes F)\\ I\bigotimes F^{(1)} + I \bigotimes F^{(2)} = I\bigotimes(F^{(1)}+F^{(2)}) I(pF)=p(IF)IF(1)+IF(2)=I(F(1)+F(2))
基于这一点,论文中提出来六中转换的情况。
在这里插入图片描述

conv-bn => conv

第一种转换是带bn层的卷积转成普通的卷积。这种转换是很常用的,一个featuremap经过卷积+bn的组合后,得到的输出为:
O j = ( ( I ⨂ F j ) − μ j ) γ j ο j + β j = γ j ο j ( I ⨂ F j ) − γ j ο j μ j + β j = I ⨂ ( γ j ο j F j ) − γ j ο j μ j + β j \begin{align} O_j &= ((I\bigotimes F_j) - \mu_j)\frac{\gamma_j}{\omicron_j} + \beta_j \\ & = \frac{\gamma_j}{\omicron_j} (I\bigotimes F_j)-\frac{\gamma_j}{\omicron_j}\mu_j + \beta_j \\ &=I \bigotimes(\frac{\gamma_j}{\omicron_j} F_j) -\frac{\gamma_j}{\omicron_j}\mu_j + \beta_j \end{align} Oj=((IFj)μj)οjγj+βj=οjγj(IFj)οjγjμj+βj=I(οjγjFj)οjγjμj+βj

branch addition => conv

并行的多个结构相同的卷积,根据卷积的可加性,可以直接相加合并到一起。
I ⨂ F ( 1 ) + I ⨂ F ( 2 ) = I ⨂ ( F ( 1 ) + F ( 2 ) ) I\bigotimes F^{(1)} + I \bigotimes F^{(2)} = I\bigotimes(F^{(1)}+F^{(2)}) IF(1)+IF(2)=I(F(1)+F(2))

sequential conv => conv

可以把一组连续的1x1卷积-bn-kxk卷积-bn组合成一个kxk卷积。这也是例子中最复杂的一种变换。在这里不考虑组卷积。

首先先按照bn和conv的融合,将1x1卷积,KxK卷积先分别与各自相接的bn融合在一起。
现在我们得到两个卷积层,每一层的滤波器大小分别是
F ( 1 ) ∈ R D × C × 1 × 1 F^{(1)} \in R^{D\times C\times1\times1} F(1)RD×C×1×1 F ( 2 ) ∈ R D × C × K × K F^{(2)} \in R^{D\times C\times K\times K} F(2)RD×C×K×K
那么这一层的输出可以写成:
O ′ = ( I ⨂ F ( 1 ) + R E P ( b ( 1 ) ) ) ⨂ F ( 2 ) + R E P ( b ( 2 ) ) O'= (I\bigotimes F^{(1)} + REP(b^{(1)}))\bigotimes F^{(2)} + REP(b^{(2)}) O=(IF(1)+REP(b(1)))F(2)+REP(b(2))
我们的目的是找到一个signle conv,它的kernel和bias满足:
O ′ = I ⨂ F ′ + R E P ( b ′ ) O' = I \bigotimes F' + REP(b') O=IF+REP(b)
我们来续写一下:
O ′ = ( I ⨂ F ( 1 ) + R E P ( b ( 1 ) ) ) ⨂ F ( 2 ) + R E P ( b ( 2 ) ) = I ⨂ F ( 1 ) ⨂ F ( 2 ) + R E P ( b ( 1 ) ) ⨂ F ( 2 ) + R E P ( b ( 2 ) ) \begin{align} O'&= (I\bigotimes F^{(1)} + REP(b^{(1)}))\bigotimes F^{(2)} + REP(b^{(2)})\\ &=I\bigotimes F^{(1)} \bigotimes F^{(2)} +REP(b^{(1)})\bigotimes F^{(2)} + REP(b^{(2)}) \end{align} O=(IF(1)+REP(b(1)))F(2)+REP(b(2))=IF(1)F(2)+REP(b(1))F(2)+REP(b(2))
其中 F ( 1 ) F^{(1)} F(1)是一个1x1卷积,它只实现了通道上的变化,而没有进行空间上的操作,所以可以通过重组KxK的参数实现两者的合并。

depth concatenation=>conv

inception单元在各分支最后使用concatenation的操作来堆叠组合来自不同分支的结果,假如多个分支的卷积结构相同,那么沿通道拼接结果就等价于把每个分支的卷积核沿通道拼接。

avgpooling => conv

大小为K,stride为s的平均池化,相当于使用了同样的大小为K,步长为s的卷积。只不过这个卷积核的参数是固定的。
在这里插入图片描述

multi-scale conv=>conv

通过padding的方式来实现不同大小的卷积核的融合。
在这里插入图片描述

代码实现

transforms

参考github源码看一下代码每一种transform是怎么实现的。

conv-bn => conv

实现bn和conv的融合。

def transI_fusebn(kernel, bias, bn):
    gamma = bn.weight
    std = (bn.running_var + bn.eps).sqrt()
    return kernel * ((gamma / std).reshape(-1, 1, 1, 1)), bias + bn.bias - bn.running_mean * gamma / std

和公式里写的一样,直接对参数进行乘法计算。符合卷积的同质性。

branch addition => conv

根据卷积的可加性,直接对多个卷积进行相加即可。

def transII_addbranch(kernels, biases):
    return sum(kernels), sum(biases)

sequential conv => conv

序列卷积的融合:1x1-BN-kxk-BN。我们只写group=1的情况。

def transIII_1x1_kxk(k1, b1,  k2, b2):
	k = F.conv2d(k2, k1.permute(1,0,2,3))
	b_hat = (k2*b1.reshape(1,-1,1,1)).sum((1,2,3))
	return k, b_hat +b2

depth concatenation=>conv

def transIV_depthconcat(kernels, biases):
    return torch.cat(kernels), torch.cat(biases)

avgpooling => conv

def transV_avg(channels, kernel_size, groups):
    input_dim = channels // groups
    k = torch.zeros((channels, input_dim, kernel_size, kernel_size))
    k[np.arange(channels), np.tile(np.arange(input_dim), groups), :, :] = 1.0 / kernel_size ** 2
    return k

multi-scale conv=>conv

def transVI_multiscale(kernel, target_kernel_size):
    H_pixels_to_pad = (target_kernel_size - kernel.size(2)) // 2
    W_pixels_to_pad = (target_kernel_size - kernel.size(3)) // 2
    return F.pad(kernel, [H_pixels_to_pad, H_pixels_to_pad, W_pixels_to_pad, W_pixels_to_pad])

DiverseBranchBlock

DBB类中,比较关键的两个函数:

  1. init() 在init函数中初始化了dbbblock模块,并定义了多个分支,具体来说,这几个分支分别是:dbb_origin, dbb_avg, dbb_1x1和dbb_1x1_kxk。
  2. get_equivalent_kernel_bias(): 在这个函数中进行分支的融合和转换,并获得合并成一个kxk卷积的kernel和bias。

init

我们首先来看一下init中定义的几个分支都是什么样子的结构。

  1. dbb_orgin: 简单的kernel大小为k的卷积+bn
dbb_origin = conv_bn(in_channels=in_channels, out_channels=out_channels, kernel_size=kernel_size, stride=stride, padding=padding, dilation=dilation, groups=groups)
  1. dbb_1x1:简单的kernel大小为1的卷积+bn
self.dbb_1x1 = conv_bn(in_channels=in_channels, out_channels=out_channels, kernel_size=1, stride=stride,padding=0, groups=groups)
  1. dbb_avg:有两种结构
    如果group数比out_channels小,则在最开始加上了1x1的组卷积。
self.dbb_avg.add_module('conv', nn.Conv2d(in_channels=in_channels, out_channels=out_channels, kernel_size=1,stride=1, padding=0, groups=groups, bias=False))
self.dbb_avg.add_module('bn', BNAndPadLayer(pad_pixels=padding, num_features=out_channels))
self.dbb_avg.add_module('avg', nn.AvgPool2d(kernel_size=kernel_size, stride=stride, padding=0))

否则只使用一个平均池化:

self.dbb_avg.add_module('avg', nn.AvgPool2d(kernel_size=kernel_size, stride=stride, padding=0))

最后还要跟上一个bn层。

self.dbb_avg.add_module('avgbn', nn.BatchNorm2d(out_channels))
  1. dbb_1x1_kxk:相对来说比较复杂的卷积序列。
self.dbb_1x1_kxk = nn.Sequential()
if internal_channels_1x1_3x3 == in_channels:
	self.dbb_1x1_kxk.add_module('idconv1', IdentityBasedConv1x1(channels=in_channels, groups=groups))
else:
	self.dbb_1x1_kxk.add_module('conv1', nn.Conv2d(in_channels=in_channels, out_channels=internal_channels_1x1_3x3,
	                                            kernel_size=1, stride=1, padding=0, groups=groups, bias=False))
self.dbb_1x1_kxk.add_module('bn1', BNAndPadLayer(pad_pixels=padding, num_features=internal_channels_1x1_3x3, affine=True))
self.dbb_1x1_kxk.add_module('conv2', nn.Conv2d(in_channels=internal_channels_1x1_3x3, out_channels=out_channels,
                                            kernel_size=kernel_size, stride=stride, padding=0, groups=groups, bias=False))
self.dbb_1x1_kxk.add_module('bn2', nn.BatchNorm2d(out_channels))

在训练阶段,会正常使用这几个分支,并把结果求和。在预测阶段,则需要把分支合并起来。

get_equivalent_kernel_bias

在这个部分,会对多个分支进行合并。

  1. dbb_orgin:合并conv和bn。

这个分支只有简单的conv和bn,可以使用我们transform中第一个方法直接进行合并。

k_origin, b_origin = transI_fusebn(self.dbb_origin.conv.weight, self.dbb_origin.bn)
  1. dbb_1x1:合并1x1卷积和bn。

这个分支是1x1卷积和bn,在合并完成后,还需要把合并后的kernel变成我们预期的大小。

k_1x1, b_1x1 = transI_fusebn(self.dbb_1x1.conv.weight, self.dbb_1x1.bn)
k_1x1 = transVI_multiscale(k_1x1, self.kernel_size)
  1. dbb_1x1_kxk:合并1x1-bn-kxk-bn

这个分支存在两个卷积+bn的组合。在把卷积和对应的bn分别融合后,再进行1x1卷积和3x3卷积串联的组合。

k_1x1_kxk_first, b_1x1_kxk_first = transI_fusebn(k_1x1_kxk_first, self.dbb_1x1_kxk.bn1)
        k_1x1_kxk_second, b_1x1_kxk_second = transI_fusebn(self.dbb_1x1_kxk.conv2.weight, self.dbb_1x1_kxk.bn2)
        k_1x1_kxk_merged, b_1x1_kxk_merged = transIII_1x1_kxk(k_1x1_kxk_first, b_1x1_kxk_first, k_1x1_kxk_second, b_1x1_kxk_second, groups=self.groups)
  1. dbb_avg:进行池化和bn的融合。

池化层在这里要先转换成卷积层,在和bn融合在一起。

k_avg = transV_avg(self.out_channels, self.kernel_size, self.groups)
k_1x1_avg_second, b_1x1_avg_second = transI_fusebn(k_avg.to(self.dbb_avg.avgbn.weight.device), self.dbb_avg.avgbn)

如果在定义的时候,池化层前面还使用了1x1卷积,那么还需要把这个卷积和新的池化层融合在一起。

k_1x1_avg_first, b_1x1_avg_first = transI_fusebn(self.dbb_avg.conv.weight, self.dbb_avg.bn)
            k_1x1_avg_merged, b_1x1_avg_merged = transIII_1x1_kxk(k_1x1_avg_first, b_1x1_avg_first, k_1x1_avg_second, b_1x1_avg_second, groups=self.groups)
  1. 最后的相加组合

在多分支结构中,不同分支的结果是通过加法组合在一起的,所以最后还需要一个相加的计算。

return transII_addbranch((k_origin, k_1x1, k_1x1_kxk_merged, k_1x1_avg_merged), (b_origin, b_1x1, b_1x1_kxk_merged, b_1x1_avg_merged))

最终我们得到的是一个大小为kxk的新的卷积和bias。它会直接作为我们新建的卷积层的参数,在部署/预测阶段使用。

kernel, bias = self.get_equivalent_kernel_bias()
        self.dbb_reparam = nn.Conv2d(in_channels=self.dbb_origin.conv.in_channels, out_channels=self.dbb_origin.conv.out_channels,
                                     kernel_size=self.dbb_origin.conv.kernel_size, stride=self.dbb_origin.conv.stride,
                                     padding=self.dbb_origin.conv.padding, dilation=self.dbb_origin.conv.dilation, groups=self.dbb_origin.conv.groups, bias=True)
        self.dbb_reparam.weight.data = kernel
        self.dbb_reparam.bias.data = bias

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1025583.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

自己实现一个简单的vhost-net

框架 vhost在网络中的位置如图&#xff1a; 要学习具体的框架可以看我之前的文章vhost-net--------深入了解Virtio-networking和vhost-net 接下来&#xff0c;我们自己实现一个vhost. vhost-net代码 在代码中写了详细注释&#xff0c;就直接上代码了 #include <stdio.h…

期权是什么?一分钟带你玩转期权策略!

很多人问我期权是什么&#xff0c;这个问题怎么回答呢&#xff1f;首先期权是一种交易模式&#xff0c;如同股票期货一样&#xff0c;但它又不同于股票和期货&#xff0c;因为它有自己的交易规则和特性&#xff0c;期权更多是一种工具&#xff0c;可以做空大盘对冲下跌风险&…

0018Java程序设计-springboot智慧环卫养管作业平台

文章目录 摘 要目 录系统设计开发环境 摘 要 本智慧环卫养管作业平台就是建立在充分利用现在完善科技技术这个理念基础之上&#xff0c;并使用IT技术进行对环卫养管作业的管理&#xff0c;从而保证环卫养管作业能够高效的进行&#xff0c;可以实现环卫养管作业的在线管理&…

健康云HIS系统源码,满足基层医疗机构业务需求,提供挂号支持、病患问诊、电子病历、开药发药、会员管理、统计查询、医生站和护士站等功能

云his系统源码 二级医院HIS系统全套源代码 自主研发&#xff0c;自主版权 一款满足基层医疗机构各类业务需要的健康云HIS系统。该系统能帮助基层医疗机构完成日常各类业务&#xff0c;提供病患挂号支持、病患问诊、电子病历、开药发药、会员管理、统计查询、医生站和护士站等一…

wabp.m 代码注释(便于算法快速理解)

算法效果: 波峰和起点检测效果: function [r,pk] = wabp(Araw, Offset,Scale, Fs) % r = wabp(Araw,Offset,Scale, Fs); % Input: Araw (125Hz sampled) waveform in wfdb-MIT format, % Offset, Scale % Araw = 血压波形 % Offset=偏移(信号减去或者加上偏移恢复成…

启山智软/O2O商城

文章目录 启山智软介绍一、O2O商业模式是什么二、启山智软O2O商城管理系统1.O2O商城系统2.多种商业形态的O2O商城系统1、类似蜜雪冰城的合作加盟模式2、类似优衣库的连锁直营模式3、类似京东到家的同城/本地服务平台 O2O商城开发具备的特色功能&#xff1a;部分源码分享 启山智…

MTBF、MTTR、MTTA 和 MTTF

了解一些最常见的事件指标 在当今永不停机的世界中&#xff0c;中断和技术事件比以往任何时候都更加重要。故障和停机期间会带来现实后果&#xff0c;错过截止时间、付款逾期、项目延迟。 这就是为什么公司必须量化和跟踪有关正常运行时间、停机期间以及团队解决问题的速度和…

改变latex单张页面宽度的正确做法

https://tex.stackexchange.com/questions/6834/change-paper-size-in-mid-document#comment115838_6838 首先注意&#xff0c;网上所有有关newgeometry的说明都是不可行的&#xff0c;因为 画红圈的地方大家自行阅读&#xff0c;这就是原因。 其次&#xff0c;更改页面宽度的…

一、Vuex相关概念和使用

目录 Vuex的概念核心概念Vuex 的使用场景1、组件之间的数据共享2、复杂状态的管理3、异步操作的处理 Vuex的概念 Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态&#xff0c;并以相应的规则保证状态以一种可预测的方式发生变化…

数据研发“新人”如何快速落地?

作者&#xff1a;肖迪(墨诩) 一、前言 这个季度主推安全月构筑&夯实稳定性底盘&#xff0c;就组织了组里的同学对核心业务链路进行了稳定性的摸排。在摸排过程中&#xff0c;不断有个声音在问你摸排出来的问题就是全部问题么&#xff1f;你加的监控加全了么&#xff1f;你…

【Git】一个完整的git项目之代码管理

1.版本库初始化 1.1 git clone 这是一种较为简单的初始化方式&#xff0c;当项目已经有了一个远程的Git版本库&#xff0c;只需在本地克隆一份代码。如&#xff1a; git clone http://192.168.x.x/github.com/someone/some_project.git some_project 上面的命令是将 htt…

SpringBoot自动装配原理及分析

一、什么是自动装配 在使用SpringBoot的时候&#xff0c;会自动将Bean装配到IoC容器中。例如我们在使用Redis数据库的时候&#xff0c;会引入依赖spring-boot-starter-data-redis。在引入这个依赖后&#xff0c;服务初始化的时候&#xff0c;会将操作Redis需要的组件注入到IoC…

给你下单前,磨练你无数次的国外客户

前段时间给几个客户做了不少方案设计和报价后都没有下文&#xff0c;给我做项目的设计师都对我没信心了&#xff0c;多少有点抱怨&#xff0c;就说我是雷声大雨点小。再有要做设计的图&#xff0c;就不会像之前那样热心了。 说真的&#xff0c;多少有点受挫。前天那个咨询了无…

log4j2 日志保存至数据库

文章目录 概述一、springmvc工程1.创建数据库日志表2.log4j2.xml引入JDBCAppender3.定义日志管理类4.编写日志输出代码5.运行结果6.完整代码 二、springboot工程1. 创建数据库日志表2.log4j2.xml引入JDBCAppender3.定义日志管理类4. 遗留问题5. 解决办法6. 完整代码 概述 Apac…

求臻医学:结直肠癌患者必看的就诊指南及基因检测意义

结直肠癌是常见的消化道肿瘤之一&#xff0c;已跃居我国高发恶性肿瘤第2位&#xff0c;且其发病率、死亡率逐年上升。数据显示&#xff0c;2020年新发病例 55.5 万&#xff0c;死亡病例 28.6 万。本文系统归纳总结了结直肠癌患者应该选择哪些诊科室、相关检查、治疗方式、预后预…

数字IC设计系列----单端口RAM、双端口RAM、同步FIFO、异步FIFO

一、单端口RAM原理及实现 1.1、概念/原理 在内存空间中开辟出一段固定大小的内存用于存储数据&#xff0c;每一个数据所占的bit位称之为位宽&#xff0c;这段内存空间中数据的总数称之为深度。例如reg [7:0] mem [255:0]&#xff0c;这段内存空间中每一个数据的位宽为8bit&am…

VS2019中使用printf函数报错处理方法

VS2019中使用printf函数报错处理方法 在使用vs2019学习OpenCV的过程中&#xff0c;使用简单的printf函数&#xff0c;竟然编译不过去&#xff0c;VS2019报错&#xff1b; 严重性 代码 说明 项目 文件 行 禁止显示状态 错误 C4996 ‘sprintf’: This function or variable may…

基础概念回顾:云原生应用交付

原文链接&#xff1a;基础概念回顾&#xff1a;云原生应用交付 转载来源&#xff1a;NGINX 开源社区 NGINX 唯一中文官方社区 &#xff0c;尽在 nginx.org.cn 尽管云原生应用开发诞生于 21 世纪初&#xff0c;但是在术语使用方面还是非常混乱。本文将带您了解常见的术语和问题。…

图像处理领域之►边缘检测大合集◄【应该是全网仅有的了吧】

图像处理领域之►边缘检测‧大合集◄ 概述 {\color{Brown}概述} 概述 数据集 {\color{Purple}数据集} 数据集 实践 {\color{Red}实践} 实践 深度学习方法 {\color{Blue} 深度学习方法} 深度学习方法 机器学习方法 {\color{Blue} 机器学习方法} 机器学习方法 基于传统方法 {\col…

如何隐藏或修改Docker容器中的Nginx响应头中的Server

背景介绍 现在大部分项目通过Nginx作为反向代理&#xff0c;实际由于安全审计要求需要隐藏或修改响应头的Server信息&#xff0c;传统的项目直接部署在nginx服务器中&#xff0c;只需要在nginx服务器安装ngx_http_headers_more_filter_module插件&#xff0c;然后通过修改ngin…