前言
整理下res2net特征提取网络
论文地址:https://arxiv.org/abs/1904.01169
文中所提供的代码来自:[https://github.com/open-mmlab/mmclassification]
1.res2net
主要是在每个残差块内部构建特征金字塔结构,在特征层内部进行多尺度的卷积,形成不同感受野,获得不同细粒度的特征。
结构图
结构图与resnet类似,只不过为了更好的分成4组,每个残差块里的3x3卷积里的通道数变了。对应的resnet相关细节可以移步到https://blog.csdn.net/weixin_41311686/article/details/125934861
2.代码解析
!!!这里主要是根据代码解析的,与原论文的开源代码或许有些不同,但基本差不多。
整个前向传播是和resnet的一样
def forward(self, x):
"""Forward function."""
if self.deep_stem:
x = self.stem(x)
else:
x = self.conv1(x)
x = self.norm1(x)
x = self.relu(x)
x = self.maxpool(x)
outs = []
for i, layer_name in enumerate(self.res_layers):
res_layer = getattr(self, layer_name)
x = res_layer(x)
if i in self.out_indices:
outs.append(x)
return tuple(outs)
残差结构与resnet的比较
通过将残差块的3x3卷积替换成类似特征金字塔的结构来更好的利用特征层内的不同尺度的特征。
整个堆叠残差结构的前向传播:
def forward(self, x):
"""Forward function."""
def _inner_forward(x):
identity = x
out = self.conv1(x) # 64-104,和resnet不同的地方
out = self.norm1(out)
out = self.relu(out)
if self.with_plugins:
out = self.forward_plugin(out, self.after_conv1_plugin_names)
spx = torch.split(out, self.width, 1) # [n,104] ,104//26 = 4
sp = self.convs[0](spx[0].contiguous()) #第一个组经过一个3X3卷积
sp = self.relu(self.bns[0](sp)) #
out = sp
for i in range(1, self.scales - 1): #循环
if self.stage_type == 'stage':
sp = spx[i]
else:
sp = sp + spx[i]
sp = self.convs[i](sp.contiguous()) # 第二个组经过一个3X3卷积
sp = self.relu(self.bns[i](sp))
out = torch.cat((out, sp), 1) # 在维度1上进行cat
if self.stage_type == 'normal' or self.conv2_stride == 1:
out = torch.cat((out, spx[self.scales - 1]), 1) # 最后一个不进行卷积,全部拼接回原来的维度
elif self.stage_type == 'stage':
out = torch.cat((out, self.pool(spx[self.scales - 1])), 1)
if self.with_plugins:
out = self.forward_plugin(out, self.after_conv2_plugin_names)
out = self.conv3(out) # 1x1卷积
out = self.norm3(out)
if self.with_plugins:
out = self.forward_plugin(out, self.after_conv3_plugin_names)
if self.downsample is not None:
identity = self.downsample(x)
out += identity
return out
if self.with_cp and x.requires_grad:
out = cp.checkpoint(_inner_forward, x)
else:
out = _inner_forward(x)
out = self.relu(out)
return out
一些结构的解析
conv1:是将输入通道由64变成104,主要是为了将通道分成4组(104//26),和resnet不同,resnet是不变。
self.conv1 = build_conv_layer(
self.conv_cfg,
self.inplanes, # 64
width * scales, # 输出通道数:26*4=104
kernel_size=1,
stride=self.conv1_stride,
bias=False)
conv1里的width是通过(输入通道x(26/64))得到的。开始输入是64,所以64x(26/64)=26。对应1x1卷积里的输出通道就是26x4=104.mmclassification是这样操作的。
width = int(math.floor(self.planes * (base_width / base_channels)))
self.convs:是三个3x3卷积,步长为1,padding=1.对应的是resnet里中间的一个3x3卷积
conv3:是一个1x1卷积,将通道由104变到256,对应resnet的64变成256.
残差连接:self.downsample与resnet不同,是一个Sequential,里面包含了一个Avgpool2d+1x1卷积+BN.(与shufflenet里的有点类似)
downsample = nn.Sequential(
nn.AvgPool2d(
kernel_size=stride,
stride=stride,
ceil_mode=True,
count_include_pad=False),
build_conv_layer(
conv_cfg,
inplanes,
planes * block.expansion,
kernel_size=1,
stride=1,
bias=False),
build_norm_layer(norm_cfg, planes * block.expansion)[1],
)
res2net和resnet相比精度会更高,但消耗的时间也会更多,所以在3x3结构上,又做了变化,进行了分组卷积。
文章来源:https://www.toymoban.com/news/detail-457207.html
总结
简单记录下,后面若有新的内容发现,会继续补充。文章来源地址https://www.toymoban.com/news/detail-457207.html
到了这里,关于特征提取网络之res2net的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!