Upload README.md with huggingface_hub
Browse files
README.md
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: apache-2.0
|
| 3 |
+
tags:
|
| 4 |
+
- executorch
|
| 5 |
+
- xnnpack
|
| 6 |
+
- pte
|
| 7 |
+
- on-device
|
| 8 |
+
- automatic-speech-recognition
|
| 9 |
+
base_model:
|
| 10 |
+
- jonatasgrosman/wav2vec2-large-xlsr-53-russian
|
| 11 |
+
---
|
| 12 |
+
# wav2vec2 XLSR-53 Russian — ExecuTorch (CTC speech recognition)
|
| 13 |
+
|
| 14 |
+
Ten seconds of Russian audio in, a transcript out, in **one forward pass**. No
|
| 15 |
+
decoder loop, no beam, no KV cache, nothing to carry between calls — the encoder
|
| 16 |
+
is the model and the transcript falls out of an argmax per 20 ms frame.
|
| 17 |
+
|
| 18 |
+
That is the difference from the Whisper conversions on this shelf, which are
|
| 19 |
+
encoder-decoder: one encoder pass per window plus one decoder pass per generated
|
| 20 |
+
token. A CTC model trades that machinery — and the ability to punctuate — for a
|
| 21 |
+
much simpler thing to run on a device.
|
| 22 |
+
|
| 23 |
+
```
|
| 24 |
+
(waveform (1,160000) fp32 16 kHz mono, attention_mask (1,160000) int64)
|
| 25 |
+
-> logits (1, 499, 39)
|
| 26 |
+
```
|
| 27 |
+
|
| 28 |
+
| build | file | MB | corr vs fp32 eager | transcripts identical | Mac ms* |
|
| 29 |
+
|---|---|---|---|---|---|
|
| 30 |
+
| fp32 | `wav2vec2_xlsr53_ru_xnnpack_fp32.pte` | 1262.3 | 1.000000 | **5/5** | 276.0 |
|
| 31 |
+
| fp16 | `wav2vec2_xlsr53_ru_xnnpack_fp16.pte` | 656.9 | 0.999914 | **5/5** | 682.4 |
|
| 32 |
+
| int8 (dynamic) | `wav2vec2_xlsr53_ru_xnnpack_int8.pte` | 355.6 | 0.999034 | **5/5** | 273.0 |
|
| 33 |
+
|
| 34 |
+
\*Mac arm64, single process, median of 5, for a 10-second window — a reference
|
| 35 |
+
point for relative cost, not a device number. Torch eager fp32 on the same
|
| 36 |
+
machine: 227.9 ms. XNNPACK delegate coverage 64.5%.
|
| 37 |
+
|
| 38 |
+
**There is no Core ML build.** coremltools rejects the graph on a `stack` whose operands are a mix of int and float. This one is XNNPACK only, and the same file runs on Android and on the Mac.
|
| 39 |
+
|
| 40 |
+
## Running it
|
| 41 |
+
|
| 42 |
+
**1. The audio.** 16 kHz mono in [-1, 1], exactly 160 000 samples. Zero-pad a
|
| 43 |
+
shorter clip; cut a longer one. **Do not normalise it** — the per-utterance
|
| 44 |
+
normalisation wav2vec2 expects is inside the graph, and it needs the mask to be
|
| 45 |
+
correct (below).
|
| 46 |
+
|
| 47 |
+
**2. The mask.** `attention_mask` is int64 `(1, 160000)`, 1 on the real samples
|
| 48 |
+
and 0 on the padding. It is not optional and it is not cosmetic: padding a
|
| 49 |
+
3.7-second clip to 10 seconds without it changed the transcript on **4 of 5**
|
| 50 |
+
test sentences in the Japanese build, where the zeros drag the mean and shrink
|
| 51 |
+
the variance the normalisation divides by, and the encoder attends to the
|
| 52 |
+
silence.
|
| 53 |
+
|
| 54 |
+
**3. The decode.** CTC greedy, over `logits[0]`:
|
| 55 |
+
|
| 56 |
+
```
|
| 57 |
+
ids = argmax per frame
|
| 58 |
+
collapse runs of the same id
|
| 59 |
+
drop the blank (id 0)
|
| 60 |
+
map through the repo's vocab.json, turn the word delimiter | into a space, strip
|
| 61 |
+
```
|
| 62 |
+
|
| 63 |
+
That last clause matters: without the strip this recipe returns a trailing space
|
| 64 |
+
where the model's own tokenizer does not. The checker in the conversion repo runs
|
| 65 |
+
the hand-written recipe and asserts it against `processor.batch_decode` on the
|
| 66 |
+
same ids, which is how that was caught.
|
| 67 |
+
|
| 68 |
+
There is no punctuation in the output — the vocabulary has 39 entries and
|
| 69 |
+
none of them are sentence marks.
|
| 70 |
+
|
| 71 |
+
## Verification
|
| 72 |
+
|
| 73 |
+
Five sentences spoken by macOS `say -v Milena`, run through the fp32 `.pte` and
|
| 74 |
+
through the eager model on the identical padded window and mask. This is what
|
| 75 |
+
both return — the model's own reading of synthetic speech, warts and all:
|
| 76 |
+
|
| 77 |
+
```
|
| 78 |
+
сегодня хорошая погода
|
| 79 |
+
где находится вокзал
|
| 80 |
+
один кофе пожалуйста
|
| 81 |
+
встреча начинается в десять часов
|
| 82 |
+
этот поезд идет до москвы
|
| 83 |
+
```
|
| 84 |
+
|
| 85 |
+
**5/5 identical to eager**, worst character error rate 0.000. Synthetic speech is a
|
| 86 |
+
control, not a benchmark — it says the conversion transcribes what the fp32 model
|
| 87 |
+
transcribes, and nothing about word error rate on real speakers.
|
| 88 |
+
|
| 89 |
+
```bash
|
| 90 |
+
python convert/check_wav2vec2.py ru fp32 # or int8
|
| 91 |
+
python convert/audit_int8.py wav2vec2_xlsr53_ru
|
| 92 |
+
```
|
| 93 |
+
|
| 94 |
+
## The conversion note worth reading
|
| 95 |
+
|
| 96 |
+
The first build of this architecture was correct and **13x slower than it should
|
| 97 |
+
have been**: 3485 ms against eager's 215 ms, at correlation 1.000000. The cost was
|
| 98 |
+
one module.
|
| 99 |
+
|
| 100 |
+
wav2vec2 puts a positional convolution in front of its encoder,
|
| 101 |
+
`Conv1d(1024, 1024, kernel_size=128, groups=16)`, and it carries `weight_norm` —
|
| 102 |
+
a live `torch.nn.utils.parametrize` parametrization. Under `torch.export` that
|
| 103 |
+
makes the weight a *computed* tensor: the graph gets a `pow` and a `sum` where a
|
| 104 |
+
constant should be. XNNPACK's partitioner requires a weight that is a static
|
| 105 |
+
parameter, so it declines the convolution, and the convolution runs on the
|
| 106 |
+
portable kernels.
|
| 107 |
+
|
| 108 |
+
Measured on that module alone at sequence 499:
|
| 109 |
+
|
| 110 |
+
```
|
| 111 |
+
as loaded delegated 70.0% 3206.7 ms
|
| 112 |
+
after remove_parametrizations delegated 100.0% 3.6 ms
|
| 113 |
+
```
|
| 114 |
+
|
| 115 |
+
with the output identical to the last bit. Materialising the weight before export
|
| 116 |
+
is what brought the whole model back to its proper speed. It is reported upstream
|
| 117 |
+
as [pytorch/executorch#22078](https://github.com/pytorch/executorch/issues/22078),
|
| 118 |
+
because ExecuTorch already ships the pass that would fix it without touching the
|
| 119 |
+
model — it just does not run it during lowering.
|
| 120 |
+
|
| 121 |
+
- **Source**: [jonatasgrosman/wav2vec2-large-xlsr-53-russian](https://huggingface.co/jonatasgrosman/wav2vec2-large-xlsr-53-russian)
|
| 122 |
+
- **License**: Apache-2.0
|
| 123 |
+
|
| 124 |
+
torch.export -> to_edge_transform_and_lower(partitioner) -> .pte
|
| 125 |
+
(conversion scripts: [executorch-models](https://github.com/john-rocky/executorch-models))
|