|
| 1 | +# MOSS-Transcribe-Diarize |
| 2 | + |
| 3 | +OpenMOSS's [`OpenMOSS-Team/MOSS-Transcribe-Diarize`](https://huggingface.co/OpenMOSS-Team/MOSS-Transcribe-Diarize) |
| 4 | +ported to transcribe.cpp. A 0.9B audio-LLM: a 24-layer Whisper-Medium audio |
| 5 | +encoder (`d_model=1024`, GELU, LayerNorm) feeds a 4x temporal merge |
| 6 | +(1024 -> 4096) and a VQAdaptor MLP bridge into a Qwen3-0.6B causal decoder |
| 7 | +(28 layers, `hidden_size=1024`, GQA 16/8 heads, `rope_theta=1e6`) via |
| 8 | +audio-token injection at `<|audio_pad|>` positions. |
| 9 | + |
| 10 | +## What it's for |
| 11 | + |
| 12 | +Offline English and Chinese speech-to-text with speaker diarization. Takes a |
| 13 | +16 kHz mono WAV and produces transcript text in the canonical diarized format |
| 14 | +`[start][Sxx]text[end]` (e.g. `[0.48][S01]Welcome[1.66]`) via greedy decoding. |
| 15 | +The speaker tags (`[S01]`, `[S02]`, ...) and segment start/end timestamps are |
| 16 | +emergent generated text, not special tokens. Built for long-form, |
| 17 | +multi-speaker audio. No translation; not a streaming model. |
| 18 | + |
| 19 | +See OpenMOSS's [model card](https://huggingface.co/OpenMOSS-Team/MOSS-Transcribe-Diarize) |
| 20 | +for training data, intended use, and upstream evaluation. All of OpenMOSS's |
| 21 | +published metrics are Chinese multi-speaker diarization CER/cpCER; LibriSpeech |
| 22 | +test-clean is used here only as an English acceptance set. |
| 23 | + |
| 24 | +Licensed Apache-2.0. Ported from upstream commit |
| 25 | +[`d7231bb`](https://huggingface.co/OpenMOSS-Team/MOSS-Transcribe-Diarize/commit/d7231bbae2587a4af278735eb765b318c4f64edd), |
| 26 | +pinned 2026-07-12. |
| 27 | + |
| 28 | +## Memory and length |
| 29 | + |
| 30 | +MOSS keeps the whole recording in memory while it transcribes, so RAM use grows |
| 31 | +with how long the audio is. Plan for roughly **85 MB of extra memory per minute |
| 32 | +of audio**, on top of the model file itself — about **2.5 GB for a 30-minute |
| 33 | +clip** and **~5 GB for an hour**. It can handle recordings up to a couple of |
| 34 | +hours if you have the memory; anything longer is rejected with a clear error |
| 35 | +instead of being silently cut off. If you run low on memory, split long audio |
| 36 | +into shorter pieces. |
| 37 | + |
| 38 | +## Download |
| 39 | + |
| 40 | +| Quantization | Download | Size | WER (LibriSpeech test-clean) | |
| 41 | +| --- | --- | ---: | ---: | |
| 42 | +| BF16 | [MOSS-Transcribe-Diarize-BF16.gguf](https://huggingface.co/handy-computer/MOSS-Transcribe-Diarize-gguf/resolve/main/MOSS-Transcribe-Diarize-BF16.gguf) | 1.83 GB | 2.08% | |
| 43 | +| F16 | [MOSS-Transcribe-Diarize-F16.gguf](https://huggingface.co/handy-computer/MOSS-Transcribe-Diarize-gguf/resolve/main/MOSS-Transcribe-Diarize-F16.gguf) | 1.83 GB | 2.07% | |
| 44 | +| Q8_0 | [MOSS-Transcribe-Diarize-Q8_0.gguf](https://huggingface.co/handy-computer/MOSS-Transcribe-Diarize-gguf/resolve/main/MOSS-Transcribe-Diarize-Q8_0.gguf) | 987 MB | 1.93% | |
| 45 | +| Q6_K | [MOSS-Transcribe-Diarize-Q6_K.gguf](https://huggingface.co/handy-computer/MOSS-Transcribe-Diarize-gguf/resolve/main/MOSS-Transcribe-Diarize-Q6_K.gguf) | 768 MB | 1.96% | |
| 46 | +| Q5_K_M | [MOSS-Transcribe-Diarize-Q5_K_M.gguf](https://huggingface.co/handy-computer/MOSS-Transcribe-Diarize-gguf/resolve/main/MOSS-Transcribe-Diarize-Q5_K_M.gguf) | 700 MB | 1.99% | |
| 47 | +| Q4_K_M | [MOSS-Transcribe-Diarize-Q4_K_M.gguf](https://huggingface.co/handy-computer/MOSS-Transcribe-Diarize-gguf/resolve/main/MOSS-Transcribe-Diarize-Q4_K_M.gguf) | 617 MB | 2.59% | |
| 48 | + |
| 49 | +These WER values describe this dataset only, not a general quality ranking. A |
| 50 | +quant that scores slightly better here is not necessarily better in real-world |
| 51 | +use; dataset-specific decoding near-ties can make quantization noise help or |
| 52 | +hurt individual utterances. |
| 53 | + |
| 54 | +WER measured on the full LibriSpeech `test-clean` split (2620 utterances) with |
| 55 | +the Whisper-style English normalizer and jiwer 3.x. MOSS emits the diarized |
| 56 | +format `[start][Sxx]text[end]`; the bracket spans are metadata and are |
| 57 | +de-diarized to a space (for both hypothesis and reference) before scoring, |
| 58 | +matching the author-repo reference runner. The same-manifest MOSS author-repo |
| 59 | +reference (bf16, greedy) lands at **2.07%**, 95% bootstrap CI [1.82%, 2.40%]; |
| 60 | +the BF16 port lands at 2.08%, within `+0.01pp` of the reference and well inside |
| 61 | +the CI. Q4_K_M's higher 2.59% is not broad degradation but a handful of 4-bit |
| 62 | +tail failures (6 empty outputs, 5 English->Chinese language-drift utterances, |
| 63 | +1 timestamp-token repetition loop); prefer Q5_K_M or higher if those matter. |
| 64 | +Dediarization is scoring-only: the runtime continues to return the raw inline |
| 65 | +timestamps and speaker labels. |
| 66 | + |
| 67 | +## Quick Start |
| 68 | + |
| 69 | +```bash |
| 70 | +cmake -B build |
| 71 | +cmake --build build |
| 72 | + |
| 73 | +build/bin/transcribe-cli \ |
| 74 | + -m models/MOSS-Transcribe-Diarize/MOSS-Transcribe-Diarize-Q8_0.gguf \ |
| 75 | + samples/jfk.wav |
| 76 | +``` |
| 77 | + |
| 78 | +If your audio is not already 16 kHz mono WAV, convert it first: |
| 79 | + |
| 80 | +```bash |
| 81 | +ffmpeg -i input.mp3 -ar 16000 -ac 1 output.wav |
| 82 | +``` |
| 83 | + |
| 84 | +CLI flags: |
| 85 | + |
| 86 | +- `-l en` / `-l zh` (or omit for `auto`): English and Chinese are supported. |
| 87 | +- No `--task` / `--target-language`: the model is ASR-only, no translation. |
| 88 | +- Diarization tags and segment timestamps appear inline in the transcript |
| 89 | + text; there is no toggle to suppress them. |
| 90 | + |
| 91 | +## Performance |
| 92 | + |
| 93 | +Cells are wall-clock latency (mean over 3 iterations after 1 warmup), |
| 94 | +with speedup over realtime in parentheses. Units: `ms` below 1 s, `s` |
| 95 | +above (2 decimal places). |
| 96 | + |
| 97 | +### Apple M4 Max |
| 98 | + |
| 99 | +| Backend | Sample | Q8_0 | Q4_K_M | |
| 100 | +| ------- | ------------ | ------------: | ------------: | |
| 101 | +| Metal | jfk (11.0s) | 388 ms (28.3×) | 369 ms (29.8×) | |
| 102 | +| Metal | dots (35.3s) | 1.27 s (27.8×) | 1.17 s (30.1×) | |
| 103 | +| CPU | jfk (11.0s) | 2.06 s (5.3×) | 2.37 s (4.6×) | |
| 104 | +| CPU | dots (35.3s) | 5.71 s (6.2×) | 5.84 s (6.0×) | |
| 105 | + |
| 106 | +macOS 26.5.1, transcribe.cpp `e745720`. |
| 107 | + |
| 108 | +### AMD Ryzen 7 PRO 4750U |
| 109 | + |
| 110 | +| Backend | Sample | Q8_0 | Q4_K_M | |
| 111 | +| ------- | ------------ | ------------: | ------------: | |
| 112 | +| Vulkan | jfk (11.0s) | 3.88 s (2.8×) | 3.68 s (3.0×) | |
| 113 | +| Vulkan | dots (35.3s) | 11.38 s (3.1×) | 10.68 s (3.3×) | |
| 114 | +| CPU | jfk (11.0s) | 7.49 s (1.5×) | 7.06 s (1.6×) | |
| 115 | +| CPU | dots (35.3s) | 21.20 s (1.7×) | 19.22 s (1.8×) | |
| 116 | + |
| 117 | +Fedora Linux 43, transcribe.cpp `e745720`. Vulkan device: `AMD Radeon |
| 118 | +Graphics (RADV RENOIR)`. |
| 119 | + |
| 120 | +Benchmark reproduction: |
| 121 | + |
| 122 | +```bash |
| 123 | +uv run scripts/bench/run.py \ |
| 124 | + --models moss-transcribe-diarize \ |
| 125 | + --quants q8_0,q4_k_m \ |
| 126 | + --samples jfk,dots \ |
| 127 | + --backends metal,cpu,vulkan \ |
| 128 | + --iters 3 --warmup 1 \ |
| 129 | + --name moss-transcribe-diarize-publication |
| 130 | +``` |
| 131 | + |
| 132 | +## Numerical Validation |
| 133 | + |
| 134 | +transcribe.cpp is validated tensor-by-tensor against the MOSS author repo |
| 135 | +(`scripts/dump_reference_moss_author.py`, `trust_remote_code`) on |
| 136 | +`samples/jfk.wav` with the strict CPU backend. The reference runs BF16 (torch, |
| 137 | +eager attention); the C++ path dequantizes BF16 weights to F32 and computes in |
| 138 | +F32, so C++ is the *more* precise side and the residual gap is a constant |
| 139 | +~1-3% relative bf16-vs-f32 drift, not a bug. The transcript compare is |
| 140 | +`dediarized` (bracket metadata stripped to a space). Confirmed WER-neutral: on |
| 141 | +the first 100 test-clean utterances the C++ ref-dtype WER (1.40%) is |
| 142 | +bit-identical to the Oracle reference on the same subset (1.40%). Tolerances |
| 143 | +are pinned in `tests/tolerances/moss.json` with a `_comment` block naming the |
| 144 | +precision regime, the large-pre-normalization-activation maxes, and the encoder |
| 145 | +padding-trim contract. Last validated at commit |
| 146 | +[`3f5e15c`](https://github.com/handy-computer/transcribe.cpp/tree/3f5e15c). |
| 147 | + |
| 148 | +| Field | Value | |
| 149 | +| --- | --- | |
| 150 | +| Reference | MOSS author repo (`OpenMOSS-Team/MOSS-Transcribe-Diarize`) | |
| 151 | +| Dump script | `scripts/dump_reference_moss_author.py` | |
| 152 | +| Manifest | `tests/golden/moss/moss-transcribe-diarize.manifest.json` | |
| 153 | +| Tolerances | `tests/tolerances/moss.json` | |
| 154 | +| Command | `uv run scripts/validate.py all --family moss --variant moss-transcribe-diarize` | |
| 155 | + |
| 156 | +Selected tensors (observed on CPU, strict backend; see the tolerance file for |
| 157 | +budgets and per-tensor notes): |
| 158 | + |
| 159 | +| Tensor | Max abs diff | Mean abs diff | Notes | |
| 160 | +| --- | ---: | ---: | --- | |
| 161 | +| `enc.mel.in` | `9.872e-05` | `5.150e-06` | C++ MelFrontend vs reference feature extractor | |
| 162 | +| `enc.pos_add.out` | `5.000e-02` | `2.200e-03` | Encoder input + positional embedding | |
| 163 | +| `enc.block.0.out` | `1.200e-01` | `4.000e-03` | First Whisper encoder block | |
| 164 | +| `enc.block.23.out` | `3.100e+03` | `8.000e-02` | Pre-final-LN residual; ref \|max\| ~3.6e3, bf16 rel error dominates max_abs (renormalized by `enc.ln_post`) | |
| 165 | +| `enc.ln_post.out` | `1.300e+01` | `3.000e-03` | Encoder output LayerNorm | |
| 166 | +| `enc.merge.out` | `1.500e+00` | `3.000e-03` | 4x temporal merge | |
| 167 | +| `enc.adaptor.out` | `4.000e-01` | `1.200e-02` | VQAdaptor decoder handoff (rel_mean ~0.96%) | |
| 168 | +| `dec.audio_injected` | `4.000e-01` | `7.000e-03` | Audio tokens scattered into the prompt | |
| 169 | +| `dec.block.0.out` | `7.500e-01` | `9.500e-03` | First Qwen3 decoder block | |
| 170 | +| `dec.block.27.out` | `3.200e+02` | `2.600e-01` | Pre-final-RMSNorm residual (bf16 accumulation over 28 layers) | |
| 171 | +| `dec.out_before_head` | `8.500e+00` | `8.000e-02` | Pre-head hidden state | |
| 172 | +| `dec.logits_raw` | `4.800e-01` | `7.200e-02` | Prefill logits; argmax preserved (transcript exact) | |
| 173 | +| `dec.logits_raw.gen8` | `5.000e-01` | `6.500e-02` | Greedy step 8 logits (KV-cache decode coverage) | |
| 174 | +| `dec.token_emb` | `0.000e+00` | `0.000e+00` | Pure embedding lookup (pinned exact) | |
| 175 | + |
| 176 | +For the full porting writeup, see |
| 177 | +[`docs/porting/families/moss.md`](../porting/families/moss.md). |
| 178 | + |
| 179 | +## Reproduction |
| 180 | + |
| 181 | +### Convert |
| 182 | + |
| 183 | +```bash |
| 184 | +uv run --project scripts/envs/moss \ |
| 185 | + scripts/convert-moss.py OpenMOSS-Team/MOSS-Transcribe-Diarize \ |
| 186 | + --revision d7231bbae2587a4af278735eb765b318c4f64edd |
| 187 | +``` |
| 188 | + |
| 189 | +### Quantize |
| 190 | + |
| 191 | +```bash |
| 192 | +uv run scripts/quantize-all.py models/MOSS-Transcribe-Diarize/MOSS-Transcribe-Diarize-BF16.gguf |
| 193 | +``` |
| 194 | + |
| 195 | +### Validate |
| 196 | + |
| 197 | +```bash |
| 198 | +uv run scripts/validate.py all --family moss --variant moss-transcribe-diarize |
| 199 | +``` |
| 200 | + |
| 201 | +### Score WER |
| 202 | + |
| 203 | +```bash |
| 204 | +PRESET=BF16 |
| 205 | +uv run scripts/wer/run.py \ |
| 206 | + --model models/MOSS-Transcribe-Diarize/MOSS-Transcribe-Diarize-${PRESET}.gguf \ |
| 207 | + --manifest samples/wer/librispeech-test-clean.manifest.jsonl \ |
| 208 | + --out reports/wer/MOSS-Transcribe-Diarize-${PRESET}.librispeech-test-clean.jsonl |
| 209 | +uv run scripts/wer/score.py \ |
| 210 | + reports/wer/MOSS-Transcribe-Diarize-${PRESET}.librispeech-test-clean.jsonl \ |
| 211 | + --dediarize |
| 212 | +``` |
| 213 | + |
| 214 | +### Score WER against the MOSS author-repo reference |
| 215 | + |
| 216 | +```bash |
| 217 | +uv run --project scripts/envs/moss \ |
| 218 | + scripts/wer/run_reference_moss_author.py \ |
| 219 | + --model OpenMOSS-Team/MOSS-Transcribe-Diarize \ |
| 220 | + --revision d7231bbae2587a4af278735eb765b318c4f64edd \ |
| 221 | + --manifest samples/wer/librispeech-test-clean.manifest.jsonl \ |
| 222 | + --out reports/wer/moss-transcribe-diarize-REF.librispeech-test-clean.jsonl |
| 223 | +uv run scripts/wer/score.py \ |
| 224 | + reports/wer/moss-transcribe-diarize-REF.librispeech-test-clean.jsonl \ |
| 225 | + --dediarize |
| 226 | +``` |
0 commit comments