Views
No views yet
.tflite) exports of Qwen/Qwen3-Embedding-0.6B
for on-device inference on Android (XNNPACK CPU and OpenCL GPU delegates)
and other LiteRT-compatible runtimes.input_ids + attention_mask; the
graph returns the L2-normalized 1024-dim sentence embedding directly.| seq_len | quant | size | file | intended backend |
|---|---|---|---|---|
| 512 | dynamic_int8 | 603 MB | qwen3-embedding-0.6b_seq512_int8.tflite | LiteRT CPU (XNNPACK) |
| 2048 | dynamic_int8 | 603 MB | qwen3-embedding-0.6b_seq2048_int8.tflite | LiteRT CPU (XNNPACK) |
| 8192 | dynamic_int8 | 603 MB | qwen3-embedding-0.6b_seq8192_int8.tflite | LiteRT CPU (XNNPACK) |
| 512 | fp16 | 1.19 GB | qwen3-embedding-0.6b_seq512_fp16.tflite | LiteRT GPU (OpenCL) |
| 2048 | fp16 | 1.19 GB | qwen3-embedding-0.6b_seq2048_fp16.tflite | LiteRT GPU (OpenCL) |
| 8192 | fp16 | 1.19 GB | qwen3-embedding-0.6b_seq8192_fp16.tflite | LiteRT GPU (OpenCL) |
seq_len is baked into each graph — if you need a different length you
will have to either pad shorter inputs up to the closest larger variant
(wastes compute but always correct), or re-run the converter (see
"Provenance" below).dynamic_int8 = weight-quantized int8 matmuls via XNNPACK (best on ARM
CPUs). fp16 = half-precision throughout (best on mobile GPUs that
dispatch fp16 kernels).SentenceTransformer("Qwen/Qwen3-Embedding-0.6B")
loaded in fp32, over a 6-string test suite (English + Japanese + near-pair
sentences like "cats are better pets than dogs" vs. "dogs are better pets
than cats"):| artifact | mean cosine | min cosine | threshold |
|---|---|---|---|
..._seq512_int8.tflite | (≥ 0.99) | ≥ 0.98 | 0.98 |
..._seq512_fp16.tflite | (≈ 1.000) | ≥ 0.999 | 0.999 |
Qwen3ForCausalLM architecture,
per arxiv:2506.05176v3: "we utilize
LLMs with causal attention, appending an [EOS] token at the end of the
input sequence"). Consequences baked into the exported graph:attention_mask.flip(1).max(1) to locate the last real-token index),
which is correct under left-, right-, or mixed-padding.input_ids: [1, seq_len] int64attention_mask: [1, seq_len] int64 (1 for real tokens, 0 for
padding)[1, 1024] float32[EOS]
token appended to every input. If your tokenizer path doesn't
auto-append, do it manually before padding — eos_token_id=151645
(<|im_end|> per tokenizer_config.json). The reference code in the
upstream HF model card appends manually; sentence_transformers's
default Transformer.tokenize does not.Instruct: Given a web search query, retrieve relevant passages that answer the query
Query: <your query>config_sentence_transformers.json declares this as the query prompt.
Document-side inputs go in unmodified.1import numpy as np
2import torch
3from transformers import AutoTokenizer
4from ai_edge_litert.interpreter import Interpreter
5
6SEQ_LEN = 512
7tok = AutoTokenizer.from_pretrained("ckg/qwen3-embedding-0.6b-litert",
8 padding_side="left")
9eos_id = tok.eos_token_id # 151645 = <|im_end|>
10
11interp = Interpreter(model_path=f"qwen3-embedding-0.6b_seq{SEQ_LEN}_int8.tflite")
12interp.allocate_tensors()
13in_details = interp.get_input_details()
14out_details = interp.get_output_details()
15
16def embed(text: str) -> np.ndarray:
17 enc = tok(text, padding="max_length", truncation=True,
18 max_length=SEQ_LEN, return_tensors="pt")
19 ids = enc["input_ids"][0].tolist()
20 if ids[-1] != eos_id:
21 # Append EOS + re-pad; simplest form shown.
22 ids = ids[:-1] + [eos_id] if enc["attention_mask"].sum() == SEQ_LEN \
23 else ids + [eos_id]
24 ids = ids[:SEQ_LEN]
25 input_ids = torch.tensor([ids], dtype=torch.int64).numpy()
26 attn = (input_ids != tok.pad_token_id).astype(np.int64)
27
28 interp.set_tensor(in_details[0]["index"], input_ids)
29 interp.set_tensor(in_details[1]["index"], attn)
30 interp.invoke()
31 return interp.get_tensor(out_details[0]["index"])[0] # [1024]
32
33q = embed("Instruct: Given a web search query, retrieve relevant passages that answer the query\nQuery: what is a supernova?")
34d = embed("A supernova is the bright explosion of a massive star at the end of its life.")
35cos = float(q @ d / (np.linalg.norm(q) * np.linalg.norm(d)))
36print(f"cosine = {cos:.4f}")ai-edge-torch) from the
upstream Qwen/Qwen3-Embedding-0.6B checkpoint.Qwen/Qwen3-Embedding-0.6B (595,776,512 parameters, 28
layers × 16 heads × 128 head_dim, 1024 hidden, 3072 intermediate, GQA
2:1, RoPE theta 1e6, tie_word_embeddings=true, vocab 151669)convert_qwen3_embedding.py from
wafer-systems/project-switchboard
(the on-device/conversion/ directory on the gemma4-ondevice-20260410 branch)litert-torch version: 0.8.0litert-torch's Qwen3
chat example with three embedding-specific overrides:vocab_size=151669 (chat variant's 151936 was hard-coded);model. prefix (the embedding
checkpoint saves Qwen3Model directly, not Qwen3ForCausalLM);lm_head=None in the tensor-names mapping (tied to tok_embedding
via tie_word_embeddings=true).Qwen/Qwen3-Embedding-0.6B model
card. No modifications were made to model weights — these artifacts are
bit-exact re-encodings of the upstream checkpoint for a different runtime
(LiteRT vs. HuggingFace Transformers).