ONNX Explained: How One Format Runs Any AI Model

ONNX Explained: How One Format Runs Any AI Model
ONNX Explained: How One Format Runs Any AI Model

ONNX is the reason a model trained in PyTorch can run inside a browser, a phone, a Kaggle scoring container, or an industrial sensor without anyone rewriting a single layer by hand. It is not a framework and it does not train models. It is a file format and a graph specification, and understanding what actually lives inside an .onnx file explains why the format won the interoperability problem that a dozen competitors tried to solve first.

The format matters more in 2026 than it did five years ago, not less. Every framework still trains models its own way, but almost nothing serves them its own way anymore. ONNX sits underneath a huge share of production inference, from mobile apps to the strict, size-capped submissions that Kaggle’s NeuroGolf competition requires entrants to package their solutions in.

Where It Came From

ONNX started inside Facebook as an internal project called Toffee, built by the PyTorch team to solve a problem specific to that framework: PyTorch’s imperative, define-by-run design made models easy to write and painful to export. In September 2017, Facebook and Microsoft rebranded the project and announced it publicly as the Open Neural Network Exchange. Amazon Web Services joined shortly after, and by December 6, 2017, the three companies announced the first production-ready release. IBM, Huawei, Intel, AMD, Arm, and Qualcomm all added support within the following year.

In November 2019, governance of ONNX moved to the Linux Foundation AI and Data Foundation as a graduated project, which is why no single company controls the spec today. Decisions run through a steering committee and public working groups, including dedicated groups for quantization and, as of the most recent release cycle, a newly formed group exploring probabilistic programming support.

What Actually Lives Inside a .onnx File

An ONNX file is a serialized protocol buffer, Google’s binary format for structured data, containing a computation graph. The graph is a directed acyclic structure: a list of nodes, where each node is a call to a specific operator, connected by named tensors that flow from one node’s output to the next node’s input. Metadata around the graph documents input and output shapes, data types, and the model’s provenance.

Every node’s operator comes from a versioned set called an opset. This is the detail that trips up more engineers than anything else about the format. A model file declares which opset version it targets, and a given ONNX Runtime build supports a contiguous range of opsets, not just the newest one. If a converter exports a model against opset 21 and a deployment target only understands up to opset 18, the affected operators need a version converter pass or the export fails outright. As of mid-2026, ONNX has moved through Opset 26, which added two-bit data types and new operators including CumProd and BitCast, and the community is actively drafting Opset 27, which adds FlexAttention, LinearAttention, and CausalConvWithState operators aimed squarely at transformer and generative workloads that the format’s original computer-vision-era design never anticipated.

This versioning is also why ONNX competitions and deployment pipelines are so strict about which operators they allow. NeuroGolf’s rules ban Loop, Scan, NonZero, Unique, Script, and Function specifically because those are the operators that let a graph branch, iterate, or hide computation behind dynamic control flow. A model built only from statically shaped tensor operations can be measured, bounded, and audited in a way a model with a hidden while-loop cannot.

How a Model Actually Gets Converted

The path from a trained model to a working .onnx file runs through a tracer, not a transpiler. PyTorch’s torch.onnx.export function runs the model forward with example input tensors and records every operation the tensors pass through, then serializes that recorded trace as the graph. TensorFlow and its ecosystem use a similar tracing approach through tools like tf2onnx. Because tracing follows the actual path the data took, any conditional branch the model did not take during the export pass simply will not appear in the graph. This is the single most common cause of an ONNX export that looks correct and then produces wrong answers in production: the trace captured one code path of a model that behaves differently at inference time.

Once exported, real-world ONNX graphs are almost never left as-is. Tracing tends to produce redundant nodes, unnecessary casts, and dead branches left over from training-only logic. Tools like ONNX Simplifier and Microsoft’s own graph optimizer fold constants, remove no-op nodes, and fuse adjacent operations into single kernels the runtime can execute faster. Community notebooks from the NeuroGolf leaderboard document this pattern under names like graph surgery: taking an existing exported model and running it through simplification passes, index rewrites, and precision conversion to shave measurable cost without changing the output.

The Part Most Explainers Skip: Execution Providers

ONNX itself only specifies the graph. It says nothing about how that graph actually runs on a given chip, and that separation is the format’s most underappreciated design decision. ONNX Runtime, the reference execution engine Microsoft maintains, delegates each node in the graph to an Execution Provider, a pluggable backend responsible for running that operator on specific hardware. CPU and CUDA are the default providers, but the ecosystem now includes DirectML for Windows GPUs, CoreML for Apple silicon, TensorRT for NVIDIA inference optimization, WebGPU and WebNN for in-browser execution, and vendor-specific providers from Qualcomm, Arm, and others for mobile NPUs.

