backbone

更新时间:2022-12-28 13:35:02 阅读: 评论:0


2022年12月28日发(作者:荷兰vs墨西哥)

NanoDet代码逐⾏精读与修改(⼀)Backbone

--neozng1@

笔者已经为nanodet增加了⾮常详细的注释,代码请戳此仓库:。

此仓库会跟着⽂章推送的节奏持续更新!

⽬录

ne

作为⼀个着眼于边缘平台部署,尤其是针对CPU型设备的⽹络,NanoDet之前⾃然选择的是使⽤深度可分离卷积的轻量⾻⼲⽹络。

这⾥我们主要介绍默认的Backbone:,这是⼀个由华为提出的轻量⾻⼲⽹络,关于GhostNet的详解请戳:。此模块提供了预训练权重下载,并

将结构封装成了⼀个类。

这个⽂件被放在仓库中的nanodet/model/backbone下。

1.0._make_divisible()

#_make_divisible()是⼀个⽤于取整的函数,确保ghostmodule的输⼊输出都可以被组卷积数整除

#这是因为2d中要求groups参数必须能被输⼊输出整除,具体请参考深度可分离卷积相关的资料

def_make_divisible(v,divisor,min_value=None):

"""

Thisfunctionistakenfromtheoriginaltfrepo.

Itensuresthatalllayershaveachannelnumberthatisdivisibleby8

Itcanbeenhere:

/tensorflow/models/blob/master/rearch/slim/nets/mobilenet/

"""

ifmin_valueisNone:

min_value=divisor

