文章目录
BEVFusion
相关的其它文章链接:
camera
的encoder
主要有3部分,分别是backbone
、neck
和vtransform
部分。其中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: Hierarchical Vision Transformer using Shifted Windows
- 论文地址:https://arxiv.org/abs/2103.14030
- 开源地址:https://github.com/microsoft/Swin-Transformer
Swin Transformer
的主要思想是把建模能力很强的transformer
和视觉信号的先验联系起来,这些先验具有层次性、局部性和平移不变性
,具体做法是用shifted window
来建立分层特征图,有了分层特征图就可以用FPN/Unet
等结构去做密集预测的任务,而且计算量与图片尺寸成正比。
Swin Transformer
和Vision Transformer
的不同:
Swin Transformer
使用了类似卷积神经网络中的层次化构建方法(Hierarchical feature maps
),比如特征图尺寸中有对图像下采样4倍的,8倍的以及16倍
的,这样的backbone
有助于在此基础上构建目标检测,实例分割等任务。而在之前的Vision Transformer
中是一开始就直接下采样16倍
,后面的特征图也是维持这个下采样率不变。- 在
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 Partition
和Linear 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
中的每个像素(或称作token
,patch
)在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-MSA
和SW-MSA
是成对使用的,那么第L+1
层使用的就是SW-MSA
(右侧图)。根据左右两幅图对比能够发现窗口(Windows
)发生了偏移(可以理解成窗口从左上角分别向右侧和下方
各偏移了M/2
个像素)。那么这就解决了不同窗口之间无法进行信息交流的问题。
1.2 patch embedding
首先需要说明的是,BEVFusion
的SwinTransformer
调用的是mmdet
库中的代码。它位于anaconda
的lib/python3.8/site-packages/mmdet
中。
patch embedding
模块实现的是patch partition
和linear embedding
功能,用来切patch
并将patch
特征嵌入到指定维度。直接用一个kernel_size=4
和stride
=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]