SpConv Documentation

repository·master·Indexed 25 days ago

https://github.com/traveller59/spconv

A highly-optimized spatially sparse convolution library for deep learning featuring tensor core support and high-performance kernels. It provides a PyTorch interface and a C++ library (libspconv), with support for prebuilt binaries across Linux and Windows. The library includes advanced features such as TF32 kernels for faster FP32 training, Int8 quantization support via PTQ and QAT using torch.fx, and benchmarking tools via the spconv.benchmark module.

Tokens
9.2K
Snippets
17
Records
37
Agent score
81%

What's inside SpConv

  1. Perform Upsampling with Inverse Convolution

    master

    In tasks like semantic segmentation, you often need to upsample features back to the original resolution.

    Important Distinction: spconv.SparseInverseConv3d is not the same as spconv.SparseConvTranspose3d.

    • spconv.SparseInverseConv3d: Designed to be the mathematical 'inverse' of a sparse convolution. The output contains the same indices as the input of the corresponding SparseConv3d. To use it, you must provide the same indice_key used during the downsampling step and the same kernel_size to create the weights.
    • spconv.SparseConvTranspose3d: Standard upsampling (equivalent to nn.ConvTranspose3d). This is very slow and cannot recover the original point cloud structure directly. It should primarily be used in generative models.
    class ExampleNet(nn.Module):
        def __init__(self, shape):
            super().__init__()
            self.net = spconv.SparseSequential(
                spconv.SparseConv3d(32, 64, 3, 2, indice_key="cp0"),
                spconv.SparseInverseConv3d(64, 32, 3, indice_key="cp0"), # Uses saved indices from cp0
            )
            self.shape = shape
    
        def forward(self, features, coors, batch_size):
            coors = coors.int()
            x = spconv.SparseConvTensor(features, coors, self.shape, batch_size)
            return self.net(x)
  2. Requirements and limitations for Spconv Int8 support

    master

    Spconv's Int8 support has specific hardware and architectural requirements:

    • Backend: Only supports CUDA backend.
    • PyTorch Version: Requires torch >= 1.13.
    • Channel Constraints: Input and output channels must satisfy input_channel % 32 == 0 and output_channel % 32 == 0.
    • Performance: Int8 kernels are faster than fp16 when shapes meet these criteria:
      • C == 32 && K == 64
      • C == 64 && K == 32
      • C >= 64 && K >= 64
    • Unsupported Operations: Pooling operations are currently not supported in Int8.
  3. Understand spconv 2.x PyTorch dependency requirements

    master

    In spconv 2.x, 'no dependency on PyTorch' means that the shared library does not have a direct dependency on the PyTorch shared library when inspected via ldd. This design choice was made to ensure compatibility with manylinux requirements for PyPI distribution.

    Important Caveats:

    • This does not mean spconv 2.x is compatible with any version of PyTorch.
    • Spconv 2.x relies on specific PyTorch features to interface with tensors and CUDA streams without needing the PyTorch C++ library in its own C++ code. You must use a PyTorch version that provides these features.
  4. Enable residual fusion for Spconv Int8 quantization

    master

    Spconv supports fusing subm residual blocks (SubMConv + BatchNorm + Add + ReLU) into a single SubMConvAddReLU node.

    To enable fusion: Your residual code must avoid using spconv specific operations like .replace_feature() or accessing .features directly on the tensor, as these create standalone nodes in the torch.fx graph that prevent fusion.

    Correct Pattern (Fused): Use spconv.SparseReLU and spconv.SparseIdentity to ensure the graph remains compatible with fusion.

    Incorrect Pattern (Non-fused): Using out.replace_feature(...) or out.features will prevent the residual block from being fused.

    class SparseBasicBlock(spconv.SparseModule):
        """residual block that supported by spconv quantization."""
        expansion = 1
        def __init__(self, in_planes, out_planes, stride=1, downsample=None):
            spconv.SparseModule.__init__(self)
            conv1 = spconv.SubMConv2d(in_planes, out_planes, 3, stride, 1, bias=False)
            conv2 = spconv.SubMConv2d(out_planes, out_planes, 3, stride, 1, bias=False)
            norm1 = nn.BatchNorm1d(out_planes, momentum=0.1)
            norm2 = nn.BatchNorm1d(out_planes, momentum=0.1)
            self.conv1_bn_relu = spconv.SparseSequential(conv=conv1, bn=norm1, relu=nn.ReLU(inplace=True))
            self.conv2_bn = spconv.SparseSequential(conv=conv2, bn=norm2)
            self.relu = spconv.SparseReLU(inplace=True)
            self.downsample = downsample
            self.iden_for_fx_match = spconv.SparseIdentity()
    
        def forward(self, x: spconv.SparseConvTensor):
            identity = x
            out = self.conv1_bn_relu(x)
            out = self.conv2_bn(out)
            if self.downsample is not None:
                identity = self.downsample(x)
            out = self.relu(out + identity)
            return out
  5. Convert PyTorch models to TensorRT using torch.fx

    master

    Once your PTQ (Post Training Quantization) or QAT (Quantization Aware Training) model is ready, use a torch.fx.Interpreter to transform the traced PyTorch model into a TensorRT-compatible format.

    For a concrete implementation pattern, refer to the mnist_net_transform.py example in the repository.

  6. Perform Quantization Aware Training (QAT)

    master

    QAT inserts observers and fake quantization nodes into the model during training.

    Workflow:

    1. Set is_qat = True.
    2. Use spconvq.get_default_spconv_qconfig_mapping(is_qat) to get the appropriate mapping.
    3. Use qfx.prepare_qat_fx to create the prepared_model.
    4. Run your training loop using the prepared_model.
    5. Once training is complete, the model can be converted for Int8 inference using the same PTQ workflow described in the PTQ guide.
    import spconv.pytorch.quantization as spconvq
    import torch.ao.quantization.quantize_fx as qfx
    
    model = ...
    is_qat = True
    qconfig_mapping = spconvq.get_default_spconv_qconfig_mapping(is_qat)
    prepare_cfg = spconvq.get_spconv_prepare_custom_config()
    backend_cfg = spconvq.get_spconv_backend_config()
    
    # prepare model with fake quantize nodes
    prepared_model = qfx.prepare_qat_fx(model, qconfig_mapping, (), backend_config=backend_cfg, prepare_custom_config=prepare_cfg)
    
    # run training
    train(prepared_model)
  7. Install SpConv via prebuilt binaries

    master

    SpConv provides prebuilt binaries for Linux and Windows 10/11. Choose the package that matches your CUDA version.

    Requirements:

    • Python >= 3.7
    • CUDA Toolkit installed (for building from source) or a compatible NVIDIA driver (for prebuilt binaries).
    • For Linux users: pip >= 20.3 is required.
    • For CUDA 11.x, a driver version >= 450.82 is required. For CUDA 11.8, driver >= 520 is required.
    • Note on CUDA Compatibility: In CUDA >= 11.0, it is safe to have a minor version mismatch between your system CUDA and your Conda/PyTorch CUDA (e.g., using spconv-cu114 with PyTorch's CUDA 11.1).

    Important Update Procedure: When updating SpConv, you MUST UNINSTALL all existing spconv, cumm, and spconv-cuxxx/cumm-cuxxx packages first to avoid conflicts.

  8. Record maximum voxel counts for TensorRT plugins

    master

    TensorRT plugins require knowing the maximum number of voxels for each layer. To obtain this:

    1. Enable the record_voxel_count argument in your SparseConvolution layers.
    2. Run inference using your entire training dataset.
    3. The maximum number of voxels will be recorded in a registered buffer during this process, which you can then use to configure your TensorRT plugin.
  9. Perform Post Training Quantization (PTQ)

    master

    To perform PTQ, trace the model via torch.fx, insert observers, run inference with test data to calculate scales, and then convert the model.

    Workflow:

    1. Define qconfig_mapping using spconvq.get_default_spconv_qconfig_mapping(is_qat=False).
    2. Configure prepare_cfg and backend_cfg to handle non-traceable modules or custom attributes.
    3. Use qfx.prepare_fx to create a prepared_model containing observers.
    4. Run inference on a data loader to populate observer statistics.
    5. Use spconvq.prepare_spconv_torch_inference(with_linear=True) (must be called before convert_fx).
    6. Convert the model using qfx.convert_fx.
    7. Apply spconvq.transform_qdq and spconvq.remove_conv_add_dq to clean up the graph for Int8 inference.

    Debugging Tip: Set the environment variable SPCONV_INT8_DEBUG=1 to reduce compilation time by removing most candidate Int8 kernels.

    Troubleshooting: If an operation does not support the CUDA backend, disable quantization for that specific layer/module in the qconfig_mapping.

    import spconv.pytorch.quantization as spconvq
    import torch.ao.quantization.quantize_fx as qfx
    
    model = ...
    is_qat = False
    qconfig_mapping = spconvq.get_default_spconv_qconfig_mapping(is_qat)
    # disable quantization for some layers here:
    qconfig_mapping.set_module_name_regex("foo.*bar.*", None)
    # disable quantization by type here:
    qconfig_mapping.set_object_type(ModuleClass, None)
    
    prepare_cfg = spconvq.get_spconv_prepare_custom_config()
    # preserve static attrs for your module here:
    prepare_cfg.preserved_attributes = [...]
    # add nontraceable modules here:
    prepare_cfg.non_traceable_module_classes.extend([...])
    
    backend_cfg = spconvq.get_spconv_backend_config()
    # add custom qconfig for your non-traceable operators:
    backend_cfg.set_backend_pattern_config(BackendPatternConfig(some_op_or_module_class).set_observation_type(
        ObservationType.OUTPUT_USE_DIFFERENT_OBSERVER_AS_INPUT).set_dtype_configs(
            [non_weighted_op_qint8_dtype_config]))
    
    # prepare model
    prepared_model = qfx.prepare_fx(model, qconfig_mapping, (), backend_config=backend_cfg, prepare_custom_config=prepare_cfg)
    
    # run inference to calculate scales
    for x in loader:
        prepared_model(x)
    
    # convert to int8
    spconvq.prepare_spconv_torch_inference(with_linear=True)
    converted_model = qfx.convert_fx(prepared_model, qconfig_mapping=qconfig_mapping, backend_config=backend_cfg)
    converted_model = spconvq.transform_qdq(converted_model)
    converted_model = spconvq.remove_conv_add_dq(converted_model)
  10. Install dependencies for Pure C++ build

    master

    To build the libspconv C++ library, you must first install spconv, cumm, and cumm cmake. You can install cumm using CMake or by including it as a subdirectory in your project.

    CMake Installation: Clone the cumm project and run:

    mkdir -p build && cd build && cmake .. && make && make install

    Subdirectory Method: Clone the cumm project and copy it directly into your parent project directory.

    mkdir -p build && cd build && cmake .. && make && make install