目标检测Backbone系列(3)HRNet —— 保持空间与语义信息的backbone

保持空间与语义信息的backbone

前言

一、如何保持图像的空间信息及提取语义信息

High-to-Low

Low-to-High

二、HRNet

网络开始层Stem

并行提取特征层 

语义空间信息交互的stage层

输出头Head 

总结

前言

虽然说这是一个图像分类的合集博客,但其实都是在介绍一些backbone网络。这次介绍的是HRNet,虽然说这个网络最初设计是做为关键点检测使用的,但其优秀保留空间信息的同时拥有高语义的特点,使其做为提取特征的backbone,也有很多优点。 


一、如何保持图像的空间信息及提取语义信息

同样在介绍HRNet前,先介绍一下,在这之前,其他网络是如何去保持图像的空间信息及提取其中的语义高维信息的。

我们可以把提取语义高维信息的过程称做High-to-Low,把恢复空间信息的过程称未Low-to-High

High指的是低分辨率特征图而高语义信息,Low指的是空间信息大而语义信息低的特征图

High-to-Low

 以Resnet为例,在每个stage的交接过程中,3x3,stride为2的卷积,对feature map进行的图像分辨率减半,特征维度翻倍的操作,其实就是High-to-Low的过程。

                                        

这种过程得到的特征图中,可以认为其中每个点,都蕴含了原图中对应感受野的信息,所有其中每个点,就拥有了高语义的特点。但以Resnet50来说,如果输入的是224x224的图片,最后返回的是一个7x7的特征图,那么相对于原图片的空间信息就被压缩了,这种空间信息的压缩,是对语义的聚合,相对于对图像的概括能力,是有利于分类任务的进行的。

但对于像图像分割,目标检测的需要空间位置信息的任务来说,在7x7的特征图上,去预测224x224上的目标位置就会变得困难了。

针对这个问题,其实最简单的方法就是只要不在每个stage之间加入步幅为2的卷积来降分辨率就好了。但如果这么操作,确实是保留了空间信息,但会导致特征图的感受野不足,虽然通道维度上去了,其实特征图上的每个点的语义信息是不够的,特征提取能力有限。

Low-to-High

Low-to-High其实就是去还原空间信息的过程,而如何在还原空间的信息的同时,还把高维度的语义信息带回去呢。多尺度融合其实就是其中一个比较被认可的思路,下面就以FPN结构来讲解一下。

          

FPN结构,现在也比较广泛的用在主流的目标检测网络的backbone中,因为前面说过,224x224的图片,最后得到7x7的特征图,空间信息是不足以来预测位置信息的。但低维度的图的语义信息又不足。

那最直观的想法就是,将高维度的特征图上采样后和低维度的图,相加或者维度拼接(个人认为相加和维度拼接的效果差不多),那么即获得了高维度语义的信息,又融合了低维度的空间信息。

对于目标检测任务来说,使用FPN的多尺度融合特点,并在不同尺度上分别去预测不同尺寸的物体,去解决高语义信息空间信息不足,而富含空间信息的低特征图语义信息不足的问题。

二、HRNet

经过上面的介绍,应该对空间信息和语义信息有了大致的概念了。而HRNet的设计思想就是如何尽可能的去利用高语义特征图的语义信息,和空间信息丰富的低语义信息。其实从网络的结构图就可以看出,HRNet的设计方法,就是融合特征图来既提取高维特征又富有空间信息。 

      

整体HRNet的结构,可以分为 网络开始的Stem层,并行提取特征层,负责语义空间信息交互的stage层,还有最后的输出头Head,下面就分解介绍一下。

网络开始层Stem

每个网络都有从原始图片到进入结构的初始阶段,像resnet中7x7的卷积+3x3的pooling层。在HRNet中,stem相对于一个特征提取的基底作用,在开始就采用了和resnet中bottleneck一样的残差结构来提取原始特征图的特征。这里说原始特征图的原因是,后面的特征图都是基于这个特征图生成的,所以作者采用bottleneck的结构来保证原始特征图的质量是没有问题的

        

上面这个图,注意一点是,conv中如果卷积核大小为3x3,则会设置其padding为1,保证特征图的大小不变。 

所以经过基础的特征提取,就得到了最原始的256x56x56的特征图了。

并行提取特征层 

在介绍如何融合不同分辨率的信息前,来看看每个分支是如何进行后续特征提取的。 

