Add model card: usage example and measured performance (M4 Max)

#11
by mlboydaisuke - opened
Files changed (1) hide show
  1. README.md +76 -1
README.md CHANGED
@@ -3,4 +3,79 @@ license: apache-2.0
3
  base_model:
4
  - openai/whisper-tiny
5
  pipeline_tag: automatic-speech-recognition
6
- ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  base_model:
4
  - openai/whisper-tiny
5
  pipeline_tag: automatic-speech-recognition
6
+ tags:
7
+ - litert
8
+ - tflite
9
+ - whisper
10
+ - asr
11
+ - on-device
12
+ ---
13
+
14
+ # whisper-tiny (LiteRT)
15
+
16
+ [openai/whisper-tiny](https://huggingface.co/openai/whisper-tiny) exported to
17
+ LiteRT (`.tflite`) for on-device speech recognition. Each file bundles the
18
+ encoder and decoder as two signatures over a fixed 30-second input window:
19
+
20
+ - `encode`: log-mel spectrogram `[1, 80, 3000]` → encoder states `[1, 1500, 384]`
21
+ - `decode`: (encoder states, token buffer `[1, 128]` int32, additive causal mask
22
+ `[1, 1, 128, 128]`) → logits `[1, 128, 51865]`
23
+
24
+ ## Files
25
+
26
+ | File | Weights | Notes |
27
+ |---|---|---|
28
+ | `whisper_tiny_30s_f32.tflite` | float32 | reference |
29
+ | `whisper_tiny_30s_i8.tflite` | int8 | ~4x smaller, same transcript and speed on the test clip |
30
+ | `whisper_tiny_30s_f32_<SoC>.tflite` | float32 | 19 MediaTek / Qualcomm NPU-compiled variants |
31
+
32
+ ## Usage (Python, `ai-edge-litert`)
33
+
34
+ ```python
35
+ import numpy as np
36
+ import soundfile as sf
37
+ from transformers import WhisperFeatureExtractor, WhisperTokenizer
38
+ from ai_edge_litert.interpreter import Interpreter
39
+
40
+ audio, sr = sf.read("speech16k.wav", dtype="float32") # 16 kHz mono
41
+ fe = WhisperFeatureExtractor.from_pretrained("openai/whisper-tiny")
42
+ mel = fe(audio, sampling_rate=sr, return_tensors="np").input_features
43
+ tok = WhisperTokenizer.from_pretrained("openai/whisper-tiny")
44
+
45
+ it = Interpreter(model_path="whisper_tiny_30s_i8.tflite", num_threads=4)
46
+ enc, dec = it.get_signature_runner("encode"), it.get_signature_runner("decode")
47
+
48
+ enc_out = enc(args_0=mel)["output_0"]
49
+ mask = np.triu(np.full((128, 128), -1e9, dtype=np.float32), k=1)[None, None]
50
+ tokens = np.zeros((1, 128), dtype=np.int32)
51
+ tokens[0, :4] = [50258, 50259, 50359, 50363] # <|sot|><|en|><|transcribe|><|notimestamps|>
52
+ seq, pos = [], 3
53
+ while pos < 127:
54
+ logits = dec(args_0=enc_out, args_1=tokens, args_2=mask)["output_0"]
55
+ nxt = int(np.argmax(logits[0, pos]))
56
+ if nxt == 50257: # <|endoftext|>
57
+ break
58
+ pos += 1
59
+ tokens[0, pos] = nxt
60
+ seq.append(nxt)
61
+ print(tok.decode(seq, skip_special_tokens=True))
62
+ ```
63
+
64
+ ## Performance
65
+
66
+ Measured on a 4.8 s window of real speech (19 output tokens), greedy decode,
67
+ CPU inference via the Python Interpreter API (XNNPACK, 4 threads,
68
+ `ai-edge-litert` 2.1.6), median of 10 runs. The graph always processes its
69
+ fixed 30 s window, so RTF is reported against 30 s; decode scales with the
70
+ number of emitted tokens.
71
+
72
+ | Device | Variant | Encode | Decode (19 tok) | Per token | Window total | RTF |
73
+ |---|---|---|---|---|---|---|
74
+ | Apple M4 Max (macOS) | i8 | 30.3 ms | 203.4 ms | 10.7 ms | 233.7 ms | 0.008 |
75
+ | Apple M4 Max (macOS) | f32 | 28.5 ms | 199.7 ms | 10.5 ms | 228.2 ms | 0.008 |
76
+
77
+ RTF = processing time / 30 s window (lower is better; below 1.0 is faster than
78
+ real time). Both variants produced an identical, fully correct transcript of the test
79
+ clip, at effectively the same speed - on Apple silicon the win from i8 is the
80
+ ~4x smaller file, not latency. The SoC-specific variants target their named NPUs and were not
81
+ measured here.