Moravian AI/ThinEmbed: compressed query encodersENCS

ThinEmbed: compressed query encoders

The query side of an embedding model, compressed into a small file that runs in the browser on WebGPU. The document index stays exactly as it is; only the query encoder changes. Every number below compares a compressed client with the full-precision model on the same queries, against the model's own fp32 index.

Size, quality and speed in the browser

harrier-0.6b on SciFact, test split, 300 queries; nDCG@10 against the model's own fp32 index. One idle laptop, Intel integrated GPU, headless Chrome; latency per query; peak memory of all Chrome processes. The ThinEmbed rows are the released files.

query encoderfilenDCG@10p50p95loadpeak memory
harrier-0.6b, full precision (reference, not run in the browser)1 143 MiB (fp16)0.7559––––
Q3_K, llama.cpp format, wllama on WebGPU235.1 MiB0.7481444 ms754 ms4.2 s2 080 MiB
IQ2_M, llama.cpp format, wllama on WebGPU199.3 MiB0.7387508 ms1 012 ms5.2 s1 983 MiB
Q2_K, llama.cpp format, wllama on WebGPU192.4 MiB0.7413405 ms680 ms4.7 s2 124 MiB
Q2_K, llama.cpp format, wllama on the CPU (WebAssembly, 8 threads)192.4 MiB0.74251 669 ms3 363 ms4.4 s1 228 MiB
ThinEmbed, 2.10 bits per weight, WebGPU119.5 MiB0.7375103 ms113 ms2.4 s1 592 MiB
ThinEmbed, 1.83 bits per weight, WebGPU105.3 MiB0.7109102 ms112 ms2.5 s1 501 MiB

The Q3_K and Q2_K files are our GPTQ exports onto llama.cpp's formats; llama.cpp's own quantiser reaches the same quality at that size. One device, one model, one corpus: discrete GPUs and phones are not measured. Source: vq_results.md §2.

The smallest files against other approaches

harrier-0.6b on SciFact, test split, 300 queries, fp32 0.7559. Native: llama.cpp on the CPU. Browser: our WebGPU runtime. Simulation: torch simulation of the quantised weights.

query encoderfilenDCG@10measured
Q3_K, our GPTQ export, generic English calibration (released)235.1 MiB0.7475native
IQ2_M, llama.cpp quantiser, domain imatrix199.3 MiB0.7418native
Q2_K, our GPTQ export, SciFact-corpus calibration192.4 MiB0.7440native
IQ2_XS, llama.cpp's smallest format, domain imatrix177.5 MiB0.7097native
BitNet-embedding-270m, ternary, trained by Microsoft (a different model with its own index)140 MiB0.7350native
ThinEmbed, 2.10 bits per weight (released)119.5 MiB0.7375browser; simulation 0.7380
ThinEmbed, 1.83 bits per weight (released)105.3 MiB0.7109browser = simulation
ThinEmbed, 1.58 bits per weight (not released)~91 MiB0.7074simulation; ±0.03 across rotation seeds

Paired against BitNet-270m at 2.12 bits per weight: SciFact +0.007 [−0.014; +0.028] and NFCorpus −0.005 [−0.020; +0.009], both indistinguishable; ArguAna +0.044 [+0.023; +0.065]. Sources: vq_results.md §1, model card of ThinEmbed-harrier-0.6b.

Czech: WebFAQ-cs

Qwen3-Embedding-0.6B on WebFAQ-cs (PaDaS-Lab/webfaq-retrieval, CC BY 4.0): 71 529 passages, 7 231 human-judged test questions; nDCG@10 against the model's own fp32 index.

query encoderfilenDCG@10cosine to fp32top-10 overlapp50
Qwen3-Embedding-0.6B, full precision (reference)–0.7308–––
ThinEmbed, 3.57 bits per weight (2-d codebooks) and the full token table (released)237.0 MiB0.70720.9460.755112 ms

ThinEmbed row: torch simulation of the file on the full test split. In the browser (Intel integrated GPU) the file reproduces the simulation on the first 50 questions to the fourth decimal (0.6393 against 0.6393); p95 130 ms, load 2.5 s. Calibration on 3 000 WebFAQ-cs training questions, one seed. The released scalar Czech files are measured on a Czech supreme-court index instead (see the released files). Source: model card of ThinEmbed-Qwen3-Embedding-0.6B-vq.

Released files

Each file is a query encoder for exactly one document encoder and its settings; the index does not change. Quality in % of the model's own fp32 nDCG@10, test splits unless stated.

