---
title: "Convert ONNX to the OneModel format"
url: https://recursiveai.org/onemodel/cookbook/convert-to-onemodel-format/
description: "Convert an ONNX model to the OneModel format in C#, so its weights load as memory-mapped views instead of being parsed, with bit-identical outputs. Runnable code."
version: "1.0.0"
---

# Convert ONNX to the OneModel format

`ModelFormat.Convert` writes the model once in OneModel’s own format, whose weights load as memory-mapped views instead of being parsed out of protobuf. `Model.Load` opens either kind, and the outputs are the same bits.

```shell
dotnet new console -o ConvertModel && cd ConvertModel
dotnet add package RecursiveAI.OneModel --version 1.0.0
curl -O https://recursiveai.org/onemodel/samples/wide.onnx
```

Put this in `Program.cs` and run `dotnet run`:

```csharp
using RecursiveAI.OneModel;
using RecursiveAI.OneModel.Format;

ModelFormat.Convert("wide.onnx", "wide.onemodel");
Console.WriteLine($"wide.onnx      {new FileInfo("wide.onnx").Length,8:N0} bytes");
Console.WriteLine($"wide.onemodel  {new FileInfo("wide.onemodel").Length,8:N0} bytes");
Console.WriteLine($"wide.onemodel is {ModelFormat.Detect("wide.onemodel")}");

float[] input = Enumerable.Range(0, 256).Select(i => i / 256f).ToArray();
using var fromOnnx = Model.Load("wide.onnx");
using var fromOwn = Model.Load("wide.onemodel");
float[] a = fromOnnx.Run(input), b = fromOwn.Run(input);

bool same = a.Zip(b).All(p => BitConverter.SingleToInt32Bits(p.First) == BitConverter.SingleToInt32Bits(p.Second));
Console.WriteLine($"Bit-identical outputs: {same}");

File.WriteAllBytes("broken.onemodel", File.ReadAllBytes("wide.onemodel")[..4000]);
try { Model.Load("broken.onemodel"); }
catch (ModelFormatException e) { Console.WriteLine($"{e.Kind}: {e.Message}"); }
```

```text
wide.onnx       655,516 bytes
wide.onemodel   656,320 bytes
wide.onemodel is OneModel
Bit-identical outputs: True
Malformed: the header says the file is 656320 bytes; it is 4000 (truncated or extended) (broken.onemodel at byte 48, file_length)
```

## The format

A `.onemodel` file is a 64-byte header, a JSON manifest of the graph, and the raw weights, each tensor aligned to 64 bytes so it loads as a view of a memory map instead of being copied out of protobuf. What is saved is the imported graph before any optimization, so a converted model optimizes and runs exactly as the ONNX file it came from. The layout is in [the OneModel format](https://recursiveai.org/onemodel/docs/format/).

- `Model.Load` and `Session.Load` tell the two kinds apart by their first eight bytes (`ONEMODEL`), so the loading code doesn’t change. `ModelFormat.Detect` makes the same check and returns `Onnx`, `OneModel` or `Unknown`.
- `ModelFormat.Save(model, path)` writes a model you already imported; `ModelFormat.Load` returns its `IrModel`.
- A damaged file fails with `ModelFormatException`, whose `Kind`, `Offset` and `FieldPath` say what is wrong and where.

## Limits

- The format is version 1, and its specification doesn’t yet say how long later releases keep reading version 1 files. Keep the ONNX file, and convert again when you update if a file stops loading.
- On a model this small the file sizes are about the same. The gain is in loading: aligned weights are mapped, not parsed and copied.
