burn-onnx

Library Listed

Import and export ONNX models with the Burn framework

Checked · Sources

Downloads
14,530
Dependents
23
Stars
141
Forks
45
Contributors
81
Commits
751
Latest release
0.22.0 ·

About

From the project README ·

Turn ONNX models into native Rust code for the Burn deep learning framework.

Docs | Burn Book | Supported Operators | Examples | Discord

burn-onnx reads an ONNX model exported from PyTorch, TensorFlow, JAX, or anything else that speaks ONNX, and writes it out as plain Burn code: a Model struct with a typed forward method, plus a weights file. There is no ONNX runtime to ship. The result compiles with the rest of your crate and runs on every Burn backend, from a browser tab or a microcontroller to a CUDA GPU.

Read the full README
  • Readable output. The generated .rs file is ordinary Burn code you can read, step through in a debugger, and edit.
  • Any backend, any target. CPU, CUDA, Metal, Vulkan, WebGPU, WebAssembly, and no_std embedded.
  • Every opset. Each supported operator handles ONNX opsets 1 through 24, including the attribute to input migrations and changed defaults along the way.
  • Optimized at import. Constant folding, shape propagation, common subexpression elimination, dead code removal, and attention fusion run before code generation.
  • Built for big models. Large graphs are split into submodules so the generated code stays compilable, which takes graphs as large as the 28,000-node Stable Diffusion XL UNet.
  • Extensible. Operators outside the supported set, including vendor domains like com.microsoft, can be implemented with your own Rust instead of blocking the import.
  • Trainable. The imported model is a regular Burn Module, so you can fine-tune it.
  • Export too. Experimental support for the reverse direction: save a Burn module as an ONNX file.

Quick Start

Add the dependencies to Cargo.toml. The generated code loads its weights through burn-store, so your crate needs it as well, and burn needs a backend feature (flex is the portable CPU backend):

[dependencies]
burn = { version = "0.22", features = ["flex"] }
burn-store = "0.22"

[build-dependencies]
burn-onnx = "0.22"

Convert the model in build.rs:

use burn_onnx::ModelGen;

fn main() {
    ModelGen::new()
        .input("src/model/my_model.onnx")
        .out_dir("model/")
        .run_from_script();
}

Include the generated code, for example from src/model/mod.rs:

pub mod my_model {
    include!(concat!(env!("OUT_DIR"), "/model/my_model.rs"));
}

Then run it:

use burn::tensor::{Device, Tensor};
use crate::model::my_model::Model;

fn main() {
    let device = Device::default();
    let model = Model::default(); // loads the weights written by build.rs
    let input = Tensor::<4>::zeros([1, 3, 224, 224], &device);
    let output = model.forward(input);
    println!("{output}");
}

The Burn Book chapter walks through the same steps in more detail, and onnx-inference is a complete project that classifies MNIST digits.

What gets generated

For each .onnx input, ModelGen writes a .rs file and a .bpk (Burnpack) weights file. Here is the forward method generated for the MNIST model in the example above:

pub fn forward(&self, input_1: Tensor<4>) -> Tensor<2> {
    let conv2d1_out1 = self.conv2d1.forward(input_1);
    let relu1_out1 = burn::tensor::activation::relu(conv2d1_out1);
    let conv2d2_out1 = self.conv2d2.forward(relu1_out1);
    let relu2_out1 = burn::tensor::activation::relu(conv2d2_out1);
    // ...
    let linear2_out1 = self.linear2.forward(relu4_out1);
    let batchnormalization2_out1 = self.batchnormalization2.forward(linear2_out1);
    let logsoftmax1_out1 = log_softmax(batchnormalization2_out1, 1);
    logsoftmax1_out1
}

Layers with weights become Burn modules (Conv2d, Linear, BatchNorm, ...) and everything else becomes direct tensor operations. Pass .development(true) to ModelGen to also dump the parsed ONNX graph next to the code, which helps when debugging an import.

Loading weights

ModelGen::load_strategy decides how the model finds its weights at runtime:

LoadStrategyGenerated constructorsGood for
File (default)default(), from_file(path, &device), from_bytes(..)desktop and server apps
Embeddeddefault(), from_embedded(&device), from_bytes(..)single-binary deploys, embedded
Bytesfrom_bytes(bytes, &device)WebAssembly, custom loaders
Nonenonemanaging weights yourself