This is what makes the single-file promise real. The same .onnx file can route through TensorRT on a data center GPU, CoreML on an iPhone, and WebGPU inside a browser tab, with ONNX Runtime picking whichever registered provider claims each operator and falling back to CPU for anything unsupported. A developer exports once and the runtime handles the fan-out to hardware, rather than a developer hand-writing a separate deployment path per chip family.

Where ONNX Actually Breaks

The format’s honesty problem is coverage, not correctness. ONNX defines a large, versioned operator set, but no single framework converter and no single execution provider implements all of it. A model using a rare or very new operator from a research paper may export cleanly from PyTorch and then fail to load in a mobile runtime that has not yet implemented that operator, or load but silently fall back to a slow, unoptimized CPU path for that one node. Custom operators, common in architectures published within the last year or two, require writing and shipping a custom operator implementation for every execution provider a team wants to target, which defeats some of the portability promise for exactly the models most likely to need it.

Dynamic shapes cause a related, quieter failure. ONNX supports symbolic dimensions, letting a graph declare that its batch size or sequence length is variable rather than fixed at export time. In practice, many converters resolve these to concrete numbers based on whatever example input was used to trace the model, and the resulting graph then either rejects any other input shape or silently pads and truncates in ways that were never tested. Engineers who export with a single example batch of size one and then deploy at batch size thirty-two are a recurring source of production incidents that have nothing to do with the model itself and everything to do with how the trace was captured.

ONNX is also, deliberately, an inference format first. An ai.onnx.training extension exists and can represent gradients and optimizer state, but almost nothing in production actually trains through ONNX. Models get trained in their native framework and exported to ONNX only once training is finished, which means ONNX graphs are a snapshot of a finished model, not a live artifact a team iterates on directly.

The quantization story is improving but still shows its seams. QuantizeLinear and DequantizeLinear, the operators that represent a quantized tensor and its scale factors, only reached parity with newer opsets recently, and different runtimes historically supported different subsets of quantized operator combinations. A model quantized to INT4 or INT8 in one framework’s native format does not automatically produce an equivalently fast ONNX export. The calibration and packing logic has to be reimplemented against ONNX’s specific quantized operator schema, the same GPTQ- and AWQ-style calibration process covered in detail here, ported into ONNX’s own operator schema rather than a framework’s native quantization path. This is the same low-bit tradeoff that shows up whenever a lab talks about serving cost, including the FP8 and FP4 precision schedule DeepSeek built into V4’s attention layers, and ONNX’s operator set has to keep expanding just to represent what training pipelines already do natively.

Why This Matters Beyond Competitions

The practical case for learning ONNX has nothing to do with novelty. It is the format that sits between a research team’s framework of choice and the reality that production inference happens on hardware the research team never touched: mobile chips, browser sandboxes, edge devices, and procurement-locked enterprise GPUs from a different vendor than the one training used. A team that understands the opset boundary, the tracing-based export process, and the execution provider model can diagnose why a model runs at native speed on one deployment target and crawls on another, instead of treating the conversion step as a black box that either works or does not.

The operator additions landing in Opset 26 and the drafts moving through Opset 27, native attention variants, low-bit types, sequence-aware convolutions, are a direct response to the fact that ONNX’s original design targeted convolutional vision models and now has to represent transformer-based generative architectures it was never built for, the same mixture-of-experts and attention designs covered here that ONNX’s qMoE kernels now have to represent directly. The format is being rebuilt in public, opset by opset, to keep up with what the field actually trains.

What Happens Next

The ONNX community’s stated near-term focus is threefold: expanding operator support for generative AI workloads specifically, improving quantization capabilities so low-bit models convert cleanly rather than requiring custom reimplementation, and extending the version converter so models do not silently break when they cross opset boundaries. None of these are finished. A newly formed Probabilistic Programming Working Group signals the community also wants to represent Bayesian and probabilistic models natively, a category ONNX has never handled well.

None of this changes the core proposition. ONNX will keep being the layer teams reach for when the model is done and the question shifts from how well it performs in training to how many different chips it needs to run on. The graph, the opset, and the execution provider are the three concepts worth understanding before touching the format, because they are the three places a working export can quietly stop working.

Comments

Leave a Reply

Discover more from My Written Word

Subscribe now to keep reading and get access to the full archive.

Continue reading