--- license: apache-2.0 language: - zh tags: - input-method - zhuyin - bopomofo - traditional-chinese - ternary - bitnet - gguf library_name: gguf pipeline_tag: token-classification --- # Sloth IME models — Zhuyin → Traditional Chinese, fully on-device **The model family behind Slothing (樹懶智慧輸入法)**, all trained from scratch: | file | role | size | |---|---|---| | `slothe-t-12m-256x12.gguf` (+ `12m/` fp32) | **conversion encoder — shipping default** | 9.65 MB | | `slothe-t-25m.gguf` (+ fp32 master) | conversion encoder — accuracy reference | 18 MB | | `pred_q35_60m-q4.gguf` (+ `pred_q35_60m/` fp32) | **next-word decoder v2.1** (neural 聯想, TW-chat register FT) | 46 MB | The sections below document the 25M reference encoder in depth; the 12M shipping encoder and the 60M decoder are covered in the latency/files sections. ## 60M next-word decoder (v2.1) Dense-Qwen3.5 (Gated DeltaNet + full attention every 4th layer), 16k word-piece vocab (next word = one forward, 8.5 ms/word on BOOX @4t). Trained on 6.1M zh-TW sentences, then register-fine-tuned on 149k PTT/Dcard chat sentences (`--init-from`, 2ep): | eval (n=3000) | v2 | **v2.1 (shipped)** | |---|---|---| | TW chat held-out | 10.9 / 21.2 | **18.3 / 31.2** | | fresh-C4 | 34.0 / 46.0 | 33.5 / 45.2 | > **Honesty note.** Earlier versions reported 47.3/75.8 — that held-out was saturated with > near-duplicates of the (small) training set and rewarded memorization; on genuinely fresh > text that model scored 4.6/12.0. All predictor numbers here use never-seen fresh corpora. > Reproduce: `predictor_qwen35.py` + `build_corpus_big.py` (in the GitHub repo) — see the > Reproducibility section of the [project README](https://github.com/vieenrose/sloth-zhuyin-linux#reproducibility). ## SlothE-T — Ternary Zhuyin → Traditional Chinese conversion encoder The conversion model behind **Slothing (懶音輸入法)**: a libchewing-free, on-device LLM-powered 注音 (Bopomofo) input method that does **免選字** (candidate-free) whole-sentence conversion — you type the phonetic keystream, it emits the sentence, with no candidate list to scroll. - **25M parameters**, ternary weights (W1.58A8), bidirectional encoder. - **~7 MB** ternary-packed GGUF (TQ2_0) · 99 MB fp32 master. - Runs on-device across four frontends: **fcitx5, IBus, Android, web**. ## What it does Given a sequence of Zhuyin **syllables** (the input keystream, e.g. `ㄋㄧˇ ㄏㄠˇ`), the model emits the **Traditional Chinese characters** for each position as an aligned sequence-labeling task. The output head is **phonetic-legality-masked**: at each position only the ~1–50 characters that are legal readings of that syllable are scored, out of the full 8342-char vocabulary. This is what replaces the traditional IME 選字 (candidate-selection) step. ## Architecture | | | |---|---| | encoder | bidirectional (BERT-like), 16 layers | | dim / ffn | 352 / 960 | | heads | 8 query, 2 KV (GQA), head_dim 44, QK-norm | | norm | RMSNorm, embed-norm, **SubLN** pre-norm before each ternary linear | | quantization | ternary weights {−1, 0, +1} × per-output-channel **absmedian** scale; **int8** activations; QAT via STE | | fp islands | boundary blocks (0 and 15) kept fp16 for stability (`fp_boundary=1`) | | vocab | 1539 syllables (in) · 8342 characters (out) | ## Evaluation — honest held-out Measured on **500 fresh zh-TW sentences** (C4-zh-TW, offset far past the training window, filtered to be **absent from the training corpus**), then g2pW-labeled. | metric | **this model** | 12M int8 (previous ship) | 32M fp teacher | |---|---|---|---| | **免選字** (whole-sentence exact) | **76 %** | 72 % | — | | **homophone-hard** | **86 %** | 82 % | 83 % | | **toneless** | **77 %** | 79 % | 81 % | > **Note on 免選字.** Earlier project numbers (~84 %) were inflated by a benchmark > leak — the reference set had been sampled from the training corpus, rewarding > memorization. **76 %** is the honest whole-sentence held-out figure. > *homophone-hard* and *toneless* are leak-clean throughout. On honest data this > 25M ternary model **beats the previously-shipped 12M int8 on 免選字 and > homophone-hard** while being smaller and faster. ### Quality vs. latency ![Held-out 免選字 vs. on-device latency](score_vs_latency.png) Latency is per-6-syllable decode on a **BOOX (Snapdragon 662, no-dotprod ARM)**. 4M and 12M are measured (ORT int8). **Update (2026-07-17): the 25M ternary's ~9 ms was a projection that real-device measurement later corrected** — measured on the BOOX (ggml/libslothe, single 6-syllable forward): **18.5 ms at 4 threads / 31.9 ms at 2 threads** (the projection missed that the per-kernel TQ2_0 speedup does not compound at full-model scale on a no-dotprod core, and an early 8-thread default dragged the A53 little cores — fixed). The Pareto story survives in a different form: a **12M ternary sibling (dim 256×12 layers, zero TQ2_0 padding tax, in this repo)** measures **9.3 ms @4t / 15.8 ms @2t** on the same device at 84 % 免選字 / 84 % homophone — the honest heir to the "~9 ms" claim. | model | params | 免選字 | homophone | toneless | latency (BOOX) | |---|---|---|---|---|---| | 4M int8 | 4M | 70 % | 83 % | 74 % | 9.1 ms | | 12M int8 | 12M | 72 % | 82 % | 79 % | 13.3 ms | | **25M ternary** | **25M** | **76 %** | **86 %** | **77 %** | **18.5 ms\*** | | **12M ternary 256×12** | **12M** | **84 %†** | **84 %†** | — | **9.3 ms\*** | \* measured on-device (BOOX SD662, ggml/TQ2_0, 4 threads; the 25M's earlier "~9 ms" was a projection — see the note above). † 230-sentence reference set (the 25M measures 85.7 % on that same set on-device); the 免選字 columns above it use the 500-sentence held-out set. ## Training recipe (teacher-free) - **Direct cross-entropy** on a g2pW-labeled zh-TW corpus, with **label smoothing 0.1**. - **Long OneCycleLR schedule (32 epochs), early-stopped at the peak (epoch 24)** — the model overfits after (73 % 免選字 by ep32). - **8-adjacency keyboard-error noise** (TAAI-2024 error model): simulated mis-keys are constrained to the QWERTY physical 8-neighbourhood rather than any edit-distance-1 syllable. - DDP on 2× RTX 5090. Knowledge distillation from a 32M teacher was tried and only **matched** (never beat) this teacher-free recipe, so the teacher was dropped — simpler and ~2.4× faster to train. The two levers that carry the quality are (a) label smoothing (a teacher-free regularizer; +5 免選字 over plain CE) and (b) enough epochs to exploit the no-overfit headroom. ## Deployment / inference The ternary weights are shipped as a **GGUF** using ggml's mainline **TQ2_0** ternary type, so the model runs on stock llama.cpp CPU kernels (ARM NEON, x86 AVX2, and the generic/WASM path). Measured `vec_dot` throughput on x86 AVX2: **TQ2_0 ≈ 2.3× int8 (Q8_0)** — the fastest quant type in the table — and ~2× on a no-dotprod ARM (Snapdragon 662). The ternary win is partly memory-bandwidth (2 bits/weight = 4× fewer weight bytes), so it holds on both weak and accelerator-equipped CPUs. > **256-alignment note.** TQ2_0 uses 256-element blocks, so the model's > in-features (352, 960) are zero-padded up to the next multiple of 256 in the > GGUF (352 → 512, 960 → 1024). The padding is exact ternary-zero (loss-free) but > costs some MACs; a future model with 256-aligned dims removes the tax. ## Files | file | what | |---|---| | `slothe-t-25m.gguf` | 25M ternary GGUF — TQ2_0 blocks (layers 1–14) + fp16 islands/embed/head | | `slothe-t-12m-256x12.gguf` | **12M ternary GGUF (shipping default)** — dim256×12, zero TQ2_0 padding tax, 9.65 MB | | `12m/model.safetensors` | 12M fp32 master weights | | `12m/slothe_config.json` / `12m/roles.json` | 12M config + ternary/fp tensor roles | | `pred_q35_60m-q4.gguf` | 60M Q4 next-word predictor **v2.1** (qwen35 GDN hybrid; llama.cpp-loadable; chat-register FT) | | `model.safetensors` | fp32 master weights (HF-native, non-pickle) | | `slothe.pt` | fp32 master checkpoint with embedded `config` | | `syl_vocab.json` | syllable tokenizer (input vocab) | | `syl2legal.npz` | phonetic legality mask, 1539 syllables × 8342 chars (bool) | | `train_slothe_ternary.py` | training script | | `gate_slothe_ternary.py` | evaluation / gating script | | `REPRODUCE.md` | end-to-end reproduction recipe | | `NAMES.md` | GGUF tensor-name ↔ checkpoint-name map | ## Intended use & limits - **Intended:** on-device Traditional-Chinese Bopomofo input (Taiwan readings). - **Out of scope:** Simplified Chinese, mainland pinyin readings, free-form generation. The model only scores per-position legal characters — it is a converter, not a chat model. - The whole-sentence (免選字) metric is honest-held-out and modest (76 %) by design: it is measured on unseen sentences, not the training distribution. ## Citation / related work The keyboard-error model and the 免選字-vs-選字 framing are discussed in the project's `docs/RELATED-WORK.md`, which positions this work against the TAAI-2024 cross-multi-IME system (李偉安, 葉展維, 張嘉惠, National Central University).