repositorybase model · licencefilesmeasured
ThinEmbed-harrier-0.6bmicrosoft/harrier-oss-v1-0.6b · MITQ3_K, generic English calibration, 235.1 MiBSciFact 98.9 · NFCorpus 99.3 · ArguAna 101.3 · SciDocs 99.2 %
ThinEmbed-harrier-0.6bMITQ2_K, SciDocs synthetic queries, 192.4 MiBSciDocs 94.5 %
ThinEmbed-harrier-0.6bMITQ2_K, generic English calibration, 192.4 MiBSciFact 96.4 · SciDocs 90.1 %
ThinEmbed-harrier-0.6b-vqMIT2.10 / 1.83 bits per weight, SciDocs files 127.0 / 112.8 MiB, SciFact files 119.5 / 105.3 MiBSciDocs 94.7 / 91.3 % · SciFact 97.6 / 94.0 % (browser)
ThinEmbed-harrier-0.6b-vqMITprompt-K/V variants, 2.10 / 1.83 bits per weight, SciDocs 129.1 / 114.9 MiB, SciFact 121.6 / 107.3 MiBSciDocs 96.6 / 93.8 % · SciFact, all 1 109 queries, 98.1 / 95.8 % (with the stored prompt keys and values)
ThinEmbed-Qwen3-Embedding-0.6B-vqQwen/Qwen3-Embedding-0.6B · Apache-2.03.57 bits per weight, Czech, 237.0 MiBWebFAQ-cs 96.8 % (browser-verified)
ThinEmbed-Qwen3-Embedding-0.6BApache-2.0Q4_K_M / Q5_K_M, English calibration, 339.9 / 385.4 MiBSciFact 100.0 / 99.8 · NFCorpus 99.4 / 100.1 · ArguAna 100.1 / 100.1 · SciDocs 99.3 / 100.1 %
ThinEmbed-Qwen3-Embedding-0.6BApache-2.0Q4_K_M / Q5_K_M, Czech calibration, 339.9 / 385.4 MiBCzech supreme-court index 98.7 / 99.5 %
ThinEmbed-bge-m3BAAI/bge-m3 · MITQ4_K_M / Q3_K, Czech calibration, 354.6 / 320.6 MiBCzech supreme-court index 99.2 / 98.3 % · SciFact 99.6 / 100.0 %

The Czech supreme-court index: 55 071 segments, 1 137 synthetic queries; the data are not public. Each repository's model card carries the compatibility, the recipe and the sha256 of every file. Sources: the model cards in docs/release of the repository.

How it was measured

Test split and the model's own fp32 index, in native llama.cpp or in the browser. Every run is pre-registered with a prediction and a kill rule, comparisons change one thing at a time, and differences are paired bootstrap intervals over queries (10 000 draws), read against the variance of the calibration draw (about 0.01 nDCG@10) and between machines (±0.006).

Quickstart

In the browser with the npm package @moravian/thinembed-vq. Needs WebGPU with shader-f16 (Chrome, Edge); there is no CPU fallback.

import { loadVqwClient } from 'https://moravian.ai/vqweb/vqweb.js';   // or: npm install @moravian/thinembed-vq

const client = await loadVqwClient(
  'https://huggingface.co/moravianai/ThinEmbed-Qwen3-Embedding-0.6B-vq/resolve/main/qwen3-0.6b-vq3.5d2-webfaq_q-vocfull.vqw');
const q = await client.embed('Kolik stojí parkování v centru Prahy?');   // Float32Array(1024), L2-normalised
// score = dot(q, d) against your Qwen/Qwen3-Embedding-0.6B document vectors

Measuring a client on your own index with the PyPI package thinembed; the document vectors you already have are never re-encoded:

pip install "thinembed[torch]"

eq import  --name mycorpus --docs corpus.jsonl --doc-emb index.f32 --doc-ids ids.json --teacher qwen3-0.6b --language cs
eq synth   --dataset mycorpus --lang Czech --n-per-doc 2 --as-calib     # synthetic queries + calibration text
eq cache   --dataset mycorpus --teacher qwen3-0.6b                      # fp32 query vectors of the unchanged model
eq compile --model model-f16.gguf --types Q4_K_M Q3_K --calib data/calib/mycorpus_synth_q.txt --table q4_0
eq eval    --dataset mycorpus --teacher qwen3-0.6b --clients models/*.gguf --bin llama-embedding --rule
eq compare results/perq/mycorpus_<a>.npz results/perq/mycorpus_<b>.npz  # paired bootstrap, same queries