Issues / #438

#438 CJK output is systematically wrong at IQ1_M: 0/10 correct on Chinese, 10/10 on English (greedy)

closed · @lybhb8 · 3 comments · View on GitHub

BenchmarksSetup & installServer & APIAMD / HIPNVIDIA / CUDAModels & quantsDocumentation

Description

**Affected:** Engine 0.1.30 (`2a57d8d`), native i-quant expert packs, HIP/gfx1100 build
(ROCm 10.0). Model: `Qwen3.8-Flash-Next-GSQ-RCO-IQ1_M` (the `Coder` GGUF, IQ1_M = 1-bit).
Reported against the Coder repo's only published quant.

At temperature 0, Chinese output is **never** correct on this box. It either degrades into a
single-token loop or drops a character. English is unaffected. This is reproducible on every
run and is not a sampling artifact.

### Reproduction

Server: `serve.server --engine strata` with the standard Coder config
(`--expert-cache auto --mmap-experts --mtp rt --spec 4 --spec-min-p 0.5 --prefill auto
--max-context 65536 --kv int8 --vision`), reasoning off.

Prompt: `用一句话说明你是哪个模型,跑在什么硬件上。`

Two distinct failure modes, both wrong:

    我是通(通通通通通通通通通通通通通通…      finish_reason=length   ← degenerate, single-token loop
    我是通千(Qwen),由阿里巴巴通千团队开发     ← no loop, but 「通义千问」 lost the 「义」

The second one is the more informative case: the answer is *structurally* correct and the
model clearly wants to say 通义千问, but it emits 通千. The character 义 is not merely
misordered — it is absent.

### Quantified

Same prompt, `temperature: 0`, `reasoning_effort: none`, n=10 per language:

| language | correct | wrong-but-fluent | degenerate loop |
|---|---:|---:|---:|
| English (equivalent question) | **10** | 0 | 0 |
| Chinese | **0** | 5 | 5 |

English answers are byte-identical across all 10 runs. Chinese is 0-for-10 correct.

### What I ruled out (each by ablation, not by reasoning)

| variable | experiment | result |
|---|---|---|
| suffix (prompt-lookup) drafter | `--suffix-draft 0` | still degenerates |
| verify window width | `--spec 2` vs `4` | still degenerates |
| speculative accept path | engine reports 63/71 and 48/48 drafts accepted | accept logic is correct — it only commits tokens the target verified (`generate.cpp:4761`) |
| GPU vs CPU expert rounding | `--expert-cache` 1500 → 9019 slots (hit 62% → 97%) | degenerates at every size |
| KV quantization | `--kv fp16`, `--kv k8v4`, full-VRAM config | still degenerates |
| sampling | `temperature: 0` (greedy) | still degenerates |
| repetition penalties | `penalty_repeat` 1.15/1.2, `penalty_freq`, `penalty_present`, with `penalty_last_n` 64/256 | no effect; does not escape even at 300 tokens |
| draft-independence switch | `STRATA_IQ_MT_MIN=1` (which 0.1.30 setup now requires, #152) | 5/5 still degenerate |
| run-to-run variance | 6 consecutive identical greedy requests | 6/6 degenerate (stable) |

**A caution about single-sample conclusions:** one `--kv fp16` run came back clean and looked
like the root cause. Re-running that exact config gave 3 clean / 1 degenerate over 4 runs, then
5 wrong / 5 degenerate over 10. The clean run was luck. fp16 only shifts the failure mix; it
fixes nothing.

### Observations that may be relevant

1. **The failure is CJK-specific.** English is unaffected, which argues against a general
   numerical problem.
2. **The pack's `vocab.json` contains no merged CJK tokens at all** — `通`, `千`, `义` are not
   keys in the 248,320-entry vocab, and zero entries contain a CJK character. There are 66,744
   byte-fallback entries. CJK is byte-mapped, consistent with #268's finding that the
   tokenizer byte-maps each CJK character to 1–3 byte-chars.
3. **IQ1_M is 1-bit** (`vec_dot_iq1_m_q8_1`, `src/kernels/cuda/iq_kernels.cu:223`). Combined
   with (2), a Han character costs 3 tokens of a 1-bit budget, versus 1 token for an English
   word — plausibly a large effective precision deficit exactly where the failures land.
4. **`draft_vocab.bin` is absent from `mtp/rt/`** on this install (only `dense.bin`,
   `dense.txt`, `experts.bin`), and this build's `--help` has no `--draft-vocab` option, so the
   CJK draft subset described in `docs/DETAILS.md:82` is not present here. Per #137 a *missing*
   `draft_vocab.bin` means the full native head, which was measured as **faster**, so I do not
   think this causes the quality failure — but it means the documented CJK drafting path does
   not exist in this build.

### Why this was not caught by the test suite

`grep -rlP '[\x{4e00}-\x{9fff}]' bench/ tests/` returns **nothing** — no file under `bench/` or
`tests/` contains a Chinese character. Every quality result in the repo rests on English or
synthetic prompts.

Related: `bench/results/2026-09-27-kv-q4/README.md` concludes "int8 is indistinguishable from
fp16" from mean KL and perplexity on English code/document/chat text. A mean-based metric
cannot catch this class of failure: the flip happens at one near-tied token, the mean barely
moves, but greedy decoding locks in permanently.

### Questions

1. Is a 1-bit quant expected to support CJK at all, or is IQ1_M intended as English/code-only?
   `docs/DETAILS.md` advertises CJK draft-vocab support for 15–38% faster CJK answers, which
   implies CJK is meant to work.
2. Is there a known-good (non-1-bit) configuration on this model that produces correct Chinese?
   The `Coder` HF repo publishes only IQ1_M; IQ3_XXS / Q2_0 exist in the separate
   `…-GSQ-RCO-GGUF` repo, which is a different fine-tune, so I could not run a clean
   quant-only ablation.
3. Would a CJK prompt set in `bench/` (even a small one) be accepted? It looks like the gap
   that let this through.

### Environment

- RX 7900 XTX (gfx1100), ROCm 10.0, engine built locally with hipcc
- Xeon E5-2696 v4 (AVX2, no AVX-512), 62 GB RAM
- Engine 0.1.30, commit `2a57d8d`

### Related / not the same as

#137 (missing `draft_vocab.bin` collapses CJK *draft acceptance* — a speed issue; a *missing* file means
the full native head, measured faster there) and #268 (tokenizer byte-maps CJK 3x, consistent with (2)
above). #152 is closed and covers CPU expert arithmetic varying with verifier width: with
`STRATA_IQ_MT_MIN=1` on, and at `--spec 2`, and across every expert-cache size, CJK output is still wrong
5/5 — so I do not think #152 is the cause.

Related on this site

Quick links to related pages.