About

ONNX Is a Package Format, Not a Model — Xenova Didn't Build BERT

Who trained it and what box it ships in are two separate questions

ONNX isn't a model. It's a package format that holds the compute order (the graph) and the weights in one file. Who trained a model and how it's packaged for distribution are separate things. Once that clicks, you can answer "will this model run in the browser?" from a repo's file list instead of its name.

How this came up: no Korean BERT

I built an experiment app that runs six BERT-family models in the browser with transformers.js. The only usable ones were ONNX exports like Xenova/.... klue/bert-base, a Korean BERT, had zero .onnx files according to the Hugging Face API, so it couldn't go in (checked 2026-09-19).

Then came the question: "So did Xenova do the pre-training and fine-tuning?" Easy to mix up. The repo name blends the maker and the packager.

Unpacking one repo name

Xenova/bert-base-multilingual-uncased-sentiment went through four steps.

Who did what in Xenova/bert-base-multilingual-uncased-sentimentGooglepre-trainingWikipedia, 104 languagesNLP Townfine-tuningproduct review starsXenovaONNX exportquantize q8 / fp16transformers.jsrunWASM / WebGPUTraining: the numbers get decided herePackaging and running: same numbers

Training ended in the first two. Google pre-trained on Wikipedia in 104 languages, and NLP Town fine-tuned it on product review stars. Xenova moved the same weights into an ONNX box and also uploaded quantized versions (q8, fp16: storing the numbers in smaller formats). transformers.js runs that with ONNX Runtime (WASM or WebGPU).

Compared with a PyTorch checkpoint

PyTorch checkpoint (.bin, .safetensors) ONNX (.onnx)
What's inside Weights (numbers) only Compute graph + weights
To run it Python + the model class code (transformers library) Just ONNX Runtime
Where it runs Mostly Python servers Browser (WASM, WebGPU), C#, Java, mobile and more
Limit I hit — Only the outputs chosen at export time. I couldn't get per-layer hidden states or attention values out

That last row actually hurt. I wanted the app to show how a sentence changes inside the encoder, but the ONNX export only exposed the final layer. The packaging step decides how many doors the box has.

Each file has its own job, too.

File Role Which step produced it
tokenizer.json Vocabulary Built from the pre-training data
config.json Layers, dimensions, heads: the blueprint numbers Model design
onnx/model_*.onnx Weights + compute graph Pre-training (+ fine-tuning)

Numbers I measured (2026-09-19, macOS, Chrome, WebGPU shader-f16 available)

For Xenova/paraphrase-multilingual-MiniLM-L12-v2: model_fp16.onnx 235MB, model_quantized.onnx 118MB, tokenizer.json 17MB. Same weights, twice the size depending on packaging.

I embedded 372 document chunks with that MiniLM. WebGPU + fp16 took about 21 seconds, first download included. WASM + q8 ran single-threaded (crossOriginIsolated=false) and was around 25% after a minute. I didn't wait for it to finish.

A few more snags.

The npm package is heavy. Installing @huggingface/transformers 4.3.0 from npm pulls in onnxruntime-node (289MB in node_modules) and sharp. I only needed the browser, so I switched to a version-pinned import from a CDN (jsDelivr).

Files don't show up on disk by name. The browser stores downloads in Cache Storage under transformers-cache, keyed by request URL. Searching for config.json finds nothing.

So when you pick a model

If it has to run in the browser, first check the repo's Files tab for an onnx/ folder. No folder means you convert it yourself. The trainer and the packager can differ, so check the license and training data on the original repo's model card (nlptown/... in the example above).

If you're stuck deciding whether a model should run in the browser or on a server, or what to do when the model you need has no export, feel free to ask negabaro. We've run the experiment above ourselves and can look at it with you.

Key Concepts

1

The first part of a repo name may be the packager. Find the original model on the model card

2

For the browser, check the Files tab for an onnx/ folder first

3

Same weights, but fp16 vs q8 doubles the size. You trade it against speed and accuracy

4

If you need inner-layer values, choose the outputs at export time. Default exports only give the last layer

Use Cases

Embeddings and sentiment analysis in the browser with transformers.js + ONNX Running a model on a C# or Java server without Python, via ONNX Runtime
← Jev: The LLM That Doesn't Write, It Just Decides Don't Put PyTorch, Hugging Face and Ollama on the Same Shelf — AI Frameworks Come in Three Layers →