new_v=max(min_value,int(v+divisor/2)//divisor*divisor)

#Makesurethatrounddowndoesnotgodownbymorethan10%.

ifnew_v<0.9*v:

new_v+=divisor

returnnew_v

eExcite

classSqueezeExcite():

def__init__(

lf,

in_chs,

_ratio=0.25,

reduced_ba_chs=None,

activation="ReLU",

gate_fn=hard_sigmoid,

divisor=4,

**_

):

super(SqueezeExcite,lf).__init__()

_fn=gate_fn

reduced_chs=_make_divisible((reduced_ba_chsorin_chs)*_ratio,divisor)

#channel-wi的全局平均池化

_pool=veAvgPool2d(1)

#1x1卷积,得到⼀个维度更⼩的⼀维向量

_reduce=2d(in_chs,reduced_chs,1,bias=True)

#送⼊激活层

1=act_layers(activation)

#再加上⼀个1x1conv,使得输出长度还原回通道数

_expand=2d(reduced_chs,in_chs,1,bias=True)

defforward(lf,x):

x_=_pool(x)

x_=_reduce(x_)

x_=1(x_)

x_=_expand(x_)

#⽤刚得到的权重乘以原输⼊

x=x*_fn(x_)

returnx

这个模块来⾃,介绍请戳笔者之前介绍visionattention的博客:。利⽤额外的全局池化+FC+channel-wimultiply构建SE分⽀,这能够⽤

来捕捉通道之间的相关性,给予重要的通道更⼤的权重。

Act

classConvBnAct():

def__init__(lf,in_chs,out_chs,kernel_size,stride=1,activation="ReLU"):

super(ConvBnAct,lf).__init__()

=2d(

in_chs,out_chs,kernel_size,stride,kernel_size//2,bias=Fal

)

1=orm2d(out_chs)

1=act_layers(activation)

defforward(lf,x):

x=(x)

x=1(x)

x=1(x)

returnx

这其实就是卷积、批归⼀化和激活函数的叠加,这三个结构⼏乎是现在深度⽹络的构成单元的标准配置了,写成⼀个模块⽅便后⾯多次调

⽤。

odule

classGhostModule():

def__init__(

lf,inp,oup,kernel_size=1,ratio=2,dw_size=3,stride=1,activation="ReLU"

):

super(GhostModule,lf).__init__()

=oup

#确定特征层减少的⽐例,init_channels是标准卷积操作得到

init_channels=(oup/ratio)

#new_channels是利⽤廉价操作得到的

new_channels=init_channels*(ratio-1)

#标准的convBNactivation层,注意conv是point-wiconv的1x1卷积

y_conv=tial(

2d(

inp,init_channels,kernel_size,stride,kernel_size//2,bias=Fal

),

orm2d(init_channels),

act_layers(activation)tial(),

)

#ghostNet的核⼼,⽤廉价的线性操作来⽣成相似特征图

#关键在于groups数为init_channels,则说明每个init_channel都对应⼀层conv

#输出的通道数是输⼊的ratio-1倍,输⼊的每⼀个channel会有ratio-1组参数

_operation=tial(

2d(

init_channels,

new_channels,

dw_size,

1,

dw_size//2,

groups=init_channels,

bias=Fal,

),

#BN和AC操作

orm2d(new_channels),

act_layers(activation)tial(),

)

defforward(lf,x):

x1=y_conv(x)

x2=_operation(x1)

#new_channel和init_channel是并列的关系,拼接在⼀起形成新的输出

out=([x1,x2],dim=1)

returnout

这个模块就是GhostNet的关键了,在了解GhostNet所谓的”廉价操作“即cheap_operation之前,你需要知道组卷积(groupconv)和深

度可分离卷积(depth-wiparableconv)的概念。⾸先对上⼀个特征层的输⼊进⾏标准卷积,⽣成init_channels的特征;随后将此特征进

⾏分组卷积,并将groups数取得和输⼊的channel数相同(每⼀个channel都对应⼀个单独的卷积核),这样就可以尽可能的降低参数量和运

算量,开销⾮常⼩.

ottleneck

GhostBottleneck就是GhostNet的基本架构了,GhostNet就由数个GhostBottleneck堆叠⽽成,对于Stride=2的bottleneck在两个Ghost

module之间增加了⼀个深度可分离卷积作为连接。

ghostbottlenect的结构,分为stage内的stride=1和stage间的stride=2

classGhostBottleneck():

"""Ghostbottleneckw/optionalSE"""

def__init__(

lf,

in_chs,

mid_chs,

out_chs,

dw_kernel_size=3,

stride=1,

activation="ReLU",

_ratio=0.0,

):

super(GhostBottleneck,lf).__init__()

#可以选择是否加⼊SEmodule

has_=_ratioisnotNoneand_ratio>0.0

=stride

#Point-wiexpansion

#第⼀个ghost将会有较⼤的mid_chs即输出通道数

1=GhostModule(in_chs,mid_chs,activation=activation)

#Depth-wiconvolution

#对于stride=2的版本(或者你⾃⼰选择添加更⼤的Stride),两个GhostModule中间增加DW卷积

>1:

_dw=2d(

mid_chs,

mid_chs,

dw_kernel_size,

stride=stride,

padding=(dw_kernel_size-1)//2,

groups=mid_chs,

bias=Fal,

)

_dw=orm2d(mid_chs)

#Squeeze-and-excitation

ifhas_:

=SqueezeExcite(mid_chs,_ratio=_ratio)

el:

=None

=None

#Point-wilinearprojection

#最后的输出不添加激活函数层,并且会使⽤⼀个较⼩的out_chs以匹配shortcut连接的通道数

2=GhostModule(mid_chs,out_chs,activation=None)

#shortcut

#最后的跳连接,如果in_chs等于out_chs则直接执⾏element-wiadd

ifin_chs==out_==1:

ut=tial()

#如果不相等,则使⽤深度可分离卷积使得featuremap的⼤⼩对齐

el:

ut=tial(

2d(

in_chs,

in_chs,

dw_kernel_size,

stride=stride,

padding=(dw_kernel_size-1)//2,

groups=in_chs,

bias=Fal,

),

orm2d(in_chs),

2d(in_chs,out_chs,1,stride=1,padding=0,bias=Fal),

orm2d(out_chs),

)

defforward(lf,x):

#保留identityfeature,稍后进⾏连接

residual=x

#1stghostbottleneck

x=1(x)

#如果stride>1则加⼊Depth-wiconvolution

>1:

x=_dw(x)

x=_dw(x)

#Squeeze-and-excitation

tNone:

x=(x)

#2ndghostbottleneck

x=2(x)

x+=ut(residual)

returnx

Ghostmodule中⽤于⽣成复杂特征的卷积是1x1的point-wiconv,对于Stride=2的bottleneck来说⼜有⼀个stride=2的DW,那么就

可以将前者就和后者看作是构成了⼀组深度可分离卷积,只不过Ghostmodule⽣成ghostfeature的操作⼤⼤降低了参数量和运算量。若

启⽤了has_的选项,则会在两个ghostmodule之间加⼊⼀个SE分⽀。

et

讲解完了基本的模块之后,我们就可以利⽤上述的GhostBottleneck来构建GhostNet了:

GhostNet原⽂中整个backbone的结构,#exp是bottleneck中通道扩展的倍数,#out是当前层的输出通道数

#exp代表了在经过bottleneck中的第⼀个Ghostmodule后通道扩展的倍数,通道数随后会在同⼀个bottleneck中的第⼆个ghost

module被减少到和该bottleneck中最开始的输⼊相同,以便进⾏res连接。#out是输出的通道数。可以发现,Stride=2的bottleneck被

⽤在两个不同的stage之间以改变featuremap的⼤⼩。

为了⽤作检测⽹络,删除最后⽤于分类的FC,并从stage4、6、9分别取出stage的输出作为FPN的输⼊。若需要追求速度,可以考虑进⼀

步减少每个stage的层数或是直接砍掉⼏个stage也⽆妨。

本文发布于:2022-12-28 13:35:02,感谢您对本站的认可!

本文链接:http://www.wtabcd.cn/fanwen/fan/90/46937.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

下一篇:carbonate
标签:backbone
相关文章
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图