在进行不同层次特征图分裂及融合前,每个层次都需要经过一系列的block结构,来增强每层特征图提取特征的能力,具体block结构,其实就是resnet中的BasicBlock。这里以第一个分支之后只有两个分支特征图举例。

        

后续的每个分支都类似上图的结构,再经过分支的basicBlock增强提取特征能力后,这些特征层就进入了HRNet中精华的FuseLayer层进行特征融合。

语义空间信息交互的stage层

在论文中作者其实对于如何融合各层之间的信息,给了比较详细的解释。

    

下面我说的高层指的是分辨率低语义信息高的特征图,低层则相反。

对于高层特征图向低层进行融合,则是将高层特征图上采样到和低层图一样的尺寸,然后进行相加拼接。对于低层特征向高层特征进行融合,则是通过经过一个3x3,stride为2的卷积进行下采样,达到和高层同样维度进行拼接。

HRNet就是利用这么一个Fuse结构,使其不管是高层还是低层的特征图,都拥有较高的语义信息和空间信息。 

而HRNet中生成新的层级特征图过程和特征图的融合是比较类似的,就相对于是多增加了一个层:即把低层的信息都通过3x3,stride为2的卷积进行下采样,然后拼接出来一个新的更高层级的特征图。

                                          

下面给出这一个结构的代码,其实就是一个for循环,然后就按照我上面说的,低层信息下采样,通过3x3的卷积向高层信息融合,高层信息则通过上采样到和底层一样的尺寸,进行融合。(里面有些函数没给出,但只要知道其中逻辑就好了。

class FuseLayers(TheseusLayer):
    def __init__(self, in_channels, out_channels):
        super().__init__()

        self._actual_ch = len(in_channels)
        self._in_channels = in_channels

        self.residual_func_list = nn.ModuleList()
        self.relu = nn.ReLU()
        for i in range(len(in_channels)):
            for j in range(len(in_channels)):
                if j > i:
                    self.residual_func_list.append(
                        ConvBNLayer(
                            num_channels=in_channels[j],
                            num_filters=out_channels[i],
                            filter_size=1,
                            stride=1,
                            act=None))
                elif j < i:
                    pre_num_filters = in_channels[j]
                    for k in range(i - j):
                        if k == i - j - 1:
                            self.residual_func_list.append(
                                ConvBNLayer(
                                    num_channels=pre_num_filters,
                                    num_filters=out_channels[i],
                                    filter_size=3,
                                    stride=2,
                                    act=None))
                            pre_num_filters = out_channels[i]
                        else:
                            self.residual_func_list.append(
                                ConvBNLayer(
                                    num_channels=pre_num_filters,
                                    num_filters=out_channels[j],
                                    filter_size=3,
                                    stride=2,
                                    act="relu"))
                            pre_num_filters = out_channels[j]

    def forward(self, x, res_dict=None):
        out = []
        residual_func_idx = 0
        for i in range(len(self._in_channels)):
            residual = x[i]
            for j in range(len(self._in_channels)):
                if j > i:
                    xj = self.residual_func_list[residual_func_idx](x[j])
                    residual_func_idx += 1

                    xj = upsample(xj, scale_factor=2**(j - i), mode="nearest")
                    residual = paddle.add(x=residual, y=xj)
                elif j < i:
                    xj = x[j]
                    for k in range(i - j):
                        xj = self.residual_func_list[residual_func_idx](xj)
                        residual_func_idx += 1

                    residual = paddle.add(x=residual, y=xj)

            residual = self.relu(residual)
            out.append(residual)

        return out

输出头Head 

HRNet相比于其他Backbone网络的特点在于,其结构在进入输出头前,有多个层级的特征图可以进行输出,且每个层级的特征图经过上述的操作,都相对于经过了FPN结构的多特征融合,所以每个层级都富有语义信息和空间信息。所以其最后的输出头结构跟带有FPN的backbone是一样的。

比如说,针对目标分类任务,可以把每一层的特征图都输出去,去预测空间大小不一样的目标,如图(a)中一样。

针对图像分割任务,可以对高层特征上才采样后,和最低层信息融合,来还原原始图像,做分割任务。如图(b)

针对分类任务,则可以在(b)的基础上再对得到的特征图,进行一系列卷积,汇聚语义信息,来进行分类任务。如图(c) 


总结

HRNet在任务比较需要空间信息的任务中,比如图像分割和目标检测,做为backbone,是有不错的结果的。

  • 8
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lzzzzzzm

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值