Nano GPT logo
NanoGPT

Private AI

Back to Blog

ONNX Interoperability with AI Frameworks: FAQ

Aug 18, 2026

You can train a model in one framework and run it somewhere else with ONNX - but only for inference, not training. That’s the big point. If I export a model to ONNX, I still need to check opset support, test outputs with the same input, and rebuild any sampling or post-processing that was never part of the graph.

Here’s the short version:

  • ONNX moves inference graphs, not full training state.
  • PyTorch, TensorFlow/Keras, scikit-learn, and Hugging Face tools can export models to ONNX.
  • ONNX Runtime is the main place to run exported models, with hardware backends such as TensorRT, OpenVINO, and Core ML.
  • Common failure points include unsupported operators, dynamic control flow, dtype issues, shape/layout mismatches, and frozen random ops.
  • Output checks matter: I should run onnx.checker, then compare source and ONNX outputs with allclose.
  • Opset choice matters a lot: for example, native Attention and RotaryEmbedding support starts at opset 23, Swish/SiLU at 24, and native FP16/BF16 range support at 27.

A simple rule helps: export, validate, compare, then deploy. If outputs drift a little, that can be normal. If outputs turn constant or wrong, the export likely dropped behavior outside the forward graph.

Area What to know
Scope ONNX is for inference portability
Not included Training state, training workflow, sampling loops, post-processing
Export tools torch.onnx, tf2onnx, sklearn-onnx, optimum
Main checks model.eval(), fixed input, onnx.checker, allclose
Common issues Ops, control flow, dtypes, shapes, randomness
Best next step Use the lowest opset that still supports your model

If I want framework switching without rewriting deployment code for each target, this FAQ lays out the main path - and the main places things go wrong.

ONNX: The Missing Link Between AI Frameworks

ONNX

sbb-itb-903b5f2

Which frameworks, converters, and runtimes ONNX supports

ONNX spans three layers: source frameworks, conversion tools, and inference runtimes. That setup shapes the stack you use to export, convert, and deploy a model.

Frameworks and export tools

PyTorch models are exported with the built-in torch.onnx module, which supports multiple opset versions. Before export, set the model to evaluation mode with model.eval() so dropout and other training-only layers don’t change inference behavior.

TensorFlow and Keras models are exported through tf2onnx, a separate library that translates the graph into ONNX. scikit-learn uses sklearn-onnx for classical machine learning models such as SVMs, random forests, and linear models.

For transformer models, Hugging Face offers dedicated export paths through optimum, which produces ONNX-ready exports for transformer architectures.

Here’s the quick breakdown:

Framework or Tool Role Typical Export Path Common Model Types
PyTorch Source Framework torch.onnx.export CNNs, Transformers
TensorFlow / Keras Source Framework tf2onnx Vision, NLP, Legacy models
scikit-learn Source Framework sklearn-onnx SVMs, Random Forests, Linear Models
ONNXMLTools Converter ONNXMLTools API Classical ML ecosystems
Hugging Face Transformer Export optimum / HF Export BERT, GPT, Llama, ViT
ONNX Runtime Runtime N/A All ONNX-compatible models
TensorRT / OpenVINO Execution Provider N/A NVIDIA/Intel optimized inference

Inference runtimes and deployment targets

After export, the next issue is simple: where will the model run?

ONNX Runtime (ORT) is the main cross-platform execution engine. It runs on CPU, GPU, and edge hardware without needing the original training framework. Its Execution Provider system lets you connect hardware-specific backends such as NVIDIA TensorRT for GPU-tuned inference, Intel OpenVINO for Intel hardware, and Apple Core ML for on-device deployment on macOS and iOS.

Once you’ve picked the runtime, the next step is to check that the exported model still lines up with the original one.

How framework switching works with ONNX

ONNX Export-to-Deploy Workflow: Prepare, Export, Validate, Deploy

ONNX Export-to-Deploy Workflow: Prepare, Export, Validate, Deploy

