Update model card with LiteRT-LM Omni (.litertlm) documentation

#12
Files changed (1) hide show
  1. README.md +375 -3
README.md CHANGED
@@ -1,6 +1,378 @@
1
  ---
2
  license: apache-2.0
3
- base_model:
4
- - openai/whisper-tiny
5
  pipeline_tag: automatic-speech-recognition
6
- ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
  license: apache-2.0
3
+ library_name: litert-lm
 
4
  pipeline_tag: automatic-speech-recognition
5
+ base_model: openai/whisper-tiny
6
+ base_model_relation: quantized
7
+ language:
8
+ - en
9
+ - zh
10
+ - de
11
+ - es
12
+ - ru
13
+ - ko
14
+ - fr
15
+ - ja
16
+ - pt
17
+ - tr
18
+ - pl
19
+ - ca
20
+ - nl
21
+ - ar
22
+ - sv
23
+ - it
24
+ - id
25
+ - hi
26
+ - fi
27
+ - vi
28
+ - he
29
+ - uk
30
+ - el
31
+ - ms
32
+ - cs
33
+ - ro
34
+ - da
35
+ - hu
36
+ - ta
37
+ - no
38
+ - th
39
+ - ur
40
+ - hr
41
+ - bg
42
+ - lt
43
+ - la
44
+ - mi
45
+ - ml
46
+ - cy
47
+ - sk
48
+ - te
49
+ - fa
50
+ - lv
51
+ - bn
52
+ - sr
53
+ - az
54
+ - sl
55
+ - kn
56
+ - et
57
+ - mk
58
+ - br
59
+ - eu
60
+ - is
61
+ - hy
62
+ - ne
63
+ - mn
64
+ - bs
65
+ - kk
66
+ - sq
67
+ - sw
68
+ - gl
69
+ - mr
70
+ - pa
71
+ - si
72
+ - km
73
+ - sn
74
+ - yo
75
+ - so
76
+ - af
77
+ - oc
78
+ - ka
79
+ - be
80
+ - tg
81
+ - sd
82
+ - gu
83
+ - am
84
+ - yi
85
+ - lo
86
+ - uz
87
+ - fo
88
+ - ht
89
+ - ps
90
+ - tk
91
+ - nn
92
+ - mt
93
+ - sa
94
+ - lb
95
+ - my
96
+ - bo
97
+ - tl
98
+ - mg
99
+ - as
100
+ - tt
101
+ - haw
102
+ - ln
103
+ - ha
104
+ - ba
105
+ - jw
106
+ - su
107
+ tags:
108
+ - litert
109
+ - litert-lm
110
+ - tflite
111
+ - asr
112
+ - automatic-speech-recognition
113
+ - speech-to-text
114
+ - on-device
115
+ - android
116
+ - ios
117
+ - whisper
118
+ - int8
119
+ - npu
120
+ ---
121
+
122
+ # Whisper-Tiny for LiteRT-LM
123
+
124
+ Compact (~42 MB), on-device speech-to-text in a single file.
125
+
126
+ This is
127
+ [openai/whisper-tiny](https://huggingface.co/openai/whisper-tiny)
128
+ (39M-parameter encoder-decoder Transformer introduced in
129
+ [Robust Speech Recognition via Large-Scale Weak Supervision](https://arxiv.org/abs/2212.04356),
130
+ Radford et al.) packaged as a self-contained `.litertlm` bundle for
131
+ [LiteRT-LM Omni](https://github.com/google-ai-edge/LiteRT-LM/tree/main/omni),
132
+ Google's open-source on-device inference runtime across Android, iOS, macOS,
133
+ Linux, and C++, alongside standalone `.tflite` and AOT-compiled NPU variants for
134
+ [LiteRT](https://github.com/google-ai-edge/LiteRT). Everything needed to
135
+ transcribe arbitrary-length audio with punctuation and capitalization is inside
136
+ the one `.litertlm` file: INT8 Whisper Tiny encoder-decoder transformer,
137
+ 51,865-token multilingual BPE tokenizer, and `Speech2TextMetadata` (16 kHz
138
+ resampler and 30-second 80-bin Slaney log-mel front-end configuration) — no
139
+ Python and no network required.
140
+
141
+ - **Footprint:** **41.8 MB** `.litertlm` bundle (`3.6×` smaller than the
142
+ 151 MB FP32 checkpoint)
143
+ - **Languages:** Multilingual Whisper Tiny checkpoint (99 languages in the
144
+ upstream vocabulary; defaults to English transcription `<|en|>` when no
145
+ custom `decode_start_token_ids` are specified)
146
+ - **Windowing:** 30-second audio windows (`input_milliseconds = 30000`, 3,000
147
+ mel frames at 10 ms hop) with configurable sliding-window overlap and seam
148
+ alignment for recordings longer than 30 seconds
149
+
150
+ ## Which file should I use?
151
+
152
+ | File | Size | Backend | Recommended for |
153
+ |---|---:|---|---|
154
+ | **`whisper_tiny_30s_i8.litertlm`** ⭐ | 41.8 MB | CPU / GPU (`--backend=cpu` recommended) | **[LiteRT-LM Omni](https://github.com/google-ai-edge/LiteRT-LM/tree/main/omni) (`OmniEngine` / `omni_runner`) across Android, iOS, macOS, Linux, and C++.** All-in-one `.litertlm` bundle with INT8 30 s Whisper Tiny model, tokenizer, and `Speech2TextMetadata`. |
155
+ | `whisper_tiny_30s_i8.tflite` | 41.1 MB | CPU / GPU | Direct [LiteRT SDK](https://github.com/google-ai-edge/LiteRT/tree/main/litert/samples/asr) integration without LiteRT-LM (`encode` and `decode` signatures). |
156
+ | `whisper_tiny_30s_f32.tflite` | 151 MB | CPU / GPU | Unquantized FP32 reference baseline. |
157
+ | `whisper_tiny_30s_f32_<SoC>.tflite` | 119–158 MB | NPU + CPU | Standalone LiteRT NPU pipelines AOT-compiled for a specific mobile SoC (see below). |
158
+
159
+ How the variants differ:
160
+
161
+ - **`.litertlm` vs. `.tflite`** — the `.litertlm` file bundles the `.tflite`
162
+ graph together with the compressed Hugging Face `tokenizer.json` and
163
+ `Speech2TextMetadata` protobuf in one self-contained archive for
164
+ [LiteRT-LM Omni](https://github.com/google-ai-edge/LiteRT-LM/tree/main/omni)'s
165
+ `OmniEngine` and `omni_runner`. Standalone `.tflite` files contain only the
166
+ neural network graph and expect the host app to supply 80-bin Whisper
167
+ log-mel preprocessing and BPE detokenization.
168
+ - **INT8 (`_i8`) vs. FP32 (`_f32`)** — INT8 dynamic range quantization shrinks
169
+ the model from **151 MB to 41.8 MB** (`3.6×` smaller on disk) and
170
+ accelerates mobile ARM64 CPU inference.
171
+ - **AOT-compiled NPU `.tflite` files (`_<SoC>.tflite`)** — pre-compiled for
172
+ on-device NPU accelerators via the LiteRT NeuroPilot / QNN compilers:
173
+
174
+ | SoC family | Available `<SoC>` suffixes |
175
+ |---|---|
176
+ | **Qualcomm Snapdragon** | `Qualcomm_SM8450` (8 Gen 1), `SM8550` (8 Gen 2), `SM8650` (8 Gen 3), `SM8750` (8 Elite), `SM8850` (8 Elite Gen 5), `SA8255`, `SA8295` |
177
+ | **MediaTek Dimensity** | `MediaTek_MT6877`, `MT6878`, `MT6879`, `MT6897`, `MT6983` (9000), `MT6985` (9200), `MT6989` (9300), `MT6991` (9400), `MT6993`, `MT8171`, `MT8188`, `MT8189` |
178
+
179
+ ## Download
180
+
181
+ ```sh
182
+ pip install -U huggingface_hub
183
+
184
+ # Recommended: INT8 .litertlm bundle (41.8 MB)
185
+ hf download litert-community/whisper-tiny \
186
+ whisper_tiny_30s_i8.litertlm --local-dir .
187
+ ```
188
+
189
+ ---
190
+
191
+ ## Usage with LiteRT-LM Omni (`omni_runner` & C++)
192
+
193
+ All commands below accept mono or multi-channel WAV files at any sample rate
194
+ (automatically resampled to 16 kHz mono) and stream confirmed and unconfirmed
195
+ transcript segments to stdout (or `--output_path`). Prerequisites (Bazel,
196
+ Android NDK) are in the LiteRT-LM
197
+ [build guide](https://github.com/google-ai-edge/LiteRT-LM/blob/main/docs/getting-started/build-and-run.md)
198
+ and the
199
+ [LiteRT-LM Omni documentation](https://github.com/google-ai-edge/LiteRT-LM/tree/main/omni).
200
+
201
+ ### 1. Download a model
202
+
203
+ ```sh
204
+ pip install -U huggingface_hub
205
+ hf download litert-community/whisper-tiny \
206
+ whisper_tiny_30s_i8.litertlm --local-dir .
207
+ ```
208
+
209
+ > [!TIP]
210
+ > If you omit `--model_path` and pass `--model_name=whisper-tiny`,
211
+ > `omni_runner` automatically downloads `whisper_tiny_30s_i8.litertlm` into
212
+ > `--cache_dir` on first run.
213
+
214
+ ### 2. Build the runner
215
+
216
+ ```sh
217
+ git clone https://github.com/google-ai-edge/LiteRT-LM.git && cd LiteRT-LM
218
+ bazel build -c opt //omni:omni_runner # Linux / macOS
219
+ bazel build -c opt --config=android_arm64 //omni:omni_runner # Android (needs ANDROID_NDK_HOME)
220
+ ```
221
+
222
+ ### 3. Transcribe speech
223
+
224
+ **On Linux or macOS (CPU):**
225
+
226
+ ```sh
227
+ bazel-bin/omni/omni_runner --model_name=whisper-tiny \
228
+ --model_path=/path/to/whisper_tiny_30s_i8.litertlm \
229
+ --audio_path=runtime/components/testdata/audio_sample.wav
230
+ ```
231
+
232
+ **On an Android phone (CPU, recommended):**
233
+
234
+ ```sh
235
+ DEVICE_DIR=/data/local/tmp/omni
236
+ adb shell mkdir -p $DEVICE_DIR
237
+ adb push bazel-bin/omni/omni_runner prebuilt/android_arm64/*.so \
238
+ /path/to/whisper_tiny_30s_i8.litertlm \
239
+ runtime/components/testdata/audio_sample.wav $DEVICE_DIR
240
+
241
+ adb shell LD_LIBRARY_PATH=$DEVICE_DIR $DEVICE_DIR/omni_runner \
242
+ --model_name=whisper-tiny \
243
+ --backend=cpu --cache_dir=$DEVICE_DIR/cache \
244
+ --model_path=$DEVICE_DIR/whisper_tiny_30s_i8.litertlm \
245
+ --audio_path=$DEVICE_DIR/audio_sample.wav
246
+ ```
247
+
248
+ **On an Android phone (GPU):**
249
+
250
+ ```sh
251
+ adb shell LD_LIBRARY_PATH=$DEVICE_DIR $DEVICE_DIR/omni_runner \
252
+ --model_name=whisper-tiny \
253
+ --backend=gpu --cache_dir=$DEVICE_DIR/cache \
254
+ --model_path=$DEVICE_DIR/whisper_tiny_30s_i8.litertlm \
255
+ --audio_path=$DEVICE_DIR/audio_sample.wav
256
+ ```
257
+
258
+ > `--model_name=whisper-tiny` (or alias `whisper`) tells the runner which model
259
+ > family the file is (it is inferred automatically when the file is named
260
+ > `whisper-tiny.litertlm`). XNNPACK weight packs and compiled GPU kernels are
261
+ > cached in `--cache_dir` so subsequent runs start fast.
262
+
263
+ The full list of flags and the C++ `OmniEngine` / `OmniSession` streaming API
264
+ are documented in the
265
+ [LiteRT-LM Omni README](https://github.com/google-ai-edge/LiteRT-LM/tree/main/omni).
266
+
267
+ ### Use from C++ (`OmniEngine`)
268
+
269
+ ```cpp
270
+ #include "omni/omni_engine.h"
271
+ #include "omni/omni_session.h"
272
+ #include "runtime/proto/speech2text_session_config.pb.h"
273
+
274
+ using ::litert::omni::OmniEngine;
275
+ using ::litert::omni::OmniSession;
276
+ using ::litert::omni::PushInputSource;
277
+
278
+ OmniEngine::Settings settings{
279
+ .backend = OmniEngine::Settings::Backend::kCpu,
280
+ .cache_dir = "/data/local/tmp/omni/cache",
281
+ .model_path = "/data/local/tmp/omni/whisper_tiny_30s_i8.litertlm",
282
+ };
283
+ litert::lm::proto::Speech2TextSessionConfig asr_config;
284
+ asr_config.set_overlap_ratio(0.4f);
285
+
286
+ ABSL_ASSIGN_OR_RETURN(
287
+ auto engine,
288
+ OmniEngine::Create("whisper-tiny", settings, &asr_config));
289
+ auto input_source = std::make_unique<PushInputSource>();
290
+ PushInputSource* input = input_source.get();
291
+ ABSL_ASSIGN_OR_RETURN(auto session,
292
+ engine->CreateSession(std::move(input_source)));
293
+
294
+ // Push input audio metadata once, followed by one or more PCM float chunks.
295
+ ABSL_RETURN_IF_ERROR(input->PushInput(
296
+ OmniSession::AudioInputMetadata{.sample_rate_hz = 16000, .num_channels = 1}));
297
+ ABSL_RETURN_IF_ERROR(input->PushInput(
298
+ OmniSession::AudioInput{.pcm_samples = std::move(pcm_chunk)}));
299
+ input->Finish();
300
+ // Pull OmniSession::TextOutput updates (confirmed_text + unconfirmed_text)
301
+ // with session->ProcessNext() until OutOfRange or NotFound, then call session->Flush().
302
+ ```
303
+
304
+ In your `BUILD` file, link only the Whisper target
305
+ (`//omni/speech2text:whisper`) to avoid pulling in other speech, TTS, or image
306
+ generation models:
307
+
308
+ ```python
309
+ cc_binary(
310
+ name = "my_asr_app",
311
+ srcs = ["my_asr_app.cc"],
312
+ deps = [
313
+ "//omni:omni_engine",
314
+ "//omni:omni_session",
315
+ "//omni/speech2text:whisper",
316
+ ],
317
+ )
318
+ ```
319
+
320
+ ## What's in the file
321
+
322
+ Inside `whisper_tiny_30s_i8.litertlm`:
323
+
324
+ | Component | Details |
325
+ |---|---|
326
+ | **Audio front-end** | Self-contained C++ `LogMelSpectrogramProcessor` (`Speech2TextMetadata`): resamples any input audio rate to 16 kHz mono, computes 400-point Hann STFT (25 ms window, 10 ms hop, 3,000 frames per 30 s window), 80-bin Slaney power mel filterbank (`mel_floor = 1e-10`), and Whisper dynamic-range log normalization (`NORM_TYPE_WHISPER`, `[1, 80, 3000]`). |
327
+ | **Encoder-decoder (`tf_lite_encoder_decoder`)** | 39M-parameter Whisper Tiny transformer (`~41.1 MB`, INT8 weights) with two signatures: `encode` (`[1, 80, 3000]` log-mel spectrogram → encoder hidden states `[1, 1500, 384]`) and `decode` (encoder states, token IDs, and additive causal mask → vocabulary logits). |
328
+ | **Decoder (`StatelessDecoder`)** | Greedy autoregressive decoding seeded by `decode_start_token_ids = [50258, 50259, 50359, 50363]` (`<|startoftranscript|>`, `<|en|>`, `<|transcribe|>`, `<|notimestamps|>`) until `<|endoftext|>` (`50257`), with automatic trailing repetition truncation. |
329
+ | **Tokenizer (`HF_Tokenizer_Zlib`)** | Zlib-compressed Whisper multilingual BPE `tokenizer.json` (51,865 tokens with punctuation, capitalization, and language/task control tokens). |
330
+ | **Text merger** | Cross-window overlap seam alignment across consecutive 30 s windows (`overlap_ratio = 0.4` in `omni_runner`). |
331
+ | **Output** | Streaming `OmniSession::TextOutput` chunks (`confirmed_text` + `unconfirmed_text`). |
332
+
333
+ The models were exported from the upstream PyTorch checkpoint
334
+ (`openai/whisper-tiny`) with
335
+ [litert-torch](https://github.com/google-ai-edge/litert-torch) and quantized
336
+ with the AI Edge Quantizer. No retraining or fine-tuning was done.
337
+
338
+ ## Limitations
339
+
340
+ - **Fixed 30-second encoder window:** Whisper Tiny's encoder always processes
341
+ a 30-second (3,000-frame) mel-spectrogram window; clips shorter than 30
342
+ seconds are zero-padded to 30 seconds before `encode`. For lower-latency 5 s
343
+ streaming windows, consider
344
+ [`litert-community/parakeet-tdt-0.6b-v3`](https://huggingface.co/litert-community/parakeet-tdt-0.6b-v3),
345
+ [`litert-community/parakeet-ctc-0.6b`](https://huggingface.co/litert-community/parakeet-ctc-0.6b),
346
+ [`litert-community/Qwen3-ASR-0.6B`](https://huggingface.co/litert-community/Qwen3-ASR-0.6B),
347
+ or
348
+ [`litert-community/moonshine-tiny`](https://huggingface.co/litert-community/moonshine-tiny).
349
+ - **Default decoder prefix targets English (`<|en|>`):** By default,
350
+ `WhisperSessionFactory` seeds `StatelessDecoder` with
351
+ `[50258, 50259, 50359, 50363]` (`<|startoftranscript|>`, `<|en|>`,
352
+ `<|transcribe|>`, `<|notimestamps|>`). Transcribing another language with
353
+ Whisper Tiny requires overriding `decode_start_token_ids` in
354
+ `Speech2TextMetadata` with that language's Whisper language token ID. For
355
+ automatic zero-config multilingual speech recognition, use
356
+ [`litert-community/parakeet-tdt-0.6b-v3`](https://huggingface.co/litert-community/parakeet-tdt-0.6b-v3)
357
+ (25 European languages) or
358
+ [`litert-community/Qwen3-ASR-0.6B`](https://huggingface.co/litert-community/Qwen3-ASR-0.6B)
359
+ (52 languages and dialects).
360
+ - **SoC-specific NPU files are standalone `.tflite` graphs:** The pre-compiled
361
+ `whisper_tiny_30s_f32_<SoC>.tflite` files target the standalone LiteRT NPU
362
+ C++/Kotlin APIs rather than `.litertlm` bundles.
363
+
364
+ ## License & acknowledgements
365
+
366
+ - **Weights & tokenizer:**
367
+ [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0), inherited from
368
+ [openai/whisper-tiny](https://huggingface.co/openai/whisper-tiny)
369
+ ([Robust Speech Recognition via Large-Scale Weak Supervision](https://arxiv.org/abs/2212.04356),
370
+ Radford et al., OpenAI).
371
+ - **Conversion & runtime:**
372
+ [litert-torch](https://github.com/google-ai-edge/litert-torch),
373
+ [LiteRT](https://github.com/google-ai-edge/LiteRT), and
374
+ [LiteRT-LM Omni](https://github.com/google-ai-edge/LiteRT-LM/tree/main/omni)
375
+ (Apache-2.0).
376
+ - **Training data, as disclosed by the upstream model:** 680,000 hours of
377
+ labeled multilingual and multitask speech data collected from the internet.
378
+ This conversion adds no training data and no personal information.