【论文笔记】Contextual Transformer Networks for Visual Recognition

论文

论文题目:Contextual Transformer Networks for Visual Recognition

收录:CVPR2021

论文地址:[2107.12292] Contextual Transformer Networks for Visual Recognition (arxiv.org)

项目地址:GitHub - JDAI-CV/CoTNet: This is an official implementation for "Contextual Transformer Networks for Visual Recognition".

参考博客:ResNet超强变体:京东AI新开源的计算机视觉模块!(附源代码) (qq.com)

CoTNet学习笔记 - 知乎 (zhihu.com)

CoTNet | 性能超越BoTNet、Swin!Transformer+CNN=奠定CV模型新格局(附代码解读) - 知乎 (zhihu.com)

CNN终于杀回来了!京东AI开源最强ResNet变体CoTNet:即插即用的视觉识别模块 - 腾讯云开发者社区-腾讯云 (tencent.com)

导读:本文是京东AI研究院梅涛团队在自注意力机制方面的探索,不同于现有注意力机制仅采用局部或者全局方式进行上下文信息获取,他们创造性的将Transformer中的自注意力机制的动态上下文信息聚合与卷积的静态上下文信息聚合进行了集成,提出了一种新颖的Transformer风格的“即插即用”CoT模块,它可以直接替换现有ResNet架构Bottleneck中的3×3卷积并取得显著的性能提升。无论是ImageNet分类,还是COCO检测与分割,所提CoTNet架构均取得了显著性能提升且参数量与FLOPs保持同水平。比如,相比EfficientNet-B6的84.3%,所提SE-CoTNetD-152取得了84.6%同时具有快2.75倍的推理速度。

前言

具有自注意力的Transformer引发了自然语言处理领域的革命,最近还激发了Transformer式架构设计的出现,并在众多计算机视觉任务中取得了具有竞争力的结果。尽管如此,大多数现有设计直接在2D特征图上使用自注意力来获得基于每个空间位置的独立查询和键对的注意力矩阵,但未充分利用相邻键之间的丰富上下文。在今天分享的工作中,研究者设计了一个新颖的Transformer风格的模块,即Contextual Transformer (CoT)块,用于视觉识别。这种设计充分利用输入键之间的上下文信息来指导动态注意力矩阵的学习,从而增强视觉表示能力。从技术上讲,CoT块首先通过3×3卷积对输入键进行上下文编码,从而产生输入的静态上下文表示。

研究者进一步将编码的键与输入查询连接起来,通过两个连续的1×1卷积来学习动态多头注意力矩阵。学习到的注意力矩阵乘以输入值以实现输入的动态上下文表示。静态和动态上下文表示的融合最终作为输出。CoT块很吸引人,因为它可以轻松替换ResNet架构中的每个3 × 3卷积,产生一个名为Contextual Transformer Networks (CoTNet)的Transformer式主干。通过对广泛应用(例如图像识别、对象检测和实例分割)的大量实验,验证了CoTNet作为更强大的主干的优越性。

背景

Attention注意力机制与self-attention自注意力机制

在CV领域使用自注意力机制主要是有以下几点原因:

  • 在CNN中,卷积层通过卷积核获得输出特征,但是卷积核的感受野很小
  • 采用堆叠卷积层的方式来增加感受野的方式并不高效
  • 计算机视觉中的很多任务都是由于语义信息不足从而影响的性能
  • 自注意力机制可以捕获全局信息获得更大的感受野
  • 为什么要注意力机制?

在Attention诞生之前,已经有CNN和RNN及其变体模型了,那为什么还要引入attention机制?主要有两个方面的原因,如下:

(1)计算能力的限制:当要记住很多“信息“,模型就要变得更复杂,然而目前计算能力依然是限制神经网络发展的瓶颈。

(2)优化算法的限制:LSTM只能在一定程度上缓解RNN中的长距离依赖问题,且信息“记忆”能力并不高。

  • 什么是注意力机制

在介绍什么是注意力机制之前,先让大家看一张图片。当大家看到下面图片,会首先看到什么内容?当过载信息映入眼帘时,我们的大脑会把注意力放在主要的信息上,这就是大脑的注意力机制。

同样,当我们读一句话时,大脑也会首先记住重要的词汇,这样就可以把注意力机制应用到自然语言处理任务中,于是人们就通过借助人脑处理信息过载的方式,提出了Attention机制。

self attention是注意力机制中的一种,也是transformer中的重要组成部分。自注意力机制是注意力机制的变体,其减少了对外部信息的依赖,更擅长捕捉数据或特征的内部相关性。自注意力机制在文本中的应用,主要是通过计算单词间的互相影响,来解决长距离依赖问题。

框架

1、Multi-head Self-attention in Vision Backbones

在这里,研究者提出了视觉主干中可扩展的局部多头自注意力的一般公式,如上图(a)所示。

 分别通过嵌入矩阵 (Wq, Wk, Wv)。 值得注意的是,每个嵌入矩阵在空间中实现为1×1卷积。

局部关系矩阵R进一步丰富了每个k × k网格的位置信息:

接下来,注意力矩阵A是通过对每个头部的通道维度上的Softmax操作对增强的空间感知局部关系矩阵Rˆ进行归一化来实现的:A = Softmax(Rˆ)。将A的每个空间位置的特征向量重塑为Ch局部注意力后矩阵(大小:k × k),最终输出特征图计算为每个k × k网格内所有值与学习到的局部注意力矩阵的聚合:

值得注意的是,每个头部的局部注意力矩阵是由沿通道维度均匀划分的 V 特征图计算的,最终输出 Y 是所有头部聚合特征图的串联。 

2、Contextual Transformer Block

传统的自注意力很好地触发了不同空间位置的特征交互,具体取决于输入本身。然而,在传统的自注意力机制中,所有成对的查询键关系都是通过孤立的查询键对独立学习的,而无需探索其间的丰富上下文。这严重限制了自注意力学习在2D特征图上进行视觉表示学习的能力。

为了缓解这个问题,研究者构建了一个新的Transformer风格的构建块,即上图 (b)中的 Contextual Transformer (CoT) 块,它将上下文信息挖掘和自注意力学习集成到一个统一的架构中。

3、Contextual Transformer Networks

 ResNet-50 (left) and CoTNet50 (right)

ResNeXt-50 with a 32×4d template (left) and CoTNeXt-50 with a 2×48d template (right).

上表分别详细地对比了ResNet-50、CoTNet-50和ResNeXt-50、CoTNeXt-50的网络结构:

  • CoTNet-50是通过将 ResNet-50 中的所有 3×3 卷积替换为 CoT 模块来构建的。由于 CoT 模块与典型的卷积相似,因此 CoTNet-50 具有与 ResNet-50 相似(甚至略小)的参数数量和 FLOP。
  • CoTNeXt-50的构建也是类似的。与典型的卷积相比,当组的数量(即上表中的 C)增加时,组卷积内的内核深度显着降低。因此,组卷积的计算成本因此降低了 C 倍。为了保持与 ResNeXt-50 相似的参数量和 FLOP,我们额外减少了输入的规模的特征图。

讨论本文的一些相关工作。

  • Blueprint Separable Convolution [18] 通过 1×1 逐点卷积加上 k×k 深度卷积来近似传统卷积,有效地减少了网络冗余。共性:Transformer-style 块也利用 1×1 逐点卷积将输入特征转换为 value,并且以类似的方式进行后续的聚合计算。此外,对于每个头部,transformer-style block 中的聚合计算采用通道共享策略以提高网络效率,且不会出现显着的精度下降。
  • Dynamic Region-Aware Convolution [6] 引入了一个滤波器生成器模块(由两个连续的 1×1 组成)用于学习不同空间位置的区域特征。这与 CoT 块中的注意力矩阵生成器类似,但我们的注意力矩阵生成器能更充分地利用上下文键和查询之间的复杂特征交互用于学习自注意力。
  • Bottleneck Transformer [44] 通过用 Transformer 式模块替换 3×3 卷积来增强具有自注意力机制的 ConvNet。具体来说,它采用全局多头自注意力层,计算上比 CoT 的局部自注意力更昂贵。此外,我们的 CoT 块通过利用输入键之间的丰富上下文来加强自注意力学习,超越了典型的局部自注意力。

 代码解读

class CotLayer(nn.Module):
    def __init__(self, dim, kernel_size):
        super(CotLayer, self).__init__()

        self.dim = dim
        self.kernel_size = kernel_size

        self.key_embed = nn.Sequential(
            nn.Conv2d(dim, dim, self.kernel_size, stride=1, padding=self.kernel_size//2, groups=4, bias=False),
            nn.BatchNorm2d(dim),
            nn.ReLU(inplace=True)
        )

        share_planes = 8
        factor = 2
        self.embed = nn.Sequential(
            nn.Conv2d(2*dim, dim//factor, 1, bias=False),
            nn.BatchNorm2d(dim//factor),
            nn.ReLU(inplace=True),
            nn.Conv2d(dim//factor, pow(kernel_size, 2) * dim // share_planes, kernel_size=1),
            nn.GroupNorm(num_groups=dim // share_planes, num_channels=pow(kernel_size, 2) * dim // share_planes)
        )

        self.conv1x1 = nn.Sequential(
            nn.Conv2d(dim, dim, kernel_size=1, stride=1, padding=0, dilation=1, bias=False),
            nn.BatchNorm2d(dim)
        )

        self.local_conv = LocalConvolution(dim, dim, kernel_size=self.kernel_size, stride=1, padding=(self.kernel_size - 1) // 2, dilation=1)
        self.bn = nn.BatchNorm2d(dim)
        act = get_act_layer('swish')
        self.act = act(inplace=True)

        reduction_factor = 4
        self.radix = 2
        attn_chs = max(dim * self.radix // reduction_factor, 32)
        self.se = nn.Sequential(
            nn.Conv2d(dim, attn_chs, 1),
            nn.BatchNorm2d(attn_chs),
            nn.ReLU(inplace=True),
            nn.Conv2d(attn_chs, self.radix*dim, 1)
        )

    def forward(self, x):
        # 获取static Contexts:CBL三元组 (3×3)
        k = self.key_embed(x)
        # 将查询特征 q 和键 k 拼接起来(Concat)
        qk = torch.cat([x, k], dim=1)
        # 获取qk矩阵的 B, C, H, W
        b, c, qk_hh, qk_ww = qk.size()

        # 两个1×1卷积层:CBL + CG
        # 1. 第二个卷积层没有ReLU,可以避免屏蔽过多的有效信息
        # 2. 第二个卷积层使用GroupNorm,将channel分为G组,每组分别做归一化,避免不同batch特征之间造成干扰
        w = self.embed(qk)
        # 展开为多个头部
        w = w.view(b, 1, -1, self.kernel_size*self.kernel_size, qk_hh, qk_ww)

        # 获取 value
        # Conv 1×1 + BN
        x = self.conv1x1(x)
        # 获取dynamic contexts:结合权重 w 和 值 v 作矩阵乘法,对每个空间像素进行加权
        # Conv 3×3 + BN + swish
        x = self.local_conv(x,w)
        x = self.bn(x)
        x = self.act(x)

        # 增强维度用于特征融合
        B, C, H, W = x.shape
        x = x.view(B, C, 1, H, W)
        k = k.view(B, C, 1, H, W)
        #将 dynamic contexts 和 static contexts 拼接起来(Concat)
        x = torch.cat([x, k], dim=2)

        # 融合 dynamic contexts 和 static contexts,并基于此再进行注意力加权
        x_gap = x.sum(dim=2)
        # 对特征图的空间维度求均值
        x_gap = x_gap.mean((2, 3), keepdim=True)
        # CBLC (1×1)特征编码
        x_attn = self.se(x_gap)
        # 特征维度变换
        x_attn = x_attn.view(B, C, self.radix)
        # 计算特征重要性
        x_attn = F.softmax(x_attn, dim=2)
        # 将注意力图重新加权回 x
        out = (x * x_attn.reshape((B, C, self.radix, 1, 1))).sum(dim=2)
        # 使tensor在内存中连续存放
        return out.contiguous()

实验结果

探索上下文信息的不同方式的性能比较,即仅使用静态上下文(Static Context),只使用动态上下文(Dynamic Context),线性融合静态和动态上下文(Linear Fusion),以及完整版的CoT块。 主干是CoTNet-50和采用默认设置在ImageNet上进行训练。

 上表对比了不同上下文信息获取的性能对比,从中可以看到:

  • 静态上下文方式(可视作无自注意力的ConvNet)仅取得77.1%的top1精度;
  • 动态上下文方式可以取得78.5%的top1精度;
  • 动静上下文简单的相加融合可以取得78.7%的top1精度;
  • 通过注意力融合的方式集成动静上下文可以进一步将其性能提升到79.2%。

 

上表展示了在 ResNet-50 中使用不同的替换设置对四个阶段(res2→res3→res4→res5)的影响:

  • 用 CoT 模块替换 3*3 卷积可以提高性能,同时参数量和 FLOP 略有减少;
  • 最后两个阶段(res4 和 res5)中 CoT 模块的替换对性能提升的贡献最大。在第一阶段(res1 和 res2)中替换 CoT 模块只能带来边际的性能提升(总共 0.2% 的 top-1 准确率),同时需要 1.34 倍的推理时间.

在ImageNet数据集上的Inference Time vs. Accuracy Curve

上表总结了在COCO数据集上使用Faster-RCNN和Cascade-RCNN在不同的预训练主干中进行目标检测的性能比较。 将具有相同网络深度(50层/101层)的视觉主干分组。 从观察,预训练的CoTNet模型(CoTNet50/101和CoTNeXt-50/101)表现出明显的性能,对ConvNets 主干(ResNet-50/101和ResNeSt-50/101) 适用于所有IoU的每个网络深度阈值和目标大小。 结果基本证明了集成self-attention学习的优势使用CoTNet中的上下文信息挖掘,即使转移到目标检测的下游任务。

总结

  1. 大多数现有的基于 Transformer的架构设计是直接作用在 2D 特征图上的,通过使用自注意力来获得注意力矩阵(独立的查询点(queries) 和所有键(keys)),这并未充分利用相邻键之间的丰富上下文。因此,本文提出了一种新的 Transformer 式架构,称为 Contextual Transformer (CoT) 模块,它能够利用输入键之间的上下文信息来指导自注意力学习
  2. CoT 模块首先捕获相邻键之间的静态上下文,进一步利用它来触发挖掘动态上下文的自我注意。这种方式优雅地将上下文挖掘和自注意力学习统一到单一架构中,从而增强了视觉表示的能力。接着,CoT 块可以很轻松地替换现有 ResNet 架构中的常规卷积,同时还能降低网络参数。最后,在COCO数据集上进行目标检测和实例分割实验也证明了 CoTNet 具有强大的泛化能力。

亮点:

  1. 技术上来讲,CoT模块首先通过卷积对输入keys进行上下文信息编码得到关于输入的静态上下文表达;进一步将编码keys与输入query进行拼接并通过两个连续卷积学习动态多头注意力矩阵;所得注意力矩阵与输入values相乘即可得到关于输入的动态上下文表达。
  2. CoTNet-50直接采用CoT替换Bottlenck中的卷积;类似的,CoTNeXt-50采用CoT模块替换对应的组卷积,为获得相似计算量,对通道数、分组数进行了调整:CoTNeXt-50的参数量是ResNeXt-50的1.2倍,FLOPs则是1.01倍。

猜你喜欢

转载自blog.csdn.net/m0_61899108/article/details/125687262#comments_26387399