[论文阅读]VoxSet——Voxel Set Transformer

VoxSet

Voxel Set Transformer: A Set-to-Set Approach to 3D Object Detection from Point Clouds
论文网址:VoxSet
论文代码:VoxSet

简读论文

这篇论文提出了一个称为Voxel Set Transformer(VoxSeT)的3D目标检测模型,主要有以下几个亮点:

  1. 提出了基于体素的集合自注意力(Voxel Set Attention,VSA)模块。该模块将整个场景划分为非重叠的体素,针对每个体素内的点云特征学习自注意力表示。其通过引入一组潜在代码来解决不同体素包含点数不同的问题,从而避免了其他方法中随机舍弃点或填充虚点的做法。

  2. 基于VSA模块,构建了端到端的VoxSeT检测器。网络主干采用VSA模块、多层感知机层交错堆叠的结构。并在最后将点云特征编码到BEV表示中,供检测头预测边界框。

  3. 在Waymo和KITTI两个数据集上进行了评估。结果显示VoxSeT能够获得与当前最先进方法相媲美的性能,表明其可以作为点云建模的一个有竞争力的替代方案。

  4. 作者通过可视化分析等 ablation 实验验证了VSA模块的有效性,如其可以聚焦于目标区域,不同的潜在代码可以编码目标的不同上下文信息等。

  5. 与其他基于卷积或点的方法相比,VoxSeT在两个数据集上的表现更为稳定,显示了其泛化能力的优势。

总的来说,这篇论文通过集合自注意力的方式,将点云处理建模为一个集合到集合的转换问题,提供了一个新的基于transformer的检测器框架,值得学习和参考。

摘要

Transformer 在许多 2D 视觉任务中表现出了良好的性能。然而,由于点云是一个长序列并且在3D空间中分布不均匀,因此在大规模点云数据上计算自注意力是很麻烦的。为了解决这个问题,现有的方法通常通过将点分组为相同大小的簇来本地计算自注意力,或者对离散表示执行卷积自注意力。然而,前者会导致随机点丢失,而后者通常具有狭窄的注意力范围。在本文中,我们提出了一种新颖的基于体素的架构,即Voxel Set Transformer(VoxSeT),通过集合到集合的转换来检测点云中的 3D 对象。 VoxSeT 建立在基于体素的集合注意力(VSA)模块之上,该模块通过两个交叉注意力减少每个体素中的自注意力,并对一组潜在代码引起的隐藏空间中的特征进行建模。借助VSA模块,VoxSeT可以管理大范围内任意大小的体素化点簇,并以线性复杂度并行处理它们。所提出的 VoxSeT 将 Transformer 的高性能与基于体素的模型的效率相结合,可以作为卷积和基于点的主干网的良好替代方案。

引言