With File, Model::default() reads the .bpk from the path it was written to at generation time: an absolute path inside OUT_DIR from a build script, or the out_dir exactly as given to onnx2burn or run_from_cli, which a relative path resolves against the working directory at runtime. That is convenient during development; for a binary you distribute, ship the .bpk and call Model::from_file(path, &device), or switch to Embedded. Avoid Model::new(&device) on its own: it builds the structure without loading any weights.

Command Line

onnx2burn runs the conversion outside a build script. It is useful for reading the generated code, or for checking in generated code that you intend to edit by hand:

cargo install burn-onnx
onnx2burn my_model.onnx ./generated

Flags: --embed-states (embed weights in the code), --no-simplify, --no-partition, and --no-development (skip the debug dumps the CLI writes by default).

Custom Operators

Operators outside the supported set do not have to block an import: vendor domains such as com.microsoft, ops from a framework's custom export, and op types not implemented yet. Register a hook and supply the Rust yourself:

// build.rs
ModelGen::new()
    .input("src/model/my_model.onnx")
    .out_dir("model/")
    .register_custom_op(FftReal)      // handles my_domain::FftReal
    .register_op_override(MyMatMul)   // replaces the generated code for every MatMul
    .run_from_script();
  • CustomOp supplies type inference and code generation for one ONNX (op_type, domain). It can read the node's attributes and constant inputs.
  • OpOverride replaces the generated code for a built-in operator, to route it to a fused, quantized, or hardware-specific kernel of your own. Type inference still comes from the built-in.

Everything a hook needs is re-exported from burn_onnx::ext, so you never depend on onnx-ir or match proc-macro2/quote versions by hand.

Not sure which operators a model needs? Build with no hooks registered. The import fails with a list of every unsupported operator, its domain, and how many nodes use it:

Failed to parse ONNX file 'src/model/custom_model.onnx': model contains 2 custom op(s) with no
covering inference hook:
  - example.custom::ChannelScale used by 1 node(s)
  - example.custom::ScaleBias used by 1 node(s)
Register hooks via ModelGen::register_custom_op.

The custom-op-hooks example is a working walkthrough, and the Development Guide has the full reference.

Exporting to ONNX (experimental)

The export feature goes the other way. OnnxExporter runs a module's forward pass once on a capture device, records the operations, and writes an ONNX model with the weights embedded:

[dependencies]
burn-onnx = { version = "0.22", features = ["export"] }
use burn_onnx::export::OnnxExporter;

let sample = Tensor::<4>::zeros([1, 3, 224, 224], &device);
OnnxExporter::new()
    .export(&model, sample, MyModel::forward)?
    .save("my_model.onnx")?;

export fixes every dimension to the sample's shape. export_dynamic takes a second sample and an InputSpec per input to mark axes such as the batch size as symbolic. The exporter targets opset 18 and currently covers the operations typical of convolutional and fully connected networks (ResNet-18 exports and passes the ONNX checker); anything it cannot lower yet is reported as ExportError::UnsupportedOperation. See the export module docs for details.

How It Works

 model.onnx
     │
     ▼
 onnx-ir      parse protobuf ─▶ typed IR nodes ─▶ type & shape inference ─▶ simplification
     │
     ▼
 burn-onnx    Burn code generation ─▶ partition large graphs into submodules
     │
     ├─▶ model.rs    Model struct + forward(), formatted Rust
     └─▶ model.bpk   weights in Burnpack format

onnx-ir is a standalone ONNX parser that knows nothing about Burn: it turns the protobuf into a typed graph, parses each operator's attributes into a typed config, infers types and static shapes, and simplifies the graph. burn-onnx then maps each node to Burn code. The Development Guide covers each phase in detail.

Crates

CrateDescription
burn-onnxCode generator, onnx2burn CLI, and ONNX exporter
onnx-irFramework-independent ONNX parser and intermediate representation
onnx-ir-deriveDerive macros used by onnx-ir

Examples

ExampleDescription
onnx-inferenceMNIST classifier: the smallest complete import
image-classification-webSqueezeNet in the browser with WebAssembly and WebGPU
raspberry-pi-picono_std inference on a microcontroller, weights embedded
custom-op-hooksCustom operators and built-in operator overrides

