Skip to main content
The 2.0 SDK is a thin client. Quantizer is the high-level facade most users need; PlatformClient and Job are the lower-level primitives it is built on.
quantize() and train_qat() run on the R3AL platform and consume plan quota. benchmark() and load() run locally, on your own hardware.

Authenticate

Or set R3AL_API_KEY in the environment. To override the platform host:
Resolution order for the key: an explicit argument, then login(...)/configure(...), then R3AL_API_KEY, then a key stored by r3al login at ~/.config/r3al/credentials.json. The environment deliberately beats the stored file, so exporting a key for one process still wins.

Quantizer

The facade for the common flow: upload, run on the platform, download.

Quantizer(config)

QuantConfig | None
default:"QuantConfig()"
Configuration shared by every call on this instance. Omit it for the defaults: mode="ptq", method="ptq_static", bits=8, calibration_method="minmax". Invalid combinations raise UnsupportedConfigError at construction time.
Methods: quantize(), train_qat(), benchmark(), load().

QuantConfig

The Pydantic config shared by PTQ and QAT. Validate one without a network call using validate_config_local.

Shared

str
default:"vision"
The only supported value. Anything else raises UnsupportedConfigError.
str
default:"ptq"
ptq (post-training, no training loop) or qat (quantization-aware training). mode="ptq" is required by quantize(); mode="qat" is required by train_qat().
str
default:"ptq_static"
PTQ method, ignored when mode="qat": ptq_static (best accuracy, needs calibration_data) or ptq_dynamic (no calibration, but only quantizes MatMul/Gemm layers — a poor fit for Conv-heavy backbones). See Methods.
int
default:"8"
Bit width for PTQ: 8 (INT8) or 4 (INT4). Any other value raises UnsupportedBitsError. INT4 halves the model again but has far less headroom for calibration error — pair it with calibration_method="percentile" or "entropy" and a larger sample set, and check accuracy with benchmark() before shipping. In mode="qat" this field is ignored and overwritten with qat_wbit, which accepts 3-8.
str
default:"auto"
Execution device for the engine: auto, cuda, or cpu.
str
default:"auto"
Device string hint, e.g. cuda:0.
str
default:"onnx"
Deliverable format. ONNX is the only supported value.

PTQ calibration (method="ptq_static")

str
default:"minmax"
How the activation clipping threshold is chosen: minmax (observed absolute max, nothing clipped), percentile (clip at calibration_percentile), or entropy (KL-divergence search over a histogram, the TensorRT scheme). Aliases: max/min_max/min-maxminmax, kl/kl_divergenceentropy. Setting anything but minmax with method="ptq_dynamic" raises UnsupportedConfigError — dynamic quantization never runs a calibration pass. See Calibration.
float
default:"99.999"
Clipping percentile for calibration_method="percentile", in percent and in the range (50, 100]. Passing a fraction (0.9999) is rejected with a message telling you the percent form.
int
default:"2048"
Histogram bins over the observed absolute-value range, used by percentile and entropy. Range [128, 8192]. More bins means a finer threshold search and a slower calibration pass. The entropy search then merges candidates down to 2^(bits-1) magnitude levels — 128 at INT8, 8 at INT4 — so bits changes which threshold wins, not just the output grid.
bool
default:"false"
Force the calibrated activation range symmetric around 0. Off by default so a one-sided range (e.g. post-ReLU [0, alpha]) keeps the whole grid — 256 levels at INT8, 16 at INT4 — instead of spending half of it below zero. Applies to every calibration method.
percentile and entropy fit a histogram per activation tensor. quantize() emits a Python warning when either is calibrating on fewer than 256 samples (counting the max_calib_samples cap); with fewer, minmax is the safer choice.

QAT (mode="qat")

int
default:"8"
Weight bit width, 3-8. Also becomes the config’s bits.
int
default:"8"
Activation bit width, 3-8.
int | None
default:"8"
Bit width for the stem convolution. None leaves the first layer in float32, which is the usual mitigation when low-bit QAT loses accuracy at the input.
bool
default:"true"
Whether activations are fake-quantized during training. False trains weight-only QAT.
int
default:"1"
Default training epochs (minimum 1). train_qat(epochs=...) overrides it. 1 is a smoke test, not a converged model.
int
default:"16"
Batches used to initialize the activation clip thresholds (act_alpha) before training. Minimum 1. Also bounds the training set: the pipeline trains on at most epochs × qat_calib_batches batches.
str
default:"percentile"
How each activation clip threshold is initialized: percentile (quantile qat_calib_q of observed absolute values), max (observed absolute max), or entropy (KL-divergence search over a qat_calib_num_bins histogram). Aliases: minmax/min_max/min-maxmax, kl/kl_divergenceentropy.
float
default:"0.9999"
Quantile for qat_calib_method="percentile", as a fraction in (0, 1] — 0.9999 is the 99.99th percentile. Note this is the opposite convention to the PTQ field calibration_percentile, which is in percent.
int
default:"2048"
Histogram bins for qat_calib_method="entropy", range [128, 8192]. Ignored by the other QAT calibration methods, and only sent with the job when the method is entropy.

