torsh-quantization
Quantization toolkit for ToRSh, enabling efficient model deployment with reduced precision.
Overview
This crate provides tensor-level quantization support for deep learning workloads:
- Quantization Schemes: INT8, INT4, binary, ternary, mixed-precision, per-channel and group-wise, exposed as
QuantConfigpresets - Observers/Calibration: MinMax, moving-average, histogram and percentile observers for computing scale/zero-point
- Auto-Configuration: An ML-assisted
AutoConfiguratorthat recommends aQuantConfigfrom tensor statistics and an optimization objective - Quality Analysis: PSNR/SNR/MAE/cosine-similarity metrics, multi-config comparison, and auto-calibration helpers
- Backends: A
QuantBackendenum (FBGEMM, QNNPACK, Native, XNNPACK) used to tag configs for downstream kernel selection
The stable, default-feature surface operates on individual Tensors (quantize_with_config / dequantize / calculate_quantization_metrics). A model-level Post-Training/Quantization-Aware-Training pipeline (calibrating and converting a whole Module, ONNX/TensorRT export, etc.) also exists in the source tree, but it is gated behind the experimental feature and currently has no concrete implementor for the Module trait it depends on — see TODO.md for the tracked status. Treat the per-tensor API below as the primary supported surface.
Usage
Per-Tensor Quantization
This is the primary, stable API surface (used directly in examples/basic_quantization.rs):
use ;
use tensor_1d;
let data = vec!;
let tensor = tensor_1d?;
// Quantize using an INT8 preset configuration
let config = int8;
let = quantize_with_config?;
// Dequantize back to floating point
let dequantized = dequantize?;
// Calculate quality metrics (bits_before, bits_after)
let metrics = calculate_quantization_metrics?;
println!;
Model-level static/dynamic quantization (prepare_static/convert/quantize_dynamic over a whole model) is not currently available: the experimental qat/post_training modules define their own local placeholder Module trait (distinct from torsh_nn::Module) with no concrete implementor yet.
Quantization-Aware Training (QAT)
A QuantConfig::qat() preset (fake-quant enabled, moving-average observer) is available today:
use QuantConfig;
let qat_config = qat;
The full QAT training pipeline (prepare_qat wrapping an arbitrary model, training-loop integration, convert back to a quantized model) lives behind the experimental feature in src/qat.rs, but — like the post-training pipeline above — it operates on a local placeholder Module trait with no concrete implementor yet, so it is not usable end-to-end at the model level. Track status in TODO.md.
Custom Quantization Configuration
Configuration is built with QuantConfigBuilder (there is no per-layer-name/per-module-type QConfigDict, and no quantize_fx):
use QuantConfigBuilder;
use ;
let config = new
.scheme
.observer
.backend
.channel_axis
.build?;
Quantization Schemes
QuantConfig ships one preset constructor per scheme (see QScheme for the full enum: PerTensorAffine, PerChannelAffine, PerTensorSymmetric, PerChannelSymmetric, Int4PerTensor, Int4PerChannel, MixedPrecision, Binary, Ternary, GroupWise):
use QuantConfig;
let int8 = int8;
let int4 = int4;
let binary = binary;
let ternary = ternary;
let mixed = mixed_precision;
let per_channel = per_channel; // ch_axis
let group_wise = group_wise; // ch_axis, group_size
// Observers (a single `Observer` type, not per-scheme observer structs)
use Observer;
let hist_observer = histogram_with_bins;
let pct_observer = percentile_with_value;
Model Analysis
There is no compare_models/sensitivity_analysis that takes a live model plus calibration/test data. The real, tensor-level equivalents are compare_quantization_configs (used in examples/advanced_schemes.rs) and a heuristic, name-pattern-based quick_sensitivity_analysis:
use ;
let configs = vec!;
// Returns Vec<(config, metrics, time_ms)>
let comparison = compare_quantization_configs?;
for in &comparison
// Heuristic sensitivity analysis by layer name (no live model/data is used)
use quick_sensitivity_analysis;
let layer_names = vec!;
let sensitivity = quick_sensitivity_analysis?;
Export and Deployment
There are no free functions optimize_for_mobile/export_quantized_onnx/export_tensorrt. The experimental-gated export module instead provides ExportFormat (Onnx, TensorRT, Mobile, TFLite, CoreML), an ExportConfig, and a ModelExporter:
use ;
let config = ExportConfig ;
let exporter = new;
// exporter.export_model(&quantized_model, output_path)? — requires a `QuantizedModel`,
// which today must be constructed by hand since the automatic model-conversion
// pipeline is blocked on the same Module-trait gap noted above.
Debugging and Visualization
QuantizationDebugger (experimental feature) works per-tensor via debug_quantization, not over a model + data loader:
use QuantizationDebugger;
let mut debugger = new;
debugger.debug_quantization?;
println!;
There is no get_observer_dict/plot_error_heatmap; observer ranges are read directly from an Observer via calculate_qparams(dtype).
Advanced Features
There is no LearnableFakeQuantize, StochasticQuantize, or standalone GroupWiseQuantConfig (group-wise is a QuantConfig::group_wise(...) preset, shown above). A genuinely available advanced feature is constraint-based auto-configuration:
use ;
let constraints = new
.with_min_bits
.with_target_compression;
let configurator = new;
let config = configurator.recommend?; // takes ConfigConstraints by value
Quantization Backends
QuantBackend is a plain enum tag on the config (there is no platform-conditional compilation tied to it, and no CustomBackend):
use ;
let fbgemm_config = default.with_backend;
let qnnpack_config = default.with_backend;
// QuantBackend also has Native and Xnnpack variants
Scope
There are no layer-type-specific quantized kernels (no QuantizedLinear/QuantizedConv2d/QuantizedLSTM, etc.). Quantization operates generically on Tensors regardless of which layer produced them — per-tensor, per-channel, or group-wise, at whatever scheme/bit-width the chosen QuantConfig specifies. An operation-fusion pattern matcher (Conv+BN, Conv+ReLU, Linear+ReLU, ...) exists behind the experimental feature for graph-level optimization passes, independent of the per-tensor API above.
Best Practices
- Use representative calibration data
- Start with INT8 before trying lower bit widths
- Use per-channel quantization for Conv/Linear layers
- Keep sensitive layers in higher precision
- Profile on target hardware
License
Licensed under the Apache License, Version 2.0. See LICENSE for details.