Version 1 ONNX mapping
Normative source: docs/format-onnx.md. This page renders that file unchanged; the guides explain it, this text defines it.
Normative section of the format contract. ONNX is opaque for
visualization: extraction returns null, and load
rejects any structure.json member with ClaimMismatchError.
Original model and native result
Export accepts .onnx paths (str or PathLike, case-insensitive suffix), raw
bytes, onnx.ModelProto, and a previously validated mlbundle._onnx.OnnxModel.
Paths are stat-bounded and read with one excess byte for detection. Paths and
bytes are stored unchanged; the model member hash is the original bytes’ SHA-256.
A ModelProto has no original file representation: serialize it once using
SerializeToString() without changing the object, then preserve those bytes.
There is no conversion, graph editing, external-data loading or file reserialization.
prepare_export validates declarations but cannot bind them into immutable bytes.
native_model is the frozen mlbundle._onnx.OnnxModel record, with data (exact
original bytes) and session (an onnxruntime.InferenceSession). A runtime uses
session.get_inputs(), get_outputs() and run() for native inference; the
bundle predictor provides validated plain-list probability/regression output.
The session and nested values are not deeply immutable. Mutating them invalidates
the loaded-result guarantee, as with other adapters.
Inputs and outputs
Inputs are rank-two numeric tensors, dtype float32, float64, int32 or int64,
with a fixed width from 1 through 100,000. Batch dimension is unknown, symbolic
or exactly one. Multiple inputs require width one each, and their graph input
names are verified feature names in graph order. A single matrix input supplies
no column names; supplied/generated names remain declarations. Verified model
dtypes repeat the matrix dtype per column or preserve the column input dtypes.
Only one target is supported. Regression has one float32/float64 output of shape
[batch] or [batch, 1]. Classifier probabilities are a float32/float64 matrix
[batch, classes] or a sequence of maps with int64/string keys and floating values.
An optional second output is an int64/string label tensor [batch] or [batch, 1].
Batch declarations in outputs must also be unknown/symbolic or exactly one.
Classifier output selection uses these types/shapes, never output name heuristics.
Multi-target regression, image/string/sequence inputs, multi-column multiple inputs,
fixed batches above one and unsupported/indeterminate outputs fail before sessions.
Predictors accept row lists, NumPy arrays and DataFrames, importing pandas only
when a frame is passed. Arrays/lists are positional. Multiple-input DataFrames
must have exactly the feature width and unique model input columns; they are
selected by graph input name, permitting another column order. Single matrix
frames are positional. Integer conversion preserves Python/NumPy integer values,
requires integral finite numbers within the dtype range, and never silently
truncates fractions. Boolean/string/object values and non-finite or overflowing
numeric inputs fail. Static one-row batches require exactly one prediction row.
Output dimensions and finiteness are checked. Probabilities must lie in [0,1]
and sum to one (rtol=1e-6, atol=1e-5); ZipMap must contain exactly the native
labels and is read in their recorded order. No missing probabilities are invented.
Kind and labels
Kind is a verified claim, with no kind= declaration. Two compatible outputs
(probabilities plus labels) determine classification. A single probability output
requires native classifier/ZipMap labels in its upstream graph, or a terminal
standard Softmax over the class dimension (axis=1 or -1, optionally followed
by Identity/Cast). Otherwise only a single scalar-per-row floating output determines
regression. Other graphs are rejected rather than guessing from user metadata.
Single-output Softmax graphs without labels require a fixed class width.
Native labels come from the output’s ai.onnx.ml ZipMap, LinearClassifier,
TreeEnsembleClassifier or SVMClassifier classlabels_strings, classlabels_ints
or classlabels_int64s attributes. Decode strings as UTF-8; preserve integers,
types and native column order. Identity/Cast can preserve a classifier’s probability
columns; unrepresented transformations of labeled probabilities are rejected.
Final ZipMap labels own the map keys, permitting a different mapping from an
upstream classifier’s encoded labels. Unrelated graph nodes cannot supply labels.
Record predictions read the optional graph label tensor. When the label producer
and final probability path preserve the same columns, its native class order maps
to final ZipMap keys, including renamed strings. A graph ArgMax on the class axis
can supply encoded indices on that same path; mlbundle never computes argmax.
Unrelated heads must return known labels, and invalid results raise bounded errors.
Without a label output, predicted_label is None. These decisions are independent
of the probability values and do not add an array-level method to LoadedBundle.
Fixed probability width determines
class count; otherwise native labels determine it. Width and native labels must
agree when both exist. Labels must satisfy the shared uniqueness/count/bounds rules.
Native labels are verified against the unchanged model; supplied labels must agree.
Absent native labels remain null in extraction. The shared pipeline resolves
supplied labels or generates 0..N-1, checking native counts. Such labels are
declarations, and load cannot authenticate the original-label mapping.
Claims
Estimator identity is exactly ONNX; output count is one and structure is null.
hyperparameters is the verified graph projection with exactly inputs, outputs
and metadata_props. It describes available graph facts, not training parameters.
Inputs/outputs are graph-ordered arrays of objects with name, dtype, shape.
Tensor shapes preserve integer static dimensions, symbolic dimension strings and
null unknown dimensions. Dtypes use normalized names above (plus string for
label tensors). Map output dtype is exactly sequence<map<int64,float32>>,
sequence<map<string,float32>> or the corresponding float64 spelling; shape is null.
metadata_props maps each unique model-level metadata key to its original string
value, without interpreting it as configuration. The existing 1 MiB/depth-16
hyperparameter ceiling applies; no training parameters are inferred from it.
environment.onnx retains exactly the existing required opsets (unique, sorted
by original domain), IR version and producer name/version. These fields, graph
signatures, counts, kind, names/dtypes and native labels are all recomputed with
the writer’s extraction code and compared by exact canonical bytes. Historical
environment, training dtypes, supplied matrix feature names, target names and
label-free mappings remain declarations.
Producer name/version each allow 0–65,536 characters, including multiline build information (CatBoost writes its build record as the version). This bound also admits the short tool/release strings written by skl2onnx, onnxmltools and PyTorch. Opset domains retain their 256-character ceiling and closed domain set. Symbolic dimensions, verified column names and string class labels retain the shared 1,024-character ceiling. Matrix input/output names and metadata keys/values have no separate character ceiling: their full projection shares the 1 MiB hyperparameter budget. Graph/node names, model domain and documentation are not claims and remain bounded by member bytes. String-limit errors identify the closed offending claim field and numeric limit without echoing its value; projection-limit errors name inputs/outputs/metadata_props and their byte/depth cap.
Before any native session
Both export and load perform the same checks, in this order:
- Bound bytes to the 512 MiB native-member ceiling (archive/caller limits also apply). Scan protobuf wire fields through the installed ONNX descriptors before construction: depth at most 32, at most 536,870,912 fields/scalar values, valid lengths/wire types/varints/packed fields, no unknown fields or duplicate singular fields. Each field header and packed scalar consumes at least one distinct byte, so this count cannot exceed the admitted member bytes. Unlike the former 1,000,000 ceiling, it admits large unpacked tree attributes up to the byte ceiling; it does not cap protobuf’s decoded object memory. Repeated scalars use bounded 64 KiB NumPy scans with C regex fallback for short/irregular runs; equal repeated strings/bytes up to 4 KiB use C regex. Packed varints use C regex with NumPy counts. A 64-byte regex probe gates vector scans, whose windows grow from 128 bytes only after proportional progress to avoid overlapping work on short alternating fields. Other fields use the descriptor walker. Batching preserves every wire check and counts every scalar/header.
- Parse in-memory with ModelProto. Visit every message, including sparse tensors, tensor attributes, nested graphs and functions. Reject any tensor with an external-data entry or non-default data location, segments or unsupported storage type. Supported storage types are the original ONNX scalar types FLOAT through BFLOAT16; newer packed/float8 formats are outside this boundary.
- Check tensor rank (at most 32), nonnegative dimensions, dimension/element/byte budgets, exact raw-data length or typed scalar count and exclusive storage. Fixed-width decoded storage and its aggregate are capped at 512 MiB; sparse declared dense storage is also bounded. Bound static value dimensions and symbolic names (1,024 characters). Count at most 100,000 nodes across all nested graphs.
- Permit only standard ONNX domains
""(the wire spelling ofai.onnx),ai.onnxandai.onnx.mlfor nodes/opsets/functions; checker/runtime must also recognize the actual operators and versions. Reject local functions entirely, including standard-domain shadowing, training info, device configuration, node overloads and overridable root initializers. Reject modelort_config. No custom-op library is registered. - Run
onnx.checker.check_model(..., full_check=False)in memory; shape inference and constant propagation are not requested. Validate the supported tabular layout and build bounded strict claims before creating any runtime session. Terminal LinearRegressor/TreeEnsembleRegressor native target counts must be one.
The immutable bytes are scanned, parsed and described once per export or load. Each newly created adapter retains one description by bytes-object identity for that operation; there is no global cache or author-supplied description. Export still creates and validates independent CPU sessions for preparation and its native round trip. Load compares the recomputed description with the manifest and constructs its predictor from that same validated description.
Create ONNX Runtime from those bytes with CPUExecutionProvider only, sequential
execution, one intra/inter-op thread, disabled graph optimization/prepacking,
CPU arena and memory patterns. This limits load-time work and retained buffers;
it is not a bound on execution memory/CPU. Profiling and optimized-model output
are disabled, model format is explicitly ONNX and read_config_from_model=False.
No files are written. The runtime host must not set ORT_LOAD_CONFIG_FROM_MODEL=1:
the pinned runtime rejects this combination; failures are bounded mlbundle errors.
Both export and load must successfully create a session, then compare its input/
output types, ranks and known dimensions with graph declarations. A declared
static dimension must remain exact in runtime metadata; unknown inferred width
cannot conceal disagreement. Runtime probability width must match native class
count when exposed. ONNX/checker/runtime
failures become bounded InvalidModelError; resource ceilings become
LimitExceededError. Missing dependencies name mlbundle[onnx].
ONNX/protobuf/checker and ONNX Runtime are native dependency boundaries, not sandboxes. Runtime-computed shapes and control flow can allocate or consume CPU beyond graph declarations. Consumers still need process isolation, memory/CPU limits, prediction row budgets and maintained native libraries.