Derived properties

bool
True for ptq_static and for every QAT config.
bool
The inverse of requires_calibration.
bool
True for both PTQ methods (the engine always quantizes ONNX).

quantize() (runs on the platform)

Uploads the model (exporting it to ONNX locally first when needed), runs the job on R3AL infrastructure, waits with live progress, and downloads the deliverable bundle into output_dir.

Signature

str | Path | nn.Module
required
An .onnx path, a checkpoint in any format the export registry understands (.pt/.pth, .h5/.keras, .pb, .tflite, Paddle, SavedModel directory), an already-loaded torch.nn.Module, or an existing r2:// reference. Everything non-ONNX is exported to ONNX locally before upload, so it needs the export options below.
Iterable[str | Path] | None
default:"None"
Local image file paths (or r2:// refs), uploaded alongside the model. Required for method="ptq_static"; omitting it raises CalibrationRequiredError locally, before anything is uploaded or a run is spent. Ignored by ptq_dynamic.
str | Path
default:"./quantized_output"
Local directory the deliverable bundle is downloaded and unpacked into.
bool
default:"true"
Print live job progress (status, percentage, elapsed) on stderr while waiting.

Export options (non-ONNX input only)

Consumed by the local export step; ignored when model is already an .onnx file.
str | None
default:"None"
Force an export adapter instead of auto-detecting by suffix: onnx, ultralytics (alias yolo), pytorch (alias torch), tensorflow (aliases tf, keras), paddle (alias paddlepaddle), tflite. Required to disambiguate .pt/.pth, which both YOLO and plain PyTorch use. list_export_sources() returns the live list.
str | None
default:"None"
Base filename for the exported .onnx. Required when model is an in-memory nn.Module (there is no source path to derive a name from); otherwise defaults to the checkpoint’s stem.
tuple[int, ...] | list[int] | None
default:"None"
Example input shape used to trace the model, e.g. [1, 3, 224, 224]. Required by the PyTorch export path.
int
default:"17"
ONNX opset for the export. The engine upgrades anything below 13 before quantizing, since per-channel QDQ needs opset ≥ 13.
int | tuple[int, int]
default:"640"
Image size for exporters that take one (Ultralytics YOLO).
bool
default:"true"
Run the exporter’s graph simplification pass where it offers one.
bool
default:"false"
Export with a dynamic batch axis instead of a fixed one.
list[str] | None
default:"None"
Names for the exported graph inputs.
list[str] | None
default:"None"
Names for the exported graph outputs.
dict | None
default:"None"
Escape hatch: extra adapter-specific options. Recognised keys (input_shape, opset, imgsz, simplify, dynamic_batch, input_names, output_names) are read out of it, and anything left over is forwarded to the adapter as-is.
str | Path
default:"./r3al_export"
Local directory the intermediate .onnx (and its r3al_export_manifest.json) is written to before upload.

Engine options

Forwarded verbatim to the quantization job.
str | None
default:"None"
Free-form tag (e.g. "object-detection") recorded in the deliverable’s manifest and stamped into the ONNX metadata as r3al_domain.
int
default:"100"
Cap on how many calibration images are actually used. Histogram methods want 256-1024; raise this when you pass more than 100 images with percentile or entropy.
str | None
default:"None"
Graph input to feed calibration data into. Defaults to the graph’s first input, which is correct for every single-input vision model.
bool
default:"true"
Per-channel weight scales (one per output channel) instead of one scale per tensor. Silently downgraded to per-tensor when the model’s opset cannot be upgraded to 13.
int
default:"3"
How many hops back from the graph outputs stay in float32 during ptq_static. Detection/pose heads end in numerically fragile ops (Sigmoid, multi-scale Concat) where int8 can collapse every confidence to zero; leaving the last few layers unquantized costs almost nothing in size or speed. Set 0 to quantize the head too.
bool
default:"true"
Keep Softmax, LayerNormalization, Gelu and Erf — plus the MatMul/Gemm nodes directly feeding or consuming a Softmax — in float32. This is the standard transformer PTQ do-not-quantize list; it is a no-op for Conv-only backbones. Applies to both ptq_static and ptq_dynamic.
list[str] | None
default:"None"
Explicit list of node names to leave in float32. Replaces both automatic exclusion lists above, so pass it only when you want full manual control.
bool
default:"false"
Also reconstruct a .pt checkpoint from the quantized graph via onnx2torch. Best-effort: quantized ops are not all supported, so a failure is recorded in the manifest as native_export_error instead of failing the job. The .onnx deliverable is always the primary contract.
calibration_method, calibration_percentile, calibration_num_bins and calibration_symmetric are rejected as quantize() kwargs with UnsupportedConfigError. Set them on QuantConfig so they are validated and recorded in the deliverable’s manifest.

Returns

QuantizedModel
Handle to the downloaded bundle. See QuantizedModel.

train_qat() (runs on the platform)

Fine-tunes the model with quantization simulated in the loop on R3AL GPU infrastructure, then downloads the deliverable. Requires mode="qat"; anything else raises UnsupportedConfigError.

Signature

str | Path | nn.Module
required
Same inputs as quantize(). Non-ONNX models are exported to ONNX locally before upload, so the export options apply here too.
Iterable[str | Path]
required
Training/calibration data. Keyword-only and always required — None raises UnsupportedConfigError. Used twice: once to initialize activation ranges, once as the training set. Accepts image files (JPG, PNG, BMP, GIF, TIFF, WebP, PPM), which are resized to the model’s input, or .npy/.npz arrays already shaped like the model input, which are used as-is. Unreadable entries are skipped and reported in the job’s steps; the run only fails if nothing was readable.
str | Path
default:"./qat_output"
Local directory the deliverable bundle is downloaded into.
bool
default:"true"
Print live job progress while waiting.

Training options

Forwarded verbatim to the QAT pipeline.
int | None
default:"config.qat_epochs"
Training epochs. Falls back to qat_epochs (1) when omitted.
Iterable[str | Path] | None
default:"None"
A held-out set, never trained on, in the same formats as calibration_data. When given, the pipeline also reports validation fidelity before training, after every epoch, and at the end — which is what tells you whether QAT generalized or just memorized the training batches. If none of its entries are readable the run fails rather than silently training without validation.
int
default:"8"
Images per training batch. A graph exported with a fixed batch axis overrides this — its baked-in batch size is used instead, and the run says so. Re-export with a dynamic batch axis to train in larger batches.
float
default:"1e-4"
Adam learning rate for the clip thresholds (wgt_alpha/act_alpha), the scalars that decide where the quantization grid sits.
bool
default:"true"
Also train the quantized layers’ weights, not just the clip thresholds. Set False to tune thresholds alone. Universal ONNX QAT only — the native Ultralytics path always trains thresholds only.
float | None
default:"learning_rate / 10"
Learning rate for the weight parameter group when train_weights=True. Defaults to a tenth of learning_rate: the weights arrive already converged and only need nudging onto grid points, while a threshold is a single scalar that tolerates a larger step. Universal ONNX QAT only.
str | None
default:"auto"
Torch device for training, e.g. cuda:0 or cpu. auto picks CUDA when available.
int | None
default:"None"
Cap on calibration images. Also bounds training: the batch budget becomes min(epochs × qat_calib_batches, max_calib_samples // batch_size).
bool
default:"false"
After training, run a real ONNX Runtime static-int8 pass over the QAT-trained weights and write it to onnxruntime_int8/. Recommended. By itself QAT folds the fake-quant values back into float32 Conv layers, so *_qat.onnx is the same size and speed as the original model; this option is what produces an actually smaller, actually int8 deliverable.
bool
default:"true"
Also write the trained weights back out as {stem}_qat.pt. For Ultralytics inputs this goes through YOLO.save(), so the result reloads as a normal YOLO checkpoint; otherwise it is a plain nn.Module state. Best-effort — a failure is recorded in steps instead of failing the run.
str | None
default:"None"
Free-form manifest tag, same as quantize().
QAT bit widths and activation calibration come from the config and are forwarded automatically: qat_wbit, qat_abit, qat_calib_method, qat_calib_q, qat_calib_batches, plus qat_calib_num_bins when the method is entropy. qat_first_layer_bit and qat_quant_act are applied engine-side from the same config.
imgsz, simplify and opset are claimed by the local export step (they are export options), so they are not forwarded to the training job. The QAT pipeline derives its image size from the uploaded ONNX graph’s input shape.
The pipeline’s progress_callback hook is engine-internal — the platform wires it to the job’s progress field, which is what verbose=True prints. It cannot be passed from the SDK.

Returns

QuantizedModel
Handle to the downloaded bundle: model_qat.quantized.onnx, r3alai_manifest.json, plus onnxruntime_int8/ and {stem}_qat.pt when those options were enabled.
With verbose=True the run also prints the fidelity MSE between the quantized model’s outputs and the original’s: once per epoch while training, then a summary of the before/after figures with a note on what they imply about the learning rate. The same numbers are recorded as fidelity_mse in the manifest and in the job result, so they survive the terminal scrollback.
Without validation_data only the training-set figures can be reported, and a training-set MSE that improves says nothing about whether the improvement generalises — it can equally mean the run fitted those specific images. Pass held-out samples if you intend to act on the numbers.

benchmark() (local)

Runs on CPU or GPU (whichever ONNX Runtime provider is available), cycles through all eval_data samples, prints live progress, and supports Ctrl+C early stopping with partial results. Never touches the network.
str | Path
required
Path to the original .onnx model.
str | Path | QuantizedModel
required
A QuantizedModel handle, or a path to a downloaded bundle directory (or the .onnx next to its manifest).
Iterable
required
Image samples as NCHW float32 arrays or tensors. Every sample is cycled through in each run.
Callable[[model, data], float] | None
default:"None"
Keyword-only. Custom quality metric, called once per model. Leave unset to measure latency only.
str
default:"perplexity"
Keyword-only. Name recorded for the quality metric in the result.
int
default:"3"
Keyword-only. Untimed passes before measurement, to let the provider warm its kernels.
int
default:"10"
Keyword-only. Timed passes per model.
str | Path | None
default:"None"
Keyword-only. YOLO-format labeled dataset config (path/train/val/names, plus kpt_shape for pose models). When given, real box (and pose) mAP50/mAP50-95 are computed for both models through Ultralytics’ own validator. Requires the yolo extra; left None on the result if ultralytics is missing or validation fails — mAP is never fabricated.
int
default:"640"
Keyword-only. Image size for the mAP validation pass, used only with data_yaml. Should match the size the model was exported/quantized at.
bool
default:"true"
Keyword-only. Print per-run progress (current sample, running average) and a final summary including hardware details.

Returns: BenchmarkResult

float
Mean inference latency of the original model, in milliseconds.
float
Mean inference latency of the quantized model, in milliseconds.
float
latency_ms_original / latency_ms_quantized.
float | None
Peak VRAM for the original model, in MB.
float | None
Peak VRAM for the quantized model, in MB.
float | None
VRAM reduction, in percent.
str
Name of the quality metric used.
float | None
Quality score for the original model — set only when quality_fn was provided.
float | None
Quality score for the quantized model.
float | None
quality_quantized - quality_original.
float | None
Detection mAP@0.5 for the original model, via Ultralytics val(). Set only with data_yaml.
float | None
Detection mAP@0.5 for the quantized model.
float | None
Detection mAP@0.5:0.95 for the original model.
float | None
Detection mAP@0.5:0.95 for the quantized model.
float | None
Pose mAP@0.5 for the original model. Pose models only.
float | None
Pose mAP@0.5 for the quantized model.
float | None
Pose mAP@0.5:0.95 for the original model.
float | None
Pose mAP@0.5:0.95 for the quantized model.
dict
Run context: original_model, hardware, provider_original, provider_quantized, num_samples, warmup_runs, benchmark_runs, runs_completed_original, runs_completed_quantized, early_stopped.
See Benchmarking for the full output reference.

load() (local)

str | Path
required
A downloaded bundle directory, or a quantized .onnx sitting next to its r3alai_manifest.json. Raises ModelLoadError if neither is found, or if the manifest says the model is not quantized. Takes no other options.
ort.InferenceSession
Session built with the best available provider (CUDA when present, else CPU).
str
Absolute path of the .onnx that was loaded.
dict
Parsed r3alai_manifest.json — method, bits, calibration settings, domain, excluded nodes. See Manifest.

QuantizedModel

Returned by quantize() and train_qat().
Path
Bundle directory on local disk.
QuantConfig
The config the run was submitted with.
Path
Copies the bundle into output_dir (created if missing, merged if it exists) and returns that path. With no argument it is a no-op that returns path — the model is already saved when quantize() returns.
dict
Same as Quantizer.load(self.path): {"session": ort.InferenceSession, "path": str, "manifest": dict}. Takes no options.

PlatformClient

The lower-level client, when you want direct control over upload, submission, and download.
str | None
default:"None"
Overrides login()/configure()/R3AL_API_KEY for this client.
str | None
default:"https://platform.r3al.ai"
Keyword-only. Platform host override.
int
default:"600"
Keyword-only. Per-request HTTP timeout.

Job

A handle to a running job:

Errors

Platform errors all subclass R3ALPlatformError (itself a subclass of the quant R3ALQuantError), so existing except R3ALQuantError handling keeps working:
Config and quantization errors live in r3alai.quant: UnsupportedConfigError, UnsupportedBitsError, CalibrationRequiredError, ModelLoadError, QuantizationError, BenchmarkError, BackendNotInstalledError.

Local helpers

These never touch the network: