Views
No views yet
facebook/wav2vec2-xlsr-53-espeak-cv-ft,
for running multilingual IPA phoneme recognition in the browser with
transformers.js.pytorch_model.bin — no safetensors, no ONNX —
so transformers.js cannot load it as-is. This repo is that missing export.| file | size | notes |
|---|---|---|
onnx/model.onnx | 1205 MB | fp32 reference. Reproduces the PyTorch output exactly. |
onnx/model_fp16.onnx | 603 MB | Best accuracy/size, but fp16 in practice wants WebGPU. |
onnx/model_q4.onnx | 230 MB | Recommended. Blockwise 4-bit weight-only; runs on the wasm backend. |
onnx/model_q4f16.onnx | 188 MB | Smallest. fp16 activations, so same WebGPU caveat as fp16. |
| variant | size | English 6 s | Lojban 60 s |
|---|---|---|---|
| fp32 | 1205 MB | 0.0% | 0.0% |
| fp16 | 603 MB | 1.8% | 0.4% |
| q8 | 303 MB | 0.0% | 12.5% |
| int8 | 303 MB | 5.4% | 17.9% |
| uint8 | 303 MB | 0.0% | 12.5% |
| q4 | 230 MB | 5.4% | 3.3% |
| q4f16 | 188 MB | 5.4% | 3.7% |
| bnb4 | 212 MB | 3.6% | 5.1% |
quantize_dynamic, which quantizes
activations as well as weights; q4/q4f16 are blockwise weight-only
(MatMulNBits, block size 32). Bits per weight is the wrong axis — what matters
is whether activations survive.q8 on the wasm backend, which for this
model is the worst available choice. Set dtype explicitly.q8 scores a perfect 0.0% and looks
like the obvious pick. Only the non-English clip separates the variants.Wav2Vec2PhonemeCTCTokenizer, which transformers.js does
not implement, and ships no tokenizer.json — so pipeline() and AutoTokenizer
both throw. Decoding a CTC phoneme model is a plain vocab lookup, so do it by hand
over vocab.json:1import { AutoModelForCTC, Wav2Vec2FeatureExtractor } from '@huggingface/transformers';
2
3const id = 'qnighy/wav2vec2-xlsr-53-espeak-cv-ft-ONNX';
4const model = await AutoModelForCTC.from_pretrained(id, { dtype: 'q4' });
5const extractor = await Wav2Vec2FeatureExtractor.from_pretrained(id);
6
7// Index by token id. `vocab.json` maps the other way.
8const vocab = [];
9for (const [token, i] of Object.entries(await (await fetch(
10 `https://huggingface.co/${id}/resolve/main/vocab.json`)).json())) vocab[i] = token;
11
12/** @param {Float32Array} pcm mono, 16 kHz */
13async function transcribe(pcm) {
14 const { logits } = await model(await extractor(pcm));
15 const [, frames, size] = logits.dims;
16 const data = logits.data;
17
18 const out = [];
19 let prev = -1;
20 for (let t = 0; t < frames; t++) {
21 let best = 0;
22 for (let v = 1; v < size; v++) {
23 if (data[t * size + v] > data[t * size + best]) best = v;
24 }
25 // Collapse repeats *before* dropping blanks -- the other order merges two
26 // genuinely repeated phonemes that the model separated with a blank.
27 if (best === prev) continue;
28 prev = best;
29 if (vocab[best] !== '<pad>') out.push(vocab[best]);
30 }
31 return out.join(' ');
32}phonemizer are not needed — they
phonemize text at training time, and decoding is pure vocab lookup.1# 1. fp32 export. Note: `optimum[exporters]` no longer exists as of optimum 2.x.
2uvx --with "optimum-onnx[onnxruntime]" --from optimum optimum-cli export onnx \
3 --model facebook/wav2vec2-xlsr-53-espeak-cv-ft \
4 --task automatic-speech-recognition out/
5
6# 2. Constant-fold before quantizing. NOT optional: this checkpoint's positional
7# conv uses weight normalisation, which exports as a runtime Mul, so the Conv
8# weight is not an initializer and the quantizer fails with
9# "Expected .../pos_conv_embed/conv/weight/weight.0/Mul_output_0 to be an
10# initializer". Symbolic shape inference crashes on this graph and is skipped.
11python -m onnxruntime.quantization.preprocess \
12 --input out/model.onnx --output folded/model.onnx --skip_symbolic_shape True
13
14# 3. Quantize with the transformers.js script (onnxruntime pinned to 1.20.1,
15# which is the last release before matmul_4bits_quantizer was renamed).
16python quantize.py --input_folder folded --output_folder onnx