点云数据体素化后,有90%+的Voxel是空的,如果像VoxelNet那样直接使用3D Conv,计算量太大。

左图是稀疏的2D Tensor,深灰色像素都是0,浅灰色是non-zero点。

右图是稀疏的3D Tensor,只有红色的体素才是non-zero。

因此提出了3D稀疏卷积——3D Sparse Convolution。传统的3D卷积则被称为稠密卷积——Dense Convolution。

核心机制

Dense Conv处理的是矩阵。

Sparse Conv处理的是表。

  • Sparse Conv使用类似于哈希表的结构,只记录坐标[X,Y,Z][X, Y, Z]和特征向量。

  • 做卷积时,算法会提前计算好,当卷积核在有数据的地方滑动时,哪些输入点会和卷积核的哪个权重相乘。这个过程是在构建Rulebook。

  • 计算时直接根据Rule把对应的数据拿出来算一下就行,不需要遍历整个空间。

既然是表结构,那么2D和3D稀疏卷积其实没有什么本质区别。那就以2D为例,2D Dense Convolution输入的是[N,C,H,W][N,C,H,W]的矩阵;Sparse Convolution输入的则是2套列表:

  1. Data List:只存non-zero点的值,我们称之为活跃点Active Sites。

  2. Index List:保存non-zero点的坐标位置。

稀疏卷积流程

定义输入

我们先定义一个输入,这部分内容除了Focal Sparse Convolutional以外,均参考《How does sparse convolution work?》[1]

输入维度[N,3,5,5][N,3,5,5]。只有P1和P2位置non-zero,取值分别为[0.1,0.1,0.1][0.1,0.1,0.1][0.2,0.2,0.2][0.2,0.2,0.2]

卷积核为[3×3][3 \times3],stride=1,padding=0。深浅代表两个输出通道。

得到稀疏表示:

  • Data[[0.1,0.1,0.1],[0.2,0.2,0.2]][[0.1,0.1,0.1], [0.2,0.2,0.2]]

  • Index[[2,1],[3,2]][[2,1], [3,2]],注意这里是列/行索引。

构建Hash Table

Input Hash Table储存所有Active Sites,根据示例的P1和P2,那就是保存2个。

  • vinv_{in}[0,1][0,1]

  • keyinkey_{in}[[2,1],[3,2]][[2,1], [3,2]]

然后构建Ouput Hash Table,先操作P1:

convolution_dynamic_reveal.gif

P2对准过程同理。然后记录卷积后Active Sites元素的位置。P1和P2输出对应的索引为:

P1

P2

(0, 0)

(1, 0)

(1, 0)

(2, 0)

(2, 0)

(1, 1)

(0, 1)

(2, 1)

(1, 1)

(1, 2)

(2, 1)

(2, 2)

把重复的部分合并,得到输出的位置表,即Output Hash Table。

输入&输出的Hash Table构建完整过程如下图:

3D-Sparse-Conv-HashTable.webp

构建Rulebook

构建Rulebook的目的和im2col算法类似,将卷积从数学形式变为高效的可编程形式。

im2col 核心是空间块→列的维度重组,相当于用空间换时间,完全不关注原子操作或Kernel Elements的独立性,即使输入稀疏,im2col 仍会完整展开所有窗口,包含大量无效零值计算。

im2col不同的是,Rulebook的核心是收集原子操作,按Kernel Elements分组。

  1. 收集原子操作:只有输入中non-zero的点参与卷积乘法加法,跳过所有zero值,每个原子操作会记录:

    • 输入的non-zero的Index:vinv_{in}

    • 对应的Kernel Element(weight & offset)

    • 输出的Index: voutv_{out}

    • 乘法加法次数 countcount

  2. 按Kernel Element关联操作,将所有原子操作按Kernel Element分组(如 F0 组、F1 组…F8 组),每组内的操作共享同一 Kernel偏移与权重,可并行执行。

下图是构建Rulebook的示例,图中GetOffset(Pin,P)GetOffset(P_{in},P)中的PP并没有被直接体现出来,而且作者没详细解释Query Kernel的过程,所以让人困惑。GetOffset(Pin,P)GetOffset(P_{in},P)的意义应该是给定输入点PinP_{in}和输出点PoutP_{out}的位置,计算卷积核(Kernel)上对应的权重位置偏移量。

以P1点为例,GetOffset(Pin,P)GetOffset(P_{in},P)就是当卷积核滑动到以输出点PoutP_{out}对应的输入位置为中心的位置时,输入点 PinP_{in}相对于这个卷积核几何中心的偏移量。最直白版:输入点P1在当前卷积核内部的相对位置(不能更直白乐)。

所以公式应该可以重写为:

(i,j)=Pin(Pout+K//2)(i,j)=P_{in} - (P_{out}+K//2)

其中KK是卷积核的Kernel Size,这里就是3,除后是1。

我们快速回顾一下到这里都做了什么(For P1 Pixel):

  1. 获取了像素点P1在2D Tensor中的位置信息,以及索引(编号),以此构造Input Hash Tabel

  2. 获取了像素点P1在Conv操作后,也就是在输出特征图中的每个输出的位置信息,以及对应索引(编号),以此构造Output Hash Table

  3. 获取了像素点P1在Kernel中的相对位置(offset偏移量),这决定P1的Value和Conv Kernel中的哪个Weight执行运算,并得到Rulebook

  4. 建立了完整的Input Pixel -> Conv Kernel × Input -> Output Feature的完整索引、空间位置映射链路

所以,Rulebook本质是在做PinFeatureP_{in} - Feature的映射。

稠密卷积对每个输出点,必须遍历K×K个输入点,无论是否为zero;但是稀疏卷积对每个non-zero输入,只需要遍历它能产生结果的输出点。

3D-Sparse-Conv-RuleBook.webp

程序运行模式

[1]作者做的这个图非常牛逼(Rulebook多了几行,要是没错误就更牛逼了),红线(红框)和黑线(黑框)是卷积核两个channel的操作。存在多少个offset,就启动多少个Block,然后每个Block内的Thread数量和原子操作数量相同。

在实际写代码的时候,所有原子操作也是按照offset进行分组的。同一组内的操作使用同一个Conv Kernel Weight。整个过程没有任何矩阵乘法,没有任何滑动窗口,没有任何分支判断,就是单纯的乘法和加法操作,所以很快。

然后说明红色原子操作的过程,原子操作在这里可以定义为:

output[vo]+=input[vi]weight[i][j]output[v_o] += input[v_i] * weight[i][j]

其中:

  • output[vo]output[v_o] :初值为0,相同vov_ooutputoutput会在不同的线程的原子操作中累加,原子操作线程安全

  • input[vi]input[v_i] :输入,0.1

  • weight[i][j]weight[i][j]:对应offset位置的权重,F0

计算得到0.1×F00.1 \times F0

然后计算黑色原子操作过程,因为两者的vov_o相同所以会累加,其中

  • output[vo]output[v_o] :已经是0.1×F00.1 \times F0

  • input[vi]input[v_i] :0.2

  • weight[i][j]weight[i][j]:F4

计算得到0.2×F40.2 \times F4,最终结果为0.1×F0+0.2×F40.1 \times F0 + 0.2 \times F4

然后你们看吼,Output Sparse Tensor是2列,其中一列保存的是索引,另一列保存的是特征值即上面的计算结果。

Sparse-Conv-GPU-Schema.webp

稀疏卷积分类

Regular Sparse Convolution(RSC)

只要卷积核覆盖到任意一个输入活跃点 → 该位置就算输出。

输出点变多,稀疏性下降。

Submanifold Sparse Convolution(SSC)

只有卷积核中心落在输入活跃点上 → 才计算输出。

输出活跃点数量 = 输入活跃点数量,稀疏性完全保留。

Focal Sparse Convolutional(FSC)

FSC 的核心创新是使特征稀疏性可学习,通过位置级别的重要性预测动态决定卷积的输出位置和感受野大小,而不是像传统稀疏卷积那样使用固定的输出模式 。

FSC 一个额外的轻量级分支,为每个输入特征预测一个立方体重要性图,然后根据这个重要性图动态选择哪些输入特征需要进行邻域扩张,以及扩张到哪些位置 。

FSC 可以认为是 SSC 和 RSC 的动态组合:

  • 当所有位置都被预测为不重要时,FSC 退化为 SSC。

  • 当所有位置都被预测为重要且所有邻域位置都超过阈值时,FSC 退化为 RSC。

这几种稀疏卷积方案,并没有绝对优劣之分,应该按照场景需要选择不同的稀疏卷积方式。

参考文章

[1] How does sparse convolution work? | Towards Data Science

[2] 3D稀疏卷积理解与使用 | Ocean

[3] [2204.12463] Focal Sparse Convolutional Networks for 3D Object Detection

[4] [1711.10275] 3D Semantic Segmentation with Submanifold Sparse Convolutional Networks

[5] [1505.02890] Sparse 3D convolutional neural networ

[6] Im2Col算法介绍 - 知乎