【MIT-BEVFusion代码解读】第三篇:camera的encoder部分


BEVFusion相关的其它文章链接:

  1. 【论文阅读】ICRA 2023|BEVFusion:Multi-Task Multi-Sensor Fusion with Unified Bird‘s-Eye View Representation
  2. MIT-BEVFusion训练环境安装以及问题解决记录
  3. 【MIT-BEVFusion代码解读】第一篇:整体结构与config参数说明
  4. 【MIT-BEVFusion代码解读】第二篇:LiDAR的encoder部分
  5. 【MIT-BEVFusion代码解读】第三篇:camera的encoder部分
  6. 【MIT-BEVFusion代码解读】第四篇:融合特征fuser和解码特征decoder

cameraencoder主要有3部分,分别是backboneneckvtransform部分。其中backbone使用SwinTransformerneck使用GeneralizedLSSFPNvtransform部分使用的是DepthLSSTransform,如下所示。

使用
使用
使用
camera encoder
backbone
SwinTransformer
neck
GeneralizedLSSFPN
vtransform
DepthLSSTransform

调用的顺序分别为backbone => neck => vtransform,具体代码如下所示。

        B, N, C, H, W = x.size()
        x = x.view(B * N, C, H, W)
		# backbone => SwinTransformer
        x = self.encoders["camera"]["backbone"](x)
        # neck => GeneralizedLSSFPN
        x = self.encoders["camera"]["neck"](x)

        if not isinstance(x, torch.Tensor):
            x = x[0]

        BN, C, H, W = x.size()
        x = x.view(B, int(BN / B), C, H, W)
		# vtransform => DepthLSSTransform
        x = self.encoders["camera"]["vtransform"](
            x,
            points,
            camera2ego,
            lidar2ego,
            lidar2camera,
            lidar2image,
            camera_intrinsics,
            camera2lidar,
            img_aug_matrix,
            lidar_aug_matrix,
            img_metas,
        )
        return x

1. backbone模块

1.1 Swin Transformer理论

backbone部分使用的是SwinTransformer,便于理解,首先简要介绍一下SwinTransformer的理论知识。

Swin Transformer的主要思想是把建模能力很强的transformer和视觉信号的先验联系起来,这些先验具有层次性、局部性和平移不变性,具体做法是用shifted window来建立分层特征图,有了分层特征图就可以用FPN/Unet等结构去做密集预测的任务,而且计算量与图片尺寸成正比。

Swin TransformerVision Transformer的不同:

  1. Swin Transformer使用了类似卷积神经网络中的层次化构建方法(Hierarchical feature maps),比如特征图尺寸中有对图像下采样4倍的,8倍的以及16倍的,这样的backbone有助于在此基础上构建目标检测,实例分割等任务。而在之前的Vision Transformer中是一开始就直接下采样16倍,后面的特征图也是维持这个下采样率不变。
  2. Swin Transformer中使用了Windows Multi-Head Self-Attention(W-MSA)的概念,将特征图划分成了多个不相交的区域(Window),并且Multi-Head Self-Attention只在每个窗口(Window)内进行。相对于Vision Transformer中直接对整个(Global)特征图进行Multi-Head Self-Attention,这样做的目的是能够减少计算量的,尤其是在浅层特征图很大的时候。这样做虽然减少了计算量但也会隔绝不同窗口之间的信息传递,所以在论文中作者又提出了 Shifted Windows Multi-Head Self-Attention(SW-MSA)的概念,通过此方法能够让信息在相邻的窗口中进行传递。

Swin Transformer的网络架构图如下所示。
在这里插入图片描述

  • Patch Partition模块:

首先将图片输入到Patch Partition模块中进行分块,即每4x4相邻的像素为一个Patch,然后在channel方向展平(flatten)。假设输入的是RGB三通道图片,那么每个patch就有4x4=16个像素,然后每个像素有R、G、B三个值所以展平后是16x3=48,所以通过Patch Partition后图像shape由 [H, W, 3]变成了 [H/4, W/4, 48]

  • Linear Embeding模块:

通过Linear Embeding层对每个像素的channel数据做线性变换,由48变成C,即图像shape再由 [H/4, W/4, 48]变成了 [H/4, W/4, C]

其实在源码中Patch PartitionLinear Embeding就是直接通过一个卷积层实现的。

  • Patch Merging模块:

在每个Stage中首先要通过一个Patch Merging层进行下采样(Stage1除外)。如下图所示,假设输入Patch Merging的是一个4x4大小的单通道特征图(feature map),Patch Merging会将每个2x2的相邻像素划分为一个patch,然后将每个patch中相同位置(同一颜色)像素给拼在一起就得到了4个feature map。接着将这四个feature map在深度方向进行concat拼接,然后在通过一个LayerNorm层。最后通过一个全连接层在feature map的深度方向做线性变化,将feature map的深度由C变成C/2。通过这个简单的例子可以看出,通过Patch Merging层后,feature map的高和宽会减半,深度会翻倍。
在这里插入图片描述

  • W-MSA(Windows Multi-head Self-Attention)模块:

引入Windows Multi-head Self-Attention(W-MSA)模块是为了减少计算量。如下图所示,左侧使用的是普通的Multi-head Self-Attention(MSA)模块,对于feature map中的每个像素(或称作tokenpatch)在Self-Attention计算过程中需要和所有的像素去计算。但在图右侧,在使用Windows Multi-head Self-Attention(W-MSA)模块时,首先将feature map按照MxM(例子中的M=2)大小划分成一个个Windows,然后单独对每个Windows内部进行Self-Attention
在这里插入图片描述

  • SW-MSA模块:

采用W-MSA模块时,只会在每个窗口内进行自注意力计算,所以窗口与窗口之间是无法进行信息传递的。为了解决这个问题,作者引入了Shifted Windows Multi-Head Self-Attention(SW-MSA)模块,即进行偏移的W-MSA。如下图所示,左侧使用的是刚刚讲的W-MSA(假设是第L层),那么根据W-MSASW-MSA是成对使用的,那么第L+1层使用的就是SW-MSA(右侧图)。根据左右两幅图对比能够发现窗口(Windows)发生了偏移(可以理解成窗口从左上角分别向右侧和下方各偏移了M/2个像素)。那么这就解决了不同窗口之间无法进行信息交流的问题。
在这里插入图片描述

1.2 patch embedding

首先需要说明的是,BEVFusionSwinTransformer调用的是mmdet库中的代码。它位于anacondalib/python3.8/site-packages/mmdet中。

patch embedding模块实现的是patch partitionlinear embedding功能,用来切patch并将patch特征嵌入到指定维度。直接用一个kernel_size=4stride=patch_size的卷积来实现。模型默认patch_size=4.

backbone的输入为(B * N, C, H, W) = (4 * 6, 3, 256, 704)。其中B = 4表示batchsize大小。N=6表示相机的个数。

x, hw_shape = self.patch_embed(x)

这一步实际上通过一个Conv2d(3, 96, kernel_size=(4, 4), stride=(4, 4))的卷积核操作,输入大小为(B * N, C, H, W) = (4 * 6, 3, 256, 704),经过卷积之后输出为:[24, 96, 64, 176],在经过flatten后大小变为[24, 11264, 96],最后再过一个LayerNorm层。代码如下所示:

        if self.adap_padding:
            x = self.adap_padding(x)

        x = self.projection(x) # Conv2d(3, 96, kernel_size=(4, 4), stride=(4, 4))
        out_size = (x.shape[2]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

非晚非晚

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

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

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

打赏作者

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

抵扣说明:

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

余额充值