Instructions to use litert-community/SenseVoiceSmall with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- LiteRT
How to use litert-community/SenseVoiceSmall with LiteRT:
# No code snippets available yet for this library. # To use this model, check the repository files and the library's documentation. # Want to help? PRs adding snippets are welcome at: # https://github.com/huggingface/huggingface.js
- Notebooks
- Google Colab
- Kaggle
File size: 6,777 Bytes
17eeff4 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | ---
license: other
license_name: funasr-model-license
license_link: https://github.com/modelscope/FunASR/blob/main/MODEL_LICENSE
base_model:
- FunAudioLLM/SenseVoiceSmall
language:
- ko
- en
- zh
- ja
- yue
tags:
- litert
- tflite
- sensevoice
- asr
- quantized
- korean
- xnnpack
pipeline_tag: automatic-speech-recognition
---
# SenseVoice-Small — fixed-shape LiteRT (TFLite) buckets, int8
[FunAudioLLM/SenseVoiceSmall](https://huggingface.co/FunAudioLLM/SenseVoiceSmall)
(~234 M params, non-autoregressive SAN-M encoder + CTC) converted to LiteRT for
on-device use, unchanged weights. One forward pass per utterance — no decoder loop.
## What is different from the upstream ONNX
- **Static shapes, one graph per window** (3 / 5 / 10 / 30 s). A dynamic-shape graph keeps
the XNNPACK delegate from attaching on LiteRT; these graphs run 10× (fp32) to 25× (i8)
faster with XNNPACK than on the built-in kernels.
- **Padding is masked, not heard.** `speech_lengths` stays a graph input: pad the features
of a shorter clip with zeros up to the bucket and feed the true length. Measured: padded +
true length is bit-identical to the unpadded graph (max |Δlogit| 0.0000). Feeding padded
*audio* without the length instead wrecks it (real-command CER 0.009 → 0.44 at a 5 s pad).
- **Weights int8.** `w8` = `weight_only_wi8_afp32` (activations fp32), `i8` =
`dynamic_wi8_afp32` (activations quantized per call — faster, slightly worse in noise).
## Files
| File | Size | Window | `speech` input | Note |
| --- | ---: | --- | --- | --- |
| `sensevoice_small_3s_w8.tflite` / `_i8` | 231 MB | 3 s | `[1, 560, 50]` | short commands |
| `sensevoice_small_5s_w8.tflite` / `_i8` | 231 MB | 5 s | `[1, 560, 84]` | **voice commands, recommended** |
| `sensevoice_small_10s_w8.tflite` / `_i8` | 231 MB | 10 s | `[1, 560, 167]` | sentences |
| `sensevoice_small_30s_w8.tflite` / `_i8` | 231 MB | 30 s | `[1, 560, 500]` | dictation |
| `am.mvn` | 11 KB | | | CMVN (AddShift + Rescale, 560 each) |
| `chn_jpn_yue_eng_ko_spectok.bpe.model` | 0.4 MB | | | SentencePiece, 25055 pieces |
| `tokens.txt` | 0.3 MB | | | `id<TAB>piece`, same vocab, for runtimes without SentencePiece |
| `config.yaml` | | | | upstream config (front-end parameters) |
| `SHA256SUMS` | | | | |
Load only the bucket(s) you need — every graph carries the full weights.
## REQUIRED integration notes (tensor contract)
Bind inputs **by name** (signature `serving_default`; the converter orders them alphabetically).
| Tensor | Shape | Type | Value |
| --- | --- | --- | --- |
| `speech` | `[1, 560, N]` | f32 | LFR+CMVN features, **feature-major** (transpose of the usual `[T, 560]`), zero-padded to N |
| `speech_lengths` | `[1]` | i32 | true number of LFR frames (≤ N) |
| `language` | `[1]` | i32 | query id: auto 0, zh 3, en 4, yue 7, ja 11, **ko 12**, nospeech 13 |
| `textnorm` | `[1]` | i32 | `withitn` 14 (punctuation + digits), `woitn` 15 |
| `ctc_logits` (out) | `[1, N+4, 25055]` | f32 | keep the first `speech_lengths + 4` rows |
| `encoder_out_lens` (out) | `[1]` | i32 | = `speech_lengths + 4` |
Front end (must match exactly — this is funasr `WavFrontend`):
1. 16 kHz mono, waveform × 32768.
2. Kaldi fbank (`torchaudio.compliance.kaldi.fbank`): 80 mel bins, 25 ms window / 10 ms
shift, Hamming, `snip_edges=True`, `energy_floor=0`, **dither 0** (funasr defaults to 1.0,
which makes runs non-deterministic); Kaldi defaults otherwise (pre-emphasis 0.97, DC removal).
3. LFR m=7, n=6: prepend 3 copies of the first frame, stack 7 frames every 6, pad the tail
with the last frame (→ 560-dim, 60 ms per frame, `ceil(T/6)` frames).
4. CMVN from `am.mvn`: `(x + shift) * scale`.
Decode: greedy CTC — argmax per row, collapse repeats, drop id 0 (blank), map ids through
`tokens.txt` (`▁` = space). The first four emitted tokens are tags
(`<|ko|>` `<|NEUTRAL|>` `<|Speech|>` `<|woitn|>`); strip `<|…|>`.
## ⚠️ Evaluation caveats — read before using the numbers
- `gate40` is the 40-clip **TTS-synthesized** Korean short-command holdout used to rank this
project's Whisper ACFT tiers. It is valid for **ranking only** (Spearman ρ = 1.00 against
real recordings) and runs ~2.8× harsher than real audio in absolute CER.
- References average 3.6 characters: one wrong syllable is CER 0.25–0.33.
- Zeroth-Korean test sentences also appear in Zeroth train (456 / 457), and SenseVoice's
training data is undisclosed — treat the Zeroth number as optimistic.
- CER = Levenshtein on the string with spaces removed and punctuation stripped (what a
command matcher sees), `woitn`, language forced to `ko`.
## Validation (desktop, Windows x86-64 CPU, LiteRT 2.1.6, XNNPACK, 4 threads)
| Set | n | PyTorch (funasr, CUDA) | tflite fp32 | **w8** | i8 |
| --- | ---: | ---: | ---: | ---: | ---: |
| gate40 (TTS, ranking only) | 40 | 0.169 · 21 exact | 0.204 · 20 | 0.204 · 20 | 0.204 · 20 |
| real commands, quiet room | 13 | 0.009 · 11 | 0.006 · 12 | **0.006 · 12** | 0.006 · 12 |
| real commands, field noise | 9 | 0.296 · 4 | 0.269 · 5 | **0.269 · 5** | 0.333 · 5 |
| Zeroth-Korean test (see caveat) | 457 | 0.024 | 0.023 | 0.023 | 0.024 |
| FLEURS ko dev | 64 | 0.071 | 0.068 | 0.068 | 0.068 |
- **Conversion parity**: fp32 tflite vs onnxruntime on the same features — max |Δlogit|
≤ 3.6e-4 on commands, 0.029 on 20 s sentences, CTC argmax agreement 100 %, transcripts
identical on all 583 clips. The PyTorch column differs only by CUDA-vs-CPU numerics
flipping borderline tokens (e.g. `확대` → kana in both, on different clips).
- **Against Whisper ACFT-KO** on the same gate, same protocol (first 24 clips, punctuation
kept): SenseVoice-Small 0.198 (PyTorch) / 0.247 (tflite) vs turbo-acft 0.182, medium
0.208, base 0.305, tiny 0.457 — between turbo and base, at a quarter of turbo's size.
Invoke latency per window (model only, p50):
| Window | fp32 | w8 | i8 |
| --- | ---: | ---: | ---: |
| 3 s | 128 ms | 134 ms | 98 ms |
| 5 s | 145 ms | 172 ms | 121 ms |
| 10 s | 290 ms | 286 ms | 211 ms |
| 30 s | 827 ms | 815 ms | 691 ms |
Not yet measured on an Android device.
## Caveats
- Stock weights. Two Korean fine-tunes were measured and lost to stock on every set
(an EDIE fine-tune: Zeroth CER 0.48; an in-house Zeroth fine-tune: real-command CER 0.020 vs 0.009).
- Field noise is the weak spot (fan/wind/vehicle recordings: 5 / 9 exact).
- Language forcing is soft: a few 1-syllable clips come out in kana even with `ko`.
## License / attribution
Weights: FunAudioLLM SenseVoiceSmall, under the
[FunASR model license](https://github.com/modelscope/FunASR/blob/main/MODEL_LICENSE).
Conversion only — no weights were changed. Paper: [FunAudioLLM, arXiv 2407.04051](https://arxiv.org/abs/2407.04051).
|