3D 点云的对象检测已受到广泛关注,因为它为自动驾驶、机器人和虚拟现实等许多应用提供了支持。与 2D 图像不同,3D 点云在连续空间中自然稀疏且分布不均匀,阻碍了 CNN 层的直接应用。为了解决这个问题,一些方法首先将点云转换为离散表示,然后应用CNN模型来提取高维特征。另一类方法对连续空间中的点云进行建模,其中通过交错分组和聚合步骤提取多尺度特征。
除了上述两种方案之外,基于Transformer的模型最近在处理点云数据方面引起了极大的兴趣,因为Transformer中使用的自注意力对于输入组件的排列和基数是不变的,这使得 Transformer 成为点云处理的合适选择。然而,Transformer 模型的主要局限性在于自注意力计算是二次的。每个token都必须使用前一层中的所有其他令牌进行更新,这使得自注意力对于长序列点云来说变得棘手。 Point Transformer 在 PointNet 架构上构建Transformer,该架构将点云数据分层分组到不同的集群中,并计算每个集群中的自注意力。 CT3D 提出了一个两阶段点云检测器,其中提取 3D RoIs 以对第一阶段中的原始点进行分组,并将Transformer应用于第二阶段中的分组点。
在这里插入图片描述
然而,由于点云的分布极其不均匀,每个簇中的点数量差异很大。为了使自注意力能够并行运行,当前的方法通过随机丢弃点或填充虚拟点来平衡每个集群中的token数量(见图1(a))。这导致检测结果不稳定和冗余计算。此外,每次将n个点分组到m个簇的操作都会花费O(nm)的复杂度,这是相对密集的。或者,Voxel Transformer 在离散体素网格上执行自注意力,如图 1(b) 所示。它以卷积方式计算自注意力,因此与具有 O(n) 复杂度的稀疏卷积一样高效。然而,由于卷积注意力是逐点操作,为了节省内存,卷积核的注意力域通常很小,从而阻碍了体素Transformer对长程依赖性进行建模。值得一提的是,尽管 Group-free 和 3DETR 通过在一组减少的种子点上计算自注意力,提出了一种很有前途的解决方案,但该解决方案仅适用于点云相对密集的室内场景并集中。考虑到室外场景的点云通常稀疏、大规模(例如> 20k)且分布不均匀,种子点的规模和覆盖范围仍然是一个问题。
为了解决上述问题,本文引入了基于体素的集合注意力(VSA)模块。对于每个 VSA,将整个场景划分为不重叠的 3D 体素,并即时高效地计算输入点的体素索引。使用这些体素来确定注意力区域,类似于 SwinTransformer 中的窗口注意力。与图像不同,LiDAR具有不规则的结构,并且产生的注意力组具有不同的长度,这阻碍了模型的并行化。
受induced set Transformer的启发,本文为每个体素分配一组可训练的“潜在代码”。这些潜在代码为点云构建了一个固定长度的瓶颈,通过该瓶颈,来自体素内输入点的信息可以被压缩到静态隐藏空间。该公式基于以下关键观察:自注意力矩阵通常是低秩的,因此可以将密集的完全自注意力分解为两个连续的交叉注意力模块。如图 1© 所示,VSA 首先通过关注输入点的投影特征(即键和值)将用作查询的潜在代码转换为隐藏空间。变换后的隐藏特征对每个体素中输入点的上下文信息进行编码,并通过卷积前馈网络进行丰富,其中跨体素的特征在空间域中交换其信息。之后,隐藏特征与输入仔细融合,产生输入分辨率的输出特征。通过利用潜在代码,可以对所有体素中执行的交叉注意力进行向量化,使 VSA 成为高度并行的模块。给定 n 维输入特征和 k 个潜在代码,VSA 的复杂度为 O(nkd),并且可以用一般的矩阵乘法来实现。
通过 VSA,本文提出了一种voxel set Transformer (VoxSeT),通过在集合到集合的转换过程中学习点云特征来检测 3D 对象。 VoxSeT 由 VSA 模块、MLP 层和用于鸟瞰 (BEV) 特征提取的浅层 CNN 组成。为了验证所提出模型的有效性,本文在两个 3D 检测基准 KITTI 和 Waymo 开放数据集上进行了实验。 VoxSeT 实现了与当前最先进技术竞争的性能。此外,所提出的 VSA 模块可以无缝地采用基于点的检测器,例如 PointRCNN ,并展示了相对于集合抽象模块的优势。
总之,本文首先发明了一种基于体素的集合注意力模块,它可以对任意大小的令牌簇的远程依赖性进行建模,绕过当前基于分组和基于卷积的注意力模块的限制。然后,提出了一个voxel set transformer,通过利用transformer在大规模序列数据上的优势来有效地学习点云特征。本文的工作为 3D 点云数据处理的当前卷积和基于点的主干提供了一种新颖的替代方案。

相关工作

基于点云的3D目标检测

早期的点云 3D 对象检测方法可以分为两类。第一类方法将点云转换为更紧凑的表示,例如鸟瞰(BEV)图像、正面视图范围图像和体积特征。严等人开发了一种稀疏卷积主干,通过将点云编码为 3D 稀疏张量来有效地处理点云。朗等人通过将体素特征堆叠为“支柱”并使用2D CNN进行处理,进一步加快了检测率。另一类方法通过采用 PointNet架构来处理连续空间中的点云。通过交错分组和采样操作分阶段提取多尺度中的逐点特征。施等人和杨等人,建议从 PointNet 输出生成 3D RoIs,并将 RoIs 应用到逐点特征分组以进一步细化。齐等人提出了一种深度投票方法,对物体表面的点进行聚类,以检测点不足的物体。与紧凑表示不同,逐点特征保留了原始点云的更多细节和细粒度结构。基于这一事实,一些方法在点和体素空间中采用混合表示来实现更可靠的检测输出。
本文提出的架构很大程度上是由基于体素的方法推动的。本文将点云划分为体素网格并在本地执行自注意力,赋予模型归纳偏差和计算效率。

点云分析中的transformer

最近,Transformer 在许多计算机视觉任务中展示了其巨大成功,例如图像分类、2D 目标检测和其他密集预测任务 。对于点云分析,Zhao 等人提出了一种基于减法注意力的新型点云分类和分割算子。郭等人研究了在特征和边缘空间中处理点云的双重关注。米斯拉等人和刘等人使用 Transformer 将点云作为顺序数据进行处理,防止模型堆叠分层分组和采样模块。苗等人将自注意力嵌入到稀疏卷积核中。盛等人将transformer构建在两级检测器之上,并对按 RoI 分组的点进行关注。
与上述在固定大小的标记簇上执行自注意力的方法不同,本文提出的voxel set transformer利用induced set transformer的思想将自注意力分解为两个交叉注意力,从而可以执行自注意力注意任意大小的标记簇。

方法

准备工作

由于其二次计算复杂性,直接将自注意力应用于点云数据是禁止的。为了绕过这个问题,[Set transformer]中提出了induced set注意力块,其中集合中的完整自注意力由一组潜在代码诱导的两个减少的交叉注意力来近似。给定大小为 n、维度为 d 的输入集 X ∈ Rn×d 和 k 个潜在代码 L ∈ Rk×d,来自induced set注意力块的输出集 O ∈ Rn×d 可以表示为:
在这里插入图片描述
第一个交叉注意力通过关注输入集将潜在特征 L 转换为隐藏特征 H。此步骤的复杂度为 O(nkd),与 n 成线性关系,因为潜在代码的数量 k 是固定的并且通常非常小。转换后的隐藏特征包含有关输入集 X 的信息,然后通过点前馈网络(FFN)更新它们。这种逐点操作的复杂度为 O(k),并且它从输入集中学习高度语义的特征。第二个交叉注意力关注输入集到生成的隐藏特征,这会花费 O(nkd) 复杂度,产生长度为 n 的输出集。诱导集注意力基于自注意力可以用低秩投影来近似的假设,因此自注意力可以被视为对输入执行 k 聚类,其中潜在代码作为聚类中心。这也类似于聚类注意力 和 Linformer,其中输入集通过线性投影显式减少。

Voxel-based Set Attention (VSA)

class MLP(nn.Module):""" Very simple multi-layer perceptron (also called FFN)"""def __init__(self, input_dim, hidden_dim, output_dim, num_layers):super().__init__()self.num_layers = num_layersh = [hidden_dim] * (num_layers - 1)self.layers = nn.ModuleList(nn.Linear(n, k) for n, k in zip([input_dim] + h, h + [output_dim]))def forward(self, x):for i, layer in enumerate(self.layers):x = F.relu(layer(x)) if i < self.num_layers - 1 else layer(x)return xclass VoxSeT(VFETemplate):def __init__(self, model_cfg, num_point_features, voxel_size, point_cloud_range, grid_size, **kwargs):super().__init__(model_cfg=model_cfg)self.num_latents = self.model_cfg.NUM_LATENTSself.input_dim = self.model_cfg.INPUT_DIMself.output_dim = self.model_cfg.OUTPUT_DIMself.input_embed = MLP(num_point_features, 16, self.input_dim, 2)self.pe0 = PositionalEncodingFourier(64, self.input_dim)self.pe1 = PositionalEncodingFourier(64, self.input_dim * 2)self.pe2 = PositionalEncodingFourier(64, self.input_dim * 4)self.pe3 = PositionalEncodingFourier(64, self.input_dim * 8)self.mlp_vsa_layer_0 = MLP_VSA_Layer(self.input_dim * 1, self.num_latents[0])self.mlp_vsa_layer_1 = MLP_VSA_Layer(self.input_dim * 2, self.num_latents[1])self.mlp_vsa_layer_2 = MLP_VSA_Layer(self.input_dim * 4, self.num_latents[2])self.mlp_vsa_layer_3 = MLP_VSA_Layer(self.input_dim * 8, self.num_latents[3])self.post_mlp = nn.Sequential(nn.Linear(self.input_dim * 16, self.output_dim),nn.BatchNorm1d(self.output_dim, eps=1e-3, momentum=0.01),nn.ReLU(),nn.Linear(self.output_dim, self.output_dim),nn.BatchNorm1d(self.output_dim, eps=1e-3, momentum=0.01),nn.ReLU(),nn.Linear(self.output_dim, self.output_dim),nn.BatchNorm1d(self.output_dim, eps=1e-3, momentum=0.01))self.register_buffer('point_cloud_range', torch.FloatTensor(point_cloud_range).view(1, -1))self.register_buffer('voxel_size', torch.FloatTensor(voxel_size).view(1, -1))self.grid_size = grid_size.tolist()a, b, c = voxel_sizeself.register_buffer('voxel_size_02x', torch.FloatTensor([a * 2, b * 2, c]).view(1, -1))self.register_buffer('voxel_size_04x', torch.FloatTensor([a * 4, b * 4, c]).view(1, -1))self.register_buffer('voxel_size_08x', torch.FloatTensor([a * 8, b * 8, c]).view(1, -1))a, b, c = grid_sizeself.grid_size_02x = [a // 2, b //  2, c]self.grid_size_04x = [a // 4, b //  4, c]self.grid_size_08x = [a // 8, b //  8, c]def get_output_feature_dim(self):return self.output_dimdef forward(self, batch_dict, **kwargs):points = batch_dict['points']points_offsets = points[:, 1:4] - self.point_cloud_range[:, :3]coords01x = points[:, :4].clone()coords01x[:, 1:4] = points_offsets // self.voxel_sizepe_raw = (points_offsets - coords01x[:, 1:4] * self.voxel_size  ) / self.voxel_sizecoords01x, inverse01x = torch.unique(coords01x, return_inverse=True, dim=0)coords02x = points[:, :4].clone()coords02x[:, 1:4] = points_offsets // self.voxel_size_02xcoords02x, inverse02x = torch.unique(coords02x, return_inverse=True, dim=0)coords04x = points[:, :4].clone()coords04x[:, 1:4] = points_offsets // self.voxel_size_04xcoords04x, inverse04x = torch.unique(coords04x, return_inverse=True, dim=0)coords08x = points[:, :4].clone()coords08x[:, 1:4] = points_offsets // self.voxel_size_08xcoords08x, inverse08x = torch.unique(coords08x, return_inverse=True, dim=0)src = self.input_embed(points[:, 1:]) src = src + self.pe0(pe_raw)src = self.mlp_vsa_layer_0(src, inverse01x, coords01x, self.grid_size)src = src + self.pe1(pe_raw)src = self.mlp_vsa_layer_1(src, inverse02x, coords02x, self.grid_size_02x)src = src + self.pe2(pe_raw)src = self.mlp_vsa_layer_2(src, inverse04x, coords04x, self.grid_size_04x)src = src + self.pe3(pe_raw)src = self.mlp_vsa_layer_3(src, inverse08x, coords08x, self.grid_size_08x)src = self.post_mlp(src)batch_dict['point_features'] = F.relu(src)batch_dict['point_coords'] = points[:, :4]batch_dict['pillar_features'] = F.relu(torch_scatter.scatter_max(src, inverse01x, dim=0)[0])batch_dict['voxel_coords'] = coords01x[:, [0, 3, 2, 1]]return batch_dictclass MLP_VSA_Layer(nn.Module):def __init__(self, dim, n_latents=8):super(MLP_VSA_Layer, self).__init__()self.dim = dimself.k = n_latents self.pre_mlp = nn.Sequential(nn.Linear(dim, dim),nn.BatchNorm1d(dim,eps=1e-3, momentum=0.01),nn.ReLU(),nn.Linear(dim, dim),nn.BatchNorm1d(dim,eps=1e-3, momentum=0.01),nn.ReLU(),nn.Linear(dim, dim),nn.BatchNorm1d(dim,eps=1e-3, momentum=0.01),)# the learnable latent codes can be obsorbed by the linear projectionself.score = nn.Linear(dim, n_latents)conv_dim = dim * self.kself.conv_dim = conv_dim# conv ffnself.conv_ffn = nn.Sequential(           nn.Conv2d(conv_dim, conv_dim, 3, 1, 1, groups=conv_dim, bias=False), nn.BatchNorm2d(conv_dim),nn.ReLU(),nn.Conv2d(conv_dim, conv_dim, 3, 1, 1, groups=conv_dim, bias=False), nn.BatchNorm2d(conv_dim),nn.ReLU(),# nn.Conv2d(conv_dim, conv_dim, 3, 1, dilation=2, padding=2, groups=conv_dim, bias=False),# nn.BatchNorm2d(conv_dim),# nn.ReLU(), nn.Conv2d(conv_dim, conv_dim, 1, 1, bias=False), ) # decoderself.norm = nn.BatchNorm1d(dim,eps=1e-3, momentum=0.01)self.mhsa = nn.MultiheadAttention(dim, num_heads=1, batch_first=True) def forward(self, inp, inverse, coords, bev_shape):x = self.pre_mlp(inp)# encoderattn = torch_scatter.scatter_softmax(self.score(x), inverse, dim=0)dot = (attn[:, :, None] * x.view(-1, 1, self.dim)).view(-1, self.dim*self.k)x_ = torch_scatter.scatter_sum(dot, inverse, dim=0)# conv ffnbatch_size = int(coords[:, 0].max() + 1)h = spconv.SparseConvTensor(F.relu(x_), coords.int(), bev_shape, batch_size).dense().squeeze(-1)h = self.conv_ffn(h).permute(0,2,3,1).contiguous().view(-1, self.conv_dim)flatten_indices = coords[:, 0] * bev_shape[0] * bev_shape[1] + coords[:, 1] * bev_shape[1] + coords[:, 2]h = h[flatten_indices.long(), :] h = h[inverse, :]# decoderhs = self.norm(h.view(-1,  self.dim)).view(-1, self.k, self.dim)hs = self.mhsa(x.view(-1, 1, self.dim), hs, hs)[0]hs = hs.view(-1, self.dim)# skip connectionreturn torch.cat([inp, hs], dim=-1)class PositionalEncodingFourier(nn.Module):"""Positional encoding relying on a fourier kernel matching the one used in the"Attention is all of Need" paper. The implementation builds on DeTR codehttps://github.com/facebookresearch/detr/blob/master/models/position_encoding.py"""def __init__(self, hidden_dim=64, dim=128, temperature=10000):super().__init__()self.token_projection = nn.Linear(hidden_dim * 3, dim)self.scale = 2 * math.piself.temperature = temperatureself.hidden_dim = hidden_dimdef forward(self, pos_embed, max_len=(1, 1, 1)):z_embed, y_embed, x_embed = pos_embed.chunk(3, 1)z_max, y_max, x_max = max_leneps = 1e-6z_embed = z_embed / (z_max + eps) * self.scaley_embed = y_embed / (y_max + eps) * self.scalex_embed = x_embed / (x_max + eps) * self.scaledim_t = torch.arange(self.hidden_dim, dtype=torch.float32, device=pos_embed.device)dim_t = self.temperature ** (2 * (dim_t // 2) / self.hidden_dim)pos_x = x_embed / dim_tpos_y = y_embed / dim_tpos_z = z_embed / dim_tpos_x = torch.stack((pos_x[:, 0::2].sin(),pos_x[:, 1::2].cos()), dim=2).flatten(1)pos_y = torch.stack((pos_y[:, 0::2].sin(),pos_y[:, 1::2].cos()), dim=2).flatten(1)pos_z = torch.stack((pos_z[:, 0::2].sin(),pos_z[:, 1::2].cos()), dim=2).flatten(1)pos = torch.cat((pos_z, pos_y, pos_x), dim=1)pos = self.token_projection(pos)return pos   

与图像不同,点云分布广泛,在场景层面语义关联较弱,而在局部区域具有较强的结构细节。本文不是将所有输入点压缩到隐藏空间中,而是修改上述indeuced set注意力以在本地执行。具体来说,将场景划分为体素网格,并为每个体素分配一组潜在代码。将该模块称为基于体素集合的注意力(VSA)。
Scatter kernel function. : 如前所述,VSA 是一个高度并行的模块,其中跨体素的操作可以矢量化。这种矢量化可以通过 scatter function 来实现,它是一个 cuda 内核库,可以在矩阵的不同段上执行对称约简,例如求和、最大值和平均值。在本文的例子中,将输入集视为单个矩阵,其中的每一行对应于一个逐点特征,并且其所属的体素可以通过体素坐标表来索引。
设 {pi = (xi, yi, zi) : i = 1, …n} 表示点云的坐标,[dx, dy, dz] 为三维像素大小。体素坐标 V 可以通过以下公式计算: V = {Vi = (⌊xi/dx⌋, ⌊yi/dy⌋, ⌊zi/dz⌋) : i = 1, …, n},其中⌊ . ⌋ 是下取整函数。因此,给定逐点输入特征 {Xi : 1 = 1, …n},在对称函数 F(·) 之后它们的简化体素形式 {Yj : j = 1, …,m} 可以是表示为:
在这里插入图片描述
其中 m 是非空体素的数量。利用散点函数Fscatter,上式可以写成向量化的形式,即
在这里插入图片描述
通过部署 VSA,不需要随机删除或填充每个体素中的点,并且模型的复杂度是线性的。
在图 2 中,为了便于理解,本文以矩阵乘法的形式说明了 VSA。可以看出,该模块类似于编码器-解码器架构,其中输入集被编码到隐藏空间,然后通过 ConvFFN 细化隐藏特征,最后解码以产生输出集。
在这里插入图片描述
Encoder. : 在编码器中,首先使用线性投影将前一个模块的输入特征分别投影到键 K ∈ Rn×d 和值 V ∈ Rn×d。接下来,在key和潜在代码(查询)L ∈ Rk×d 之间执行交叉注意力,产生注意力矩阵 A ∈ Rn×k×d。然后对注意力矩阵 A 进行体素归一化以获得 A~ ,并与该value相乘,产生隐藏特征 H~ 。 H~的计算可以表示为:
在这里插入图片描述
之后,根据体素索引 V 对隐藏特征进行体素缩减:
在这里插入图片描述
编码器中的总体计算包括两个 GEMM 和两个分散操作。编码器的整体复杂度为 O(2n(k + 1)d)。
值得一提的是,基于交叉注意力的编码方案可以被视为[pointpillars,second,voxelnet]中使用的体素特征编码(VFE)的扩展。不同之处在于,VFE 将体素内的点编码为单个特征向量,而本文的方案则基于由潜在特征组成的码本对点进行编码。由于VSA的高表达能力,可以使用相对较大的体素尺寸来捕获大范围的特征。
Convolutional feed-forward network. : VSA 的核心思想是使用潜在代码将区域特征编码到隐藏空间中。隐藏特征作为瓶颈,通过它本文应用ConvFFN来实现更灵活和复杂的信息更新。与仅执行逐点标记更新的传统 FFN 不同,ConvFFN 能够实现跨体素的信息交换,这对于密集预测尤其重要。为了自适应地将体素特征与全局依赖性相结合,根据体素坐标 Cr 将减少的隐藏特征分散到 3D 稀疏张量中,然后对它们进行两个深度卷积(DwConv)以强制空间域中的特征交互。给定卷积权重 W1 和 W2,来自 ConvFFN 的丰富隐藏特征 Hˆ r ∈ Rm×k×d 可以写为:
在这里插入图片描述
其中 σ 表示非线性激活,T 表示稀疏张量的公式,DwConv 中的组数等于潜在特征的组数。此操作的复杂度为 O(HWDkd/dxdydz ),其中 H、W、D 分别指三个方向的点云范围,[dx, dy, dz] 指定体素大小。 ConvFFN 在 VSA 中发挥着重要作用,因为它为模块引入了理想的归纳偏差和全局上下文。
Decoder. : 解码器根据丰富的隐藏特征 Hˆ r 重建输出集。具体来说,首先基于体素索引 V 广播隐藏特征,产生与输入集长度相同的 Hˆ ∈ Rn×k×d。然后,使用线性投影分别从输入集和隐藏特征生成查询、键值对。给定查询 Q ∈ Rn×d、键 K ∈ Rn×k×d 和值 V ∈ Rn×k×d 的矩阵,解码器输出 O 可以计算为:
在这里插入图片描述
解码器的整体计算复杂度为O(2nkd)。由于交叉注意力机制的灵活性,VSA 将点云处理表述为一个 set-set 转换问题。
Relative position embedding. : 保留点云的局部结构对于输入特征和输出特征提高性能至关重要。因此,本文引入了位置嵌入(PE)模块,将体素内点云的局部坐标编码为高维特征,并将它们注入到每个 VSA 模块中。具体来说,PE 模块应用傅里叶参数化来取值 [sin(fkπx), cos(fkπ, x)],给定归一化局部坐标 x ∈ [0, 1] 和带宽为 L 的第 k 个频率 fk。得到的傅里叶embedding 的维度为 3L,通过可学习的线性层进一步映射到第一个 MLP 模块的输入维度。

Voxel Set Transformer (VoxSeT)

VoxSeT 的整体架构如图 3 所示。遵循传统的 Transformer 范例,VoxSeT 主干由互连的多层感知 (MLP) 和 VSA 模块组成。使用批量归一化作为归一化层,并将每个 VSA 模块包装到残差块中以获得最佳梯度流。
在这里插入图片描述
与基于分组的方法逐步下采样和聚合逐点特征以进行上下文提取不同,本文的骨干网将点云特征提取为集合到集合的转换过程。特征的语义级别由 VSA 模块中体素的大小控制。本文凭经验发现,应用大体素可以学习更丰富的上下文信息,并更好地理解具有稀疏点的对象,特别是行人和骑自行车的人实例。
Birds-eye-view feature encoding. : 在点云检测中,一个常见的现象是使用密集鸟瞰(BEV)特征的模型通常比使用稀疏逐点特征的模型获得更高的召回率。在这方面,本文将主干网的逐点特征编码为 BEV 表示,并应用浅层 CNN 来增加特征密度。 CNN 只有两个跨步,每个跨步涉及三个卷积。两个步幅的卷积特征最终被连接并传递到检测头进行边界框预测。为了生成 BEV 特征,将逐点特征聚合在尺寸为 0.36m×0.36m 的柱子内,并应用“软池化”操作来生成 BEV 特征。给定第 j 个支柱中的逐点输出特征 Xj ∈ Rk×d,池化 Fj 后的支柱特征可以表示为:
在这里插入图片描述
Detection head and training objectives. : 为了增强 VoxSeT 主干的表现力,本文遵循 PointRCNN 将前景分割损失 Lseg 应用于输出特征。这迫使 VoxSeT 捕获上下文信息以生成准确的边界框。检测头遵循传统的基于锚的设计。最终的损失就变成:
在这里插入图片描述
其中Np是与anchor的IoU在[σ1,σ2]之间的正样本的数量。 Lcls 是边界框分类的焦点损失,Lreg 是边界框偏移回归的 Smooth-Ll 损失。 Ldir 是用于边界框方向预测的二元熵损失。
Two-stage model. : 值得注意的是,VoxSeT 可以扩展到两级检测器,其中采用 LiDAR-RCNN 中的高效 RoI 头作为本文的第二级模块。

结论

本文提出了 VoxSeT,这是一种基于 Transformer 的新颖框架,用于从 LiDAR 点云进行 3D 对象检测。与之前使用稀疏 CNN 和 PointNet 主干网络来学习点云特征的 3D LiDAR 检测器相比,首次尝试将点云处理建模为集合到集合的转换,从而在每次处理时都保留原始点云的完整分辨率。特征提取步骤,本文提出了一种基于体素的集合注意力模块,该模块对任意大小的体素簇执行自注意力,并使用来自大感受野的更具辨别力的上下文信息对点特征进行编码。 Waymo 和 KITTI 数据集上的实验结果表明, VoxSeT 可以实现具有竞争力的性能,使其成为点云建模的良好替代方案。
应该指出的是,在 VoxSeT 中,本文只探索了一种基于诱导潜在代码的线性注意力的可能表述。这限制了 VoxSeT 表示不同点云结构及其相关性的表达能力。通过使用更强的注意力机制,VoxSeT的性能可以进一步提高。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/187438.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

QML学习一、GridView的使用和增加添加动画、删除动画

一、效果预览 二、源码分享 import QtQuick import QtQuick.ControlsApplicationWindow {visible: truewidth: 640height: 480title: "Test"property int cnt:cnt model.countListModel{id:modelListElement{index:0}ListElement{index:1}ListElement{index:2}List…

Docker 使用心得

创建一个docker 镜像&#xff0c;相关运行代码&#xff0c;放在docker镜像文件同级&#xff0c; pm2 不能与 docker一起使用&#xff08;&#xff09; # node 服务docker FROM node:10.16.3LABEL author"sj"RUN mkdir -p /var/nodeCOPY ./node /var/nodeWORKDIR /va…

java_springboot企业人事考勤请假管理信息系统rsglxx+jsp

&#xff08;1&#xff09;熟练掌握Java开发的原理和方法 &#xff08;2&#xff09;熟练学习掌握SSM框架 &#xff08;3&#xff09;熟悉软件开发的流程 &#xff08;4&#xff09;了解中内外互联网中所主流的技术 &#xff08;5&#xff09;深层次的了解计算机学科领域的知识…

CCS中静态库lib的生成与调用

在调试DSP设备的时候&#xff0c;发现好多工程会把比较核心的代码生成静态库lib&#xff0c;代码运行的时候直接调用lib里面的相关函数就行。但是从外部是看不到lib库里面的内容的&#xff0c;这样通过静态库的方式实现对代码的加密。 在网上找了好久如何将函数生成静态库*.lib…

Mybatis 的简单运用介绍

Mybatis 用于操作数据库 操作数据库肯定需要: 1.SQL语句 2.数据库对象和 java 对象的映射 接下来我们看看怎么使用 Mybatis 我们先搞一些数据库内容 然后将其这些内容和Java对象进行映射 再创建一个类实现 select * from 再写一个类证明上述代码是否可以实现 别忘了在appli…

unity学习笔记13

一、常用物理关节 Unity中的物理关节&#xff08;Physics Joints&#xff09;是用于在游戏中模拟和控制物体之间的连接。物理关节允许你在对象之间应用各种约束&#xff0c;例如旋转、移动或固定连接&#xff0c;以模拟真实世界中的物理交互。 物理关节类型&#xff1a; 1.F…

C 语言-数组

1. 数组 1.1 引入 需求&#xff1a;记录班级10个学员的成绩 需要定义10个变量存在的问题:变量名起名困难变量管理困难需求&#xff1a;记录班级1000个学员的成绩 1.2 概念 作用&#xff1a;容纳 数据类型相同 的多个数据的容器 。 特点&#xff1a; 长度不可变容纳 数据类型…

Django 用户验证与权限管理详解

概要 Django是一款强大且灵活的Python Web框架&#xff0c;不仅在构建功能复杂的网站应用中表现出色&#xff0c;还在诸如用户验证、权限管理等细微之处提供了优秀的解决方案。在多用户、权限复杂的Web应用中&#xff0c;认证和权限管理尤其重要。接下来&#xff0c;我们就来探…

数据之美:数据可视化的力量与必要性

在当今信息爆炸的时代&#xff0c;数据量呈指数级增长&#xff0c;它们是我们日常生活和工作中的重要组成部分。然而&#xff0c;数据本身是无生命的数字和统计&#xff0c;若不能有效地被理解、传达和利用&#xff0c;其潜力就难以实现。这正是数据可视化变得越来越重要的原因…

深入了解Java8新特性-日期时间API之ChronoUnit、ChronoField

阅读建议 嗨&#xff0c;伙计&#xff01;刷到这篇文章咱们就是有缘人&#xff0c;在阅读这篇文章前我有一些建议&#xff1a; 本篇文章大概3000多字&#xff0c;预计阅读时间长需要5分钟。本篇文章的实战性、理论性较强&#xff0c;是一篇质量分数较高的技术干货文章&#x…

基于gitlab的webhook集成jenkins,并在gitlab流水线中展示jenkins的job状态信息

文章目录 1. 环境信息2. gitlab 部署3. jenkins部署4. gitlab集成jenkins4.1 jenkins的凭据上保存gitlab的账号信息4.2 jenkins中配置gitlab的连接信息4.3 编写jenkins上pipeline文件4.4 jenkins上创建pipeline项目4.5 gitlab上配置webhooks事件4.6 测试 1. 环境信息 gitlab服…

LangChain 16 通过Memory记住历史对话的内容

LangChain系列文章 LangChain 实现给动物取名字&#xff0c;LangChain 2模块化prompt template并用streamlit生成网站 实现给动物取名字LangChain 3使用Agent访问Wikipedia和llm-math计算狗的平均年龄LangChain 4用向量数据库Faiss存储&#xff0c;读取YouTube的视频文本搜索I…

Windows系列:windows2003-建立域

windows2003-建立域 Active Directory建立DNS建立域查看日志xp 加入域 Active Directory 活动目录是一个包括文件、打印机、应用程序、服务器、域、用户账户等对象的数据库。 常见概念&#xff1a;对象、属性、容器 域组件&#xff08;Domain Component&#xff0c;DC&#x…

snat与dnat

一.SNAT的原理介绍 1.应用环境 局域网主机共享单个公网IP地址接入Internet &#xff08;私有IP不能在Internet中正常路由&#xff09; 2.SNAT原理 源地址转换&#xff0c;根据指定条件修改数据包的源IP地址&#xff0c;通常被叫做源映谢 数据包从内网发送到公网时&#xf…

Loki安装部署

Loki安装部署 1、Loki介绍 Loki 是受 Prometheus 启发由 Grafana Labs 团队开源的水平可扩展&#xff0c;高度可用的多租户日志聚合系统。开发语 言: Google Go。它的设计具有很高的成本效益&#xff0c;并且易于操作。使用标签来作为索引&#xff0c;而不是对全文进行检索&…

<avue-crud/>,二级表头,children下字典项的dicUrl失效问题

目录 1.提出问题&#xff1a; 1.1 代码&#xff1a; 1.2 效果图&#xff1a;会发现处在children下的dicUrl失效了 2. 解决思路 3. 解决代码&#xff08;你要的都在这&#xff0c;看这里&#xff09; 1.提出问题&#xff1a; 在使用<avue-crud/>组件实现二级表头时&…

C++中异常的栈展开概念

C中的异常栈展开是指&#xff0c;当某个函数中有异常产生&#xff08;这里不考虑是主动抛出的还是被动产生的&#xff09;&#xff0c;在异常被捕获之前的函数调用链上&#xff0c;函数不会正常执行返回&#xff0c;即异常产生之后的程序逻辑不会被执行。 &#xff08;注意&…

Kafka的存储机制和可靠性

文章目录 前言一、Kafka 存储选择二、Kafka 存储方案剖析三、Kafka 存储架构设计四、Kafka 日志系统架构设计4.1、Kafka日志目录布局4.2、Kafka磁盘数据存储 五、Kafka 可靠性5.1、Producer的可靠性保证5.1.1、kafka 配置为 CP(Consistency & Partition tolerance)系统5.1.…

建堆的时间复杂度和堆排序

文章目录 建堆的时间复杂度向下调整建堆向上调整建堆 堆排序实现 建堆的时间复杂度 下面都以建大堆演示 向下调整建堆 void Adjustdown(HPDataType* a, int size,int parent) {int child parent * 2 1;while (child < size){if (child1<size&&a[child 1] &…

Pandas进阶:transform 数据转换的常用技巧

引言 本次给大家介绍一个功能超强的数据处理函数transform&#xff0c;相信很多朋友也用过&#xff0c;这里再次进行详细分享下。 transform有4个比较常用的功能&#xff0c;总结如下&#xff1a; 转换数值 合并分组结果 过滤数据 结合分组处理缺失值 一. 转换数值 pd.…