Testing and Compatibility

  • Real-world models. 27 models are imported, compiled, and compared against ONNX Runtime outputs, covering image classification, detection, depth estimation, language models, speech, text-to-speech, and diffusion: ResNet, YOLO, CLIP, BERT variants, Qwen, SmolLM, Kokoro, Silero VAD, Depth Pro, Stable Diffusion XL, and more.
  • The official ONNX test suite. 1,185 of the 1,765 upstream ONNX backend node tests pass end to end on every CI run, with the status of the rest tracked in a checked-in expectations file.
  • Opset compliance. Every supported operator is tested at every opset version it exists in, 461 operator-version combinations in all.
  • Operator tests. Hundreds of integration tests built from PyTorch- or NumPy-generated models, with expected outputs from the ONNX reference evaluator.

Troubleshooting

  • Unsupported operator. The error lists each missing operator. Check the supported operators, then either implement it as a custom op or open an issue.
  • Generated code does not compile. Make sure burn, burn-store, and burn-onnx are the same version. Find the generated file under target/<profile>/build/<your-crate>-*/out/ and read it, or generate it with onnx2burn to inspect it directly. Please report it, with the model if you can.
  • Wrong outputs. Confirm the model was loaded with default(), from_file, from_embedded, or from_bytes, not new. Then compare against ONNX Runtime with the same input.
  • Very old models. Opsets 1 through 24 are supported, but upgrading an old model with onnx_opset_upgrade.py (uv run --script onnx_opset_upgrade.py) to opset 16 also runs ONNX shape inference, which can help with models that carry little shape information.

Contributing

Contributions are welcome. Please read the Contributing Guidelines before opening a PR, and the Development Guide for the architecture and a step-by-step walkthrough of adding an operator. For questions and discussion, join us on Discord.

License

Licensed under either of Apache License, Version 2.0 or MIT license at your option.

Runs on
GPU support unknown
What shows it
  • no dependency or feature matches a backend row
Native code
None found
  • No native code found
Python bindings
No Python binding crate found
License
Apache-2.0 OR MIT

Compare with similar projects

Order: strongest tier first, then most downloads, then name, with unmeasured values last. A library counts downloads over 30 days. An app counts binary downloads per release. An app with none counts its crate downloads over 30 days. ML Rust runs this catalogue, so its closest project always gets a row.

burn-onnx beside the projects that share a topic with it
ProjectDownloadsDependentsStarsLatest releaseRuns on (GPU)Native codeLicense
burn-onnx This page Listed

Import and export ONNX models with the Burn framework

14,530 downloads over 30 days 23 141 0.22.0 GPU support unknown No native code found Apache-2.0 OR MIT

Minimal ONNX model loader for extracting weights, tensors, operations, and graph structure

3,975 downloads over 30 days 4 6 0.5.2 GPU support unknown No native code found MIT
gguf Listed

GGUF file parser

2,472 downloads over 30 days 4 25 0.1.8 GPU support unknown No native code found MIT
onnx-rs Listed

Zero-dependency ONNX model parser in Rust

453 downloads over 30 days 5 6 0.1.3 GPU support unknown No native code found MIT
cmf App Listed

One backbone, many specialists. CMF is a self-describing, memory-mappable single-file format and a dependency-free runtime for quantized LLMs — zero-copy execution on CPU or GPU (Vulkan · Metal · DX12) that overlays task-specialized skills onto one shared model, with no per-model duplication.

24 binary downloads per release Not applicable 52 Not measured Not measured Apache-2.0
compressr App New

Model conversion and quantization with no Python: reads SafeTensors, GGUF, TCF and PyTorch state dicts, writes SafeTensors, GGUF and TCF, and quantizes to Q2_K through Q8_0 and IQ4 formats, including Q4_K_M mixes. GPTQ, AWQ, NF4, FP8, EXL2 and ONNX input are not implemented.

Python bindings

23 downloads over 30 days Not applicable 0 0.2.1 GPU support unknown No native code found Apache-2.0
Why ListedNo archive, New or Featured rule places it, so it is Listed.

What it lacks for Featured

  • It has 6 active dependents and 6 new dependents in 365 days. Featured needs 11 active dependents or 9 new dependents in 365 days. The repository route reads 141 stars, 45 forks and 81 contributors. It needs 4,539 stars, 434 forks and 79 contributors together.