ONNX lets you take a trained model and run it in a different inference runtime. It does not move the model back into training. You export the model as a .onnx file, and a compatible runtime executes that file on the hardware you care about.

Once that part is clear, the next step is simple: move the model from the source framework into the runtime without breaking anything along the way.

The basic export-to-runtime workflow

The workflow is straightforward: prepare, export, validate, then deploy.

Prepare: Put the model in inference mode before export.

Export: Use the right converter for your stack - PyTorch (torch.onnx.export), TensorFlow/Keras (tf2onnx), or a JAX/Flax converter - and save the output as a .onnx file. One catch: sampling, generation loops, and other post-processing steps are not exported. You need to rebuild those parts in the target environment. If post-processing is missing after export, recreate it in the target runtime.

Validate: This is where you catch structural problems before deployment. First, check the model structure with onnx.checker. Then run an allclose comparison against the source model using the same input.

Deploy: Load the checked .onnx file into the target runtime and run inference.

After export, compatibility comes down to the opset you choose and the runtime version you plan to use.

Opset versions, model metadata, and compatibility checks

Every export targets an opset. The opset defines which operators are available and how they behave. In practice, you usually want the lowest opset that still covers every operator your model uses. That gives you the best chance of running the model across more runtimes.

Feature / Operator Minimum Opset Notes
Attention / RotaryEmbedding Opset 23 Supported directly in this opset; older versions use fallback implementations
Swish / SiLU Opset 24 Supported directly in this opset; earlier versions use fallback implementations
Native FP16/BF16 Range Opset 27 Earlier versions may require cast fallbacks

Before deployment, make sure the source framework, converter, ONNX package, opset, and runtime all line up. When they don’t, conversion tends to fail in fairly predictable ways.

When ONNX conversion breaks and how to reduce the risk

A model can still fail after export, even when the opset looks compatible.

Common reasons exports fail or produce wrong outputs

The usual trouble spots are framework-only operators, dynamic control flow, and type mismatches.

These issues show up when framework behavior doesn't map cleanly to ONNX. Dynamic control flow often needs graph rewrites before it translates the way you expect. Data types can also trip things up, especially with BF16, complex numbers, and FP64. And then there are tensor layouts. A mismatch like NCHW vs. NHWC can trigger shape errors that are frustrating to track down.

One reported PyTorch export shows how messy this can get: generation logic that lived outside the traced graph was dropped, which led to the same output for every input. If your model uses stochastic calls like torch.rand, those calls can also get frozen into constants during export.

The main risk areas are operators, control flow, data types, shapes, and randomness.

Checks to run before and after conversion

A few checks can help catch both structural and meaning-level mismatches before deployment.

  • Set opset_version explicitly and verify that every operator is supported in that opset. Some newer Transformer architectures need opset 23 or higher for native Attention and RotaryEmbedding support.
  • If stochastic operations are present, set do_constant_folding=False so runtime values don't get baked in as constants.
  • After export, run onnx.checker.check_model.
  • Compare outputs with allclose using the same input on both sides.
  • Use graph simplification to flatten unsupported custom layers.

How to test whether ONNX outputs match the original model

After the export checks pass, the next step is simple: make sure ONNX Runtime gives almost the same numbers as the source model.

A simple workflow for checking output accuracy

Use a parity test to see how close the ONNX model is to the original one. Feed the same fixed input into both models, then compare the logits or final outputs with a strict tolerance threshold.

The key here is consistency. Fixed test inputs help you separate conversion issues from random variation.

For language models, only compare the forward pass. Sampling should stay outside the exported graph, so the forward pass is the part you can compare directly.

What to do when outputs are within tolerance but not equal

Small numeric drift is normal, especially across hardware or runtime backends.

That said, not every mismatch is harmless. If the model returns a constant output, the export probably lost dynamic behavior.

If the gap is larger than your tolerance allows, start with preprocessing. Then check data types and dynamic axes. Those three areas are often where things go off the rails.

Back to Blog