Skip to main content

Neural network quantization toolkit for ONNX models

Project description

quantize-rs Python API

Python bindings for quantize-rs, a neural network quantization toolkit for ONNX models.

Scope

quantize-rs is designed and validated primarily for computer-vision (CNN-style) ONNX models -- ResNet, MobileNet, SqueezeNet, and similar architectures. Weight-only quantization (quantize()) is model-agnostic and works on any FP32 ONNX file. Activation calibration (quantize_with_calibration()) runs inference through tract, whose op coverage is centered on CNNs; transformer / LLM / RNN models may fail to load through tract or hit unsupported ops during calibration.

Installation

pip install quantization-rs

Wheels are built with PyO3 abi3-py39, so a single wheel per OS/arch covers Python 3.9 through 3.13+. No interpreter-specific wheels needed.

Build from source (requires Rust toolchain and maturin):

pip install maturin
maturin develop --release --features python

API reference

quantize(input_path, output_path, bits=8, per_channel=False, excluded_layers=None, min_elements=0, layer_bits=None, native_int4=False, symmetric=False)

Weight-based quantization. Loads the model, quantizes all weight tensors, and saves the result in ONNX QDQ format.

Parameters:

Name Type Default Description
input_path str required Path to input ONNX model
output_path str required Path to save quantized model
bits int 8 Bit width: 4 or 8
per_channel bool False Use per-channel quantization (separate scale/zp per output channel, axis 0 only — see Limitations)
excluded_layers list[str] or None None Initializer names to leave in FP32
min_elements int 0 Skip tensors with fewer than N elements (e.g., biases)
layer_bits dict[str, int] or None None Per-layer bit-width overrides, e.g. {"conv1.weight": 4}
native_int4 bool False Store INT4 weights as ONNX DataType.Int4 (opset 21). True 8x on-disk compression but requires opset-21 runtime. No effect on INT8-only models.
symmetric bool False Symmetric quantization (zero_point == 0). Required by most ORT / TensorRT INT8 matmul kernels for per-channel weights.

Example:

import quantize_rs

# Plain INT8
quantize_rs.quantize("model.onnx", "model_int8.onnx", bits=8)

# INT4 with native opset-21 storage (8x on-disk)
quantize_rs.quantize("model.onnx", "model_int4.onnx", bits=4, native_int4=True)

# Symmetric per-channel INT8 for ORT INT8 matmul kernels
quantize_rs.quantize(
    "model.onnx",
    "model_int8_sym.onnx",
    bits=8,
    per_channel=True,
    symmetric=True,
)

# Mixed precision: some layers INT4, rest INT8
quantize_rs.quantize(
    "model.onnx",
    "out.onnx",
    bits=8,
    layer_bits={"fc.weight": 4},
    excluded_layers=["embedding.weight"],
    min_elements=1024,  # skip small tensors (biases) and keep them FP32
)

quantize_with_calibration(input_path, output_path, calibration_data=None, bits=8, per_channel=False, method="minmax", num_samples=100, sample_shape=None, native_int4=False, symmetric=False)

Activation-based calibration quantization. Runs inference on calibration samples to determine optimal quantization ranges per layer, then quantizes using those ranges. The full filter pipeline (excluded_layers, min_elements, layer_bits) is honored; pass these via quantize() directly if you need to skip layers explicitly.

Parameters:

Name Type Default Description
input_path str required Path to input ONNX model
output_path str required Path to save quantized model
calibration_data str or None None Path to .npy file (shape [N, ...]), or None for random samples
bits int 8 Bit width: 4 or 8
per_channel bool False Per-channel quantization
method str "minmax" Calibration method (see below)
num_samples int 100 Number of random samples when calibration_data is None
sample_shape list[int] or None None Shape of random samples; auto-detected from model if None. Default fallback is [3, 224, 224] (CHW image) -- override for non-image inputs.
native_int4 bool False Store INT4 weights as ONNX DataType.Int4 (opset 21)
symmetric bool False Symmetric quantization (zero_point == 0)

Calibration methods:

Method Description
"minmax" Uses observed min/max from activations
"percentile" Clips at the 99.9th percentile to reduce outlier sensitivity
"percentile:NN" Clips at the NNth percentile (e.g. "percentile:95"); must be in [0, 100]
"entropy" Selects range minimizing KL divergence between original and quantized distributions
"mse" Selects range minimizing mean squared error

Example:

import quantize_rs

# With real calibration data
quantize_rs.quantize_with_calibration(
    "resnet18.onnx",
    "resnet18_int8.onnx",
    calibration_data="calibration_samples.npy",
    method="minmax"
)

# With random samples (auto-detects input shape from model)
quantize_rs.quantize_with_calibration(
    "resnet18.onnx",
    "resnet18_int8.onnx",
    num_samples=100,
    sample_shape=[3, 224, 224],
    method="percentile"
)

model_info(input_path)

Returns metadata about an ONNX model.

Parameters:

Name Type Default Description
input_path str required Path to ONNX model

Returns: ModelInfo object with the following fields:

Field Type Description
name str Graph name
version int model_version field (often 0)
opset_version int Default-domain opset version (governs operator compatibility)
num_nodes int Number of computation nodes
inputs list[str] Input tensor names
outputs list[str] Output tensor names

Example:

info = quantize_rs.model_info("model.onnx")
print(f"Name: {info.name}")
print(f"Nodes: {info.num_nodes}")
print(f"Inputs: {info.inputs}")
print(f"Outputs: {info.outputs}")

Preparing calibration data

For best results, use 50-200 representative samples from your validation or training set:

import numpy as np

# Collect preprocessed samples
samples = []
for img in validation_dataset[:100]:
    preprocessed = preprocess(img)  # your preprocessing pipeline
    samples.append(preprocessed)

# Save as .npy (shape: [num_samples, channels, height, width])
calibration_data = np.stack(samples)
np.save("calibration_samples.npy", calibration_data)

# Use during quantization
quantize_rs.quantize_with_calibration(
    "model.onnx",
    "model_int8.onnx",
    calibration_data="calibration_samples.npy",
    method="minmax"
)

If you do not have calibration data, the function generates random samples. This is adequate for testing but will produce less accurate quantization than real data.

ONNX Runtime integration

Quantized models use the standard DequantizeLinear operator and load directly in ONNX Runtime:

import onnxruntime as ort
import numpy as np

session = ort.InferenceSession("model_int8.onnx")
input_name = session.get_inputs()[0].name
output = session.run(None, {input_name: your_input})

Logging

quantize-rs routes its warnings (e.g. unpreserved ONNX sections, opset-migration caveats) through Rust's log crate, bridged into Python's standard logging under loggers named quantize_rs.*. Configure or silence them like any other logger:

import logging
logging.getLogger("quantize_rs").setLevel(logging.ERROR)  # silence quantize-rs warnings

Set your logging configuration before the first quantize call — the bridge caches logger levels, so changes made afterward may not take effect.

Limitations

  • ONNX format only. Export PyTorch/TensorFlow models to ONNX before quantizing.
  • Validated primarily on CNN-style vision models. Activation calibration uses tract for inference; transformer / LLM / RNN architectures may report unsupported ops or shape mismatches in quantize_with_calibration(). The plain quantize() (weight-only) function does not use tract and works on any FP32 ONNX model.
  • Requires ONNX opset >= 10 for per-tensor quantization, >= 13 for per-channel (automatically upgraded if needed).
  • INT4 values are stored as INT8 bytes by default. Pass native_int4=True to write them as ONNX DataType.Int4 (opset 21) for true 8x compression -- requires an ONNX runtime with opset-21 support.
  • Per-channel always uses axis 0 (the output-channel dim, as expected by Conv and MatMul weights). Transformer-style linear layers that expect axis=1 per-channel quantization are not yet supported.
  • Single-input models are assumed by random-sample auto shape detection; for multi-input graphs, pass sample_shape explicitly or supply real calibration_data.
  • External-data models (weights in a sidecar .onnx.data file, common above ~2 GB) are not supported — quantize() raises with instructions to re-save with weights embedded.
  • A few ONNX sections are not preserved on save (functions/local custom ops, sparse_initializer, training_info); a warning is printed when a loaded model carried them.

Type stubs (quantize_rs.pyi) ship with the wheel, so editors, mypy, and pyright get completion and type checking for the API above.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

quantization_rs-0.9.0.tar.gz (187.0 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

quantization_rs-0.9.0-cp39-abi3-win_amd64.whl (6.5 MB view details)

Uploaded CPython 3.9+Windows x86-64

quantization_rs-0.9.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (6.1 MB view details)

Uploaded CPython 3.9+manylinux: glibc 2.17+ x86-64

quantization_rs-0.9.0-cp39-abi3-macosx_11_0_arm64.whl (5.1 MB view details)

Uploaded CPython 3.9+macOS 11.0+ ARM64

quantization_rs-0.9.0-cp39-abi3-macosx_10_12_x86_64.whl (5.9 MB view details)

Uploaded CPython 3.9+macOS 10.12+ x86-64

File details

Details for the file quantization_rs-0.9.0.tar.gz.

File metadata

  • Download URL: quantization_rs-0.9.0.tar.gz
  • Upload date:
  • Size: 187.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for quantization_rs-0.9.0.tar.gz
Algorithm Hash digest
SHA256 70f0fae8413281550f24c7ea3aed6ab2176992047ea539db5af225febbf2adeb
MD5 5f9c47c95f641009e39e88a0debe2568
BLAKE2b-256 ca1c734281f612dc0f593dc47ef376384f05dd3d1060819084cab85621bf1058

See more details on using hashes here.

File details

Details for the file quantization_rs-0.9.0-cp39-abi3-win_amd64.whl.

File metadata

File hashes

Hashes for quantization_rs-0.9.0-cp39-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 bf91401f08065ae604a8576bf959edcb23e5cdb6365c67288dfc3a25aea8ae27
MD5 8a6aa2bc7b978cdabee1873333114466
BLAKE2b-256 783cf8a8d471f8ca2022fcbd98f5bf75477081ea6071b1a30936f15e3ca3e838

See more details on using hashes here.

File details

Details for the file quantization_rs-0.9.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for quantization_rs-0.9.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 49c7c3c4232ca7e08c2dd40f828886581b950e85dddc4080e8c4b5d25f8ff24e
MD5 cba7dcaf4794b0eab4b57a4e92db4851
BLAKE2b-256 0690dae49dee0873a231567dc843102bf6e5e4a41197fc800a9ea057e9160c38

See more details on using hashes here.

File details

Details for the file quantization_rs-0.9.0-cp39-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for quantization_rs-0.9.0-cp39-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 0f9819bdfcb4c0ae8364c9c9b1b55eb5e7aa2fd6bab2e6c7c81c0a1113f45316
MD5 561deb3fe90166c1e2e26c32633a160a
BLAKE2b-256 59f009badda78937e4c066a682f4ad9859d5c91c970a0502a38c672324acc951

See more details on using hashes here.

File details

Details for the file quantization_rs-0.9.0-cp39-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for quantization_rs-0.9.0-cp39-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 24b0f5314de2d1dbc5f2d3994e69b2e6a5326bc427368e55581635b71e9d729e
MD5 75a8b456e7d8171413d31654420cd8b3
BLAKE2b-256 35fc2fa7f3819ee1547149f576a17c15052f9efcd2e013625c2d5678dc7f9a49

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page