What else the check found

  • The last activity is on 7 Nov 2026, 2 days ago. The rules expect an update within 365 days. Dormancy rule

Exact rules: Listed, Featured library. The check on applied thresholds version 9.

Every rule outcome, as the check records it
  • expected-interval: version 0.22.0 base 140, raised to the 365-day floor: E = 365 days
  • dormancy: last activity 2026-11-07, 2 days ago, E = 365: active
  • featured: not featured: active_users 6 < 11 and new_adopters 6 < 9 and repository route stars 141 (needs 4539), forks 45 (needs 434), contributors 81 (needs 79)
  • listed: listed: no archive, new or featured rule applies
Crates4 crates

Measurements and sources

Each signal row shows the raw value, its source and the fetch date. The Sources list gathers every distinct source on this page. Movement needs more history.

Sources6 sources
Adoption6 active dependents, 6 new dependents in 365 days1 not measured
Adoption measurements
SignalValueSourceFetchedCounts
Active dependents6crates.io database dumpYes
New dependents (365 days)6crates.io database dumpYes
Peak active dependents5.5crates.io database dumpYes
Active dependents as a share of the peak1crates.io database dumpYes
Binary downloads per releaseNot measuredYes
MaintenanceLatest human commit: 2 days ago3 not measured
Maintenance measurements
SignalValueSourceFetchedCounts
Latest human commit2026-11-07GitHub repositoryYes
Latest release2026-10-29crates.io database dumpYes
First release2023-05-06crates.io database dumpYes
Latest version0.22.0crates.io database dumpYes
Days the oldest outside issue waits for a reply0GitHub repositoryYes
Maintainer replies over 365 days25GitHub repositoryYes
Repository archivedfalseGitHub repositoryYes
Repository gonefalseGitHub repositoryYes
Release commit or manifest in the repotruecrates.io packageYes
Published docstruecrates.io database dumpYes
Latest release fails cargo checkNot measuredYes
Build check dateNot measuredYes
Build check toolchainNot measuredYes
SecurityUnpatched vulnerabilities: none
Security measurements
SignalValueSourceFetchedCounts
Unpatched vulnerabilities past the grace windownoneRustSec advisory databaseYes
RustSec unmaintained advisorynoneRustSec advisory databaseYes
Deprecation noticenonecrates.io database dumpYes
Deprecated requirementnonecrates.io database dumpYes
Distribution10 releases, 14,530 downloads over 30 days5 not measured
Distribution measurements
SignalValueSourceFetchedCounts
Releases10crates.io database dumpYes
Downloads over 30 days14530crates.io database dumpYes
Binary downloads over the last 3 releasesNot measuredYes
Homebrew installs on request over 365 daysNot measuredYes
Repology repository familiesNot measuredYes
Docker pull growth over 90 daysNot measuredYes
Docker pulls at the growth baselineNot measuredYes
Every version a prereleasefalsecrates.io database dumpYes
Every version 0.0.xfalsecrates.io database dumpYes
Every version yankedfalsecrates.io database dumpYes
Crate deleted from crates.iofalsecrates.io database dumpYes
Repository141 stars, 45 forks, 81 contributors, 751 commits. Stars alone never qualify a project.
Repository measurements
SignalValueSourceFetchedCounts
GitHub stars141GitHub repositoryYes — one part of the repository route
GitHub forks45GitHub repositoryYes — one part of the repository route
Contributors with a GitHub account81GitHub contributorsYes — one part of the repository route
Commits on the default branch751GitHub repositoryNo
Repository's main crate left out of the entrynonecrates.io database dumpYes
Facts that never count
Facts that never count measurements
SignalValueSourceFetchedCounts
LicenseApache-2.0 OR MITcrates.io database dumpNo
Runs on, from listed crates and their direct dependenciesGPU support unknowncrates.io database dumpNo
What shows each backendno dependency or feature matches a backend rowcrates.io database dumpNo
Native code, from listed crates and their dependenciesNo native code foundcrates.io database dumpNo
Python bindings, from direct dependencies of listed cratesNo Python binding crate foundcrates.io database dumpNo
Crates that depend on a listed crate directly23crates.io database dumpNo

Corrections and review history

Report a correction

No correction applies to this entry.

Judgments

A judgment enters only through an approved issue and a merged pull request.

No judgment applies to this entry.