RESEARCH NOTES · SEPTEMBER 2026

Q-Omni: a model with no embedding table

A 53M-parameter language model whose weights only ever take three values, whose entire vocabulary is frozen before training starts, and whose arithmetic is computed, not learned.

53.2Mparameters
1.58-bitternary weights
33 MBpacked, lossless
98.7%exact circuit answers

Most language models spend a large slice of their parameters on two things that have nothing to do with language understanding: a giant lookup table mapping tokens to vectors, and weights precise enough to store sixteen bits of information each. Q-Omni is an experiment in refusing both — trained from scratch on a single consumer GPU.

Why spend nothing on the things you can compute for free

Ternary weights, trained that way from the first step

Q-Omni follows the BitNet b1.58 line of work: every attention and feed-forward matrix is quantized to {-1, 0, +1} on every forward pass during training, not compressed after the fact. Each row keeps one full-precision scale α (its mean absolute value); the forward pass computes clip(round(W/α), -1, 1) · α, with gradients flowing straight through the rounding step. The network only ever experiences the three-valued version of itself, so it never has to adapt to a distribution shift at deployment.

The measured cost. A controlled ablation — two identical models, one ternary and one fp16, same 0.3B-token slice, same schedule — puts the honest price of going ternary at this size at +18.1% perplexity (58.7 vs 49.7). Most published ternary results are reported at ≥1B parameters, where the cost is expected to shrink.

A vocabulary that costs nothing to store and nothing to train

Instead of a trained vocab_size × d_model embedding table, every token gets a fixed 512-bit code — a vector of ±1 values computed once, before training starts, from real co-occurrence statistics (PPMI-SVD, then thresholded to bits). Neither the input code nor the output code ever receives a gradient.

Not a random hash. Because the codes come from real corpus statistics, the frozen table has useful geometry before a single gradient step: the Hamming-nearest neighbours of the code for king are throne, kings, and prince.

Arithmetic that is computed, not approximated

When Q-Omni needs to compute something exact, it writes a short structured span like <CALC>347*86<EQ>, and a deterministic logits processor intercepts that span, evaluates it exactly, and forces the correct digits into the output. The model's only job is recognizing when to reach for the circuit — fourteen deterministic sub-programs cover arithmetic, dates, weekdays, unit conversion, comparison, sorting, counting, and more. Across 300 held-out questions, it writes the correct span 99.7% of the time and the final answer is exact 98.7% of the time.

The architecture

Seven decoder blocks between a frozen input table and a frozen readout — everything inside stays ternary. The dots below trace one token's forward pass; watch it loop.

token id frozen fingerprint table c_id  =  ±1 vector, 512 bits + trainable Δ image / audio ids W_in projection transformer block ×7 RMSNorm grouped-query attention 14 query heads · 2 kv heads · head dim 64 RoPE · per-head QK-norm · sigmoid gate Q / K / V / O projections: TernaryLinear + RMSNorm SiLU feed-forward 896 → 2464 → 896, two matrices both matrices: TernaryLinear + final RMSNorm readout (h·W_out) · c_v / √512 + frozen bias dot product against the same frozen table ordinary token sampled and emitted as-is <CALC> span? deterministic circuit → exact answer the model chooses which the circuit is never wrong

One vocabulary, three modalities

The same 512-bit fingerprint mechanism extends past text: 8,192 image codes (VQGAN) and 8,192 audio codes (four Mimi codebooks, frame-interleaved) live in the same id space as text — one model, one embedding mechanism, one readout head.

What broke. The first joint checkpoint's image-code loss was worse than random guessing — 11.07 nats against a chance floor of 9.01 — on training and held-out data. Diagnosis: the 64- and 256-dimensional image/audio codebooks have low effective rank once squeezed into 512-bit codes, so the frozen readout leaked 35–39% of its probability mass onto the wrong modality. The fix: a small trainable correction on non-text ids only — text stays frozen. Image loss dropped to 7.37, audio to 3.98, both now below chance.
Metricv1v2 (fixed)reference
Image-code loss (nats)11.077.37chance = 9.01
Audio-code loss (nats)6.943.98chance = 7.62
Speech recognition WER120%123% → 37.6%*100% = uninformative

* after fine-tuning on the semantic Mimi codebook alone — see below.

Results, honestly

CapabilityResultVerdict
Exact circuits (span / final answer)99.7% / 98.7%works
FineWeb-Edu holdout perplexity43.1fluent, not factual
ARC-Easy / PIQA (zero-shot)38.0% / 58.7%above chance
HellaSwag / ARC-C / OpenBookQA27.5% / 22.7% / 25.0%at chance
Speech recognition (semantic codebook)WER 123% → 37.6%improving
Ternary vs fp16 ablation+18.1% perplexitydisclosed cost
Image generation is not solved. Loss is below chance — real structure was learned — but sampled images decode to flat colour fields, not pictures. A capacity ceiling, too little image data, or a fundamental limit of frozen fingerprint codes for vision: unresolved, and we're still running the experiment that might tell us which.

33 MB, and it runs in a browser tab

Ternary weights pack losslessly at 1.6 bits/weight; the frozen table packs at 1 bit/value. The whole model — architecture, weights, tokenizer, self-test — fits into one 33 MB file, a format we call QPACK1, down from 230–263 MB of fp32 safetensors.

Checked three ways, not just claimed. Byte-exact round-trip packing (zero mismatched trits across 43.75M weights); three independent runtimes — PyTorch, a from-scratch NumPy port, and a hand-written WebAssembly/SIMD128 kernel — producing token-for-token identical greedy generations; and that WASM kernel running the real .qpack file client-side, no server, no Python, at roughly 32 tokens/second.
quickstart.py
from transformers import AutoModelForCausalLM, AutoTokenizer

tok = AutoTokenizer.from_pretrained("q-project/Q-Omni-53M-Experimental")
model = AutoModelForCausalLM.from_pretrained(
    "q-project/Q-Omni-53M-Experimental", trust_remote_code=True)

ids = tok("Once upon a time,", return_tensors="pt").input_ids
out = model.generate(ids, max_new_tokens=60, do_sample=True, top_k=40)
print(tok.decode(out[0], skip_special_tokens=True))

What this adds up to

Q-Omni spends its parameters carefully: nothing on an embedding table, nothing on relearned arithmetic, and a measured, disclosed 18% quantization tax for the smallest possible footprint. The same frozen-vocabulary idea reaches across modalities, with one real bug found and fixed, and at least one — image generation — still open. Every number on this page comes from a checkpoint that was actually run, evaluated, and, where a claim could be checked twice, checked twice.

Model card & weights Run it in your browser
Back toAll posts