当前位置:   article > 正文

sparse conv稀疏卷积

稀疏卷积

 很好的教程,感谢作者的分享

通俗易懂的解释Sparse Convolution过程 - 知乎

一、稀疏卷积是什么,为什么提出稀疏卷积?它有什么好处?

稀疏卷积和普通卷积的区别

spconv和普通卷积没有区别,最重要的区别在于卷积的数据的存储方式和计算方法,这种计算方法可以增加计算稀疏点云的效率,其他的都是完全相同的(但SubMConv3d还是稍微有点区别的),此外spconv的3D稀疏卷积和普通卷积使用类似,唯一多了一个indice_key,这是为了在indice相同的情况下重复利用计算好的'rulebook'和'hash表',减少计算。

三维图像太稀疏了,比如我的教室的点云其中相当一部分都是空气,真正有点云的部分连一半都不到,不像二维图像,二维图像每个位置都有像素点。

对于三维图像我们想加快卷积速度,不去计算空的部分

二、具体计算原理(只是使用也可以不了解)

还是这个教程,对应二、三章节

通俗易懂的解释Sparse Convolution过程 - 知乎

 这个位置写错了,底下应该是A1A2才对,不同底色代表了两张特征图。

三、如何使用

1.首先要初始化一个SparseConvTensor

SparseConvTensor的定义

  1. class SparseConvTensor(object):
  2. def __init__(self, features, indices, spatial_shape, batch_size, grid=None):
  3. """
  4. Args:
  5. grid: pre-allocated grid tensor. should be used when the volume of spatial shape
  6. is very large.
  7. """
  8. self.features = features # 储存密集的feature
  9. self.indices = indices # 储存每个feature对应的voxel坐标系下的坐标
  10. if self.indices.dtype != torch.int32:
  11. self.indices.int()
  12. self.spatial_shape = spatial_shape #存储voxel的最大边界
  13. self.batch_size = batch_size # 储存batch size
  14. self.indice_dict = {} # 储存坐标之间的对应关系
  15. self.grid = grid
  16. ...

 示例

sinput = spconv.SparseConvTensor(feat, coord.int(), spatial_shape, args.batch_size)

参数

features[n,c]每个点的特征

indices是[n,4],第一列存储的是batch编号,后面才是该点对应的voxel的[x,y,z]坐标,因为batch号也算是坐标的一种,

spatial_shape [3],最大的voxel范围,也就是最右上角voxel的右上角的棱角坐标

features, indices, spatial_shape, batch_size传入之后都是可以在外部进行调用的,比如有SparseConvTensor对象sinput,使用sinput.features    sinput.indices等即可取出数据。

方法

def replace_feature(self, feature: torch.Tensor):->tensor

实际上就是将一个tensor读入,替换原来的feature,比如

对于F.relu,我们要传入features,之前要这样写

x.features = F.relu(x.features)

但这样写会导致torch.fx出问题,replace_feature就是为了解决这个问题而出现的

x = x.replace_feature(F.relu(x.features))

注意该函数并非在x上进行更改了,而是创建了一个全新的SparseConvTensor,也就是说要用x去接收返回值


 

2.进行卷积

共有两种卷积(深入了解见教程)

SubMConv3d与SparseConv3d(我们主要使用SubMConv3d)

二者的区别和联系:
SubMConv3d输入与输出feature map上不为空的位置相同,保持了稀疏性(sparity=不为空的位置/所有位置和),也就保持了计算量。而SparseConv3d会增加稀疏性,从而也就增加了计算量。但是如果只用SubMConv3d,卷积核的感受野会限制在一定范围内,所以要结合stride=2的SparseConv3d一起使用,在尽量保持稀疏性的同时增大感受野

解释:也就是SubMConv3d+padding=1可以让每一轮的activite-site都和上一轮一样,也就是保持了稀疏性,但是这种方案会限制感受野

SparseConv3d

一种是 regular output definition,就像普通的卷积一样,只要kernel 覆盖一个 active input site,就可以计算出output site。也就是输出的有效点的数量是比输入多的,有笑点数量会越变越多。

SubMConv3d

另一个称为submanifold output definition。只有当kernel的中心覆盖一个 active input site时,卷积输出才会被计算,也就是输入和输出的有效点的数量是一致的(有效点指的是该坐标下有数据,无效点的话就是该坐标下没有数据,没有点云),通常可以搭配kernel size为3,padding=1来使用可以保证输入和输出的有效点完全一致,空间大小spatial_shape也不会改变

  1. class SubMConv3d(SparseConvolution):
  2. def __init__(self,
  3. in_channels,
  4. out_channels,
  5. kernel_size,
  6. stride=1,
  7. padding=0,
  8. dilation=1,
  9. groups=1,
  10. bias=True,
  11. indice_key=None,
  12. algo: Optional[ConvAlgo] = None,
  13. fp32_accum: Optional[bool] = None,
  14. name=None):

这就是普通conv的参数了

in_channels,out_channels输入输出特征
kernel_size为卷积核大小
stride步长
indice_key

这是为了在indice相同的情况下重复利用计算好的'rulebook'和'hash表',

减少计算

只要保证第一个SubMConv3d输入输出的激活点和第二个SubMConv3d输入输出的激活点是完全相同的(且kenel size也要相同,但一般都是相同的,可忽略),就可以放心使用indice_key

注意:

这里的卷积操作实际上是按voxel为基本单位的,也就是voxel对应了二维的像素点,stride这种参数也是以voxel为度量

实例

1.首先创建该模块,和bn,linear啥的一样定义

  1. self.input_conv = spconv.SparseSequential(
  2. spconv.SubMConv3d(input_c, m,
  3. kernel_size=3, padding=1, bias=False, indice_key='subm1'
  4. ))

2.将SparseConvTensor给传入

output = self.input_conv(input)

这里output是新的SparseConvTensor,由于用的是SubMConv3d所以除了features变为了[n,out_features]其他的都不变

感受野比较

都假设padding为1,kernel为3
首先是SparseConv3d,由前两步可知在第三步就能出现123的感受野

在这里插入图片描述

其次是SubMConv3d,步长为1

在这里插入图片描述
可以看到无论多少轮都无法将感受野扩大到A3,可见SubMConv3d的感受野是十分有限的。

SubMConv3d在sienet中用的都是1步长,spconv有用两步长的,这个应该就是和普通的卷积没有区别的。

SparseSequential

SparseSequential — mmcv 2.0.0 documentation

参考nn.Sequential

pytorch接口_wa1ttinG的博客-CSDN博客

是类似于nn.Sequential,都是将多个模块连接起来,将上一个模块的输出作为输入传入下一个模块。且SparseSequential是可以传入torch.nn中的模块的,内部做了封装,包括取出feature,feature赋值replace_feature等等,直接将SparseConvTensor给传入torch.nn的模块是不行的

  1. >>> # using Sequential:
  2. >>> from mmcv.ops import SparseSequential
  3. >>> model = SparseSequential(
  4. SparseConv2d(1,20,5),
  5. nn.ReLU(),
  6. SparseConv2d(20,64,5),
  7. nn.ReLU()
  8. )
  9. >>> # using Sequential with OrderedDict
  10. >>> model = SparseSequential(OrderedDict([
  11. ('conv1', SparseConv2d(1,20,5)),
  12. ('relu1', nn.ReLU()),
  13. ('conv2', SparseConv2d(20,64,5)),
  14. ('relu2', nn.ReLU())
  15. ]))
  16. >>> # using Sequential with kwargs(python 3.6+)
  17. >>> model = SparseSequential(
  18. conv1=SparseConv2d(1,20,5),
  19. relu1=nn.ReLU(),
  20. conv2=SparseConv2d(20,64,5),
  21. relu2=nn.ReLU()
  22. )

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/490485
推荐阅读
相关标签
  

闽ICP备14008679号