Files
WhispAssist/docs/benchmarks/2026-07-parakeet-vs-whisper.md
T

9.6 KiB
Raw Blame History

Parakeet vs Whisper — default-model bake-off (2026-07-16)

Pre-registered comparison (criteria fixed before any numbers existed — see the plan on branch test_parakeet) between WhispAssist's shipping Whisper base.en paths and NVIDIA Parakeet TDT 0.6B v2, to decide whether the default transcription model should change.

Provenance

  • Machine: Intel Core Ultra 5 135U (Meteor Lake: 12C/14T CPU, Intel Arc iGPU, Intel AI Boost NPU, driver 32.0.100.4724), Windows 11 Pro 26200. App version under test: 0.7.3.
  • Harness: src-tauri/examples/asr_bench.rs — drives the production transcribers (WhisperTranscriber, OnnxTranscriber) and a sherpa-onnx TransducerRecognizer; one contender per process (clean CPU accounting). WER = word-level Levenshtein after lowercase/punctuation-strip normalization (self-tested).
  • Corpus: first 30 LibriSpeech test-clean utterances (241.7 s, 644 reference words), 16 kHz mono, staged by scripts/bench/fetch-corpus.ps1. Live latency: sequential 10 s windows over a 3-minute concatenation (16 windows).
  • Models: ggml base.en-q5_1, medium.en-q5_0; WA ONNX base.en (merged decoder, encoder on OpenVINO/NPU + decoder on OpenVINO/GPU — EP confirmed in-run); sherpa-onnx-nemo-parakeet-tdt-0.6b-v2-int8 (encoder 652 MB + decoder 7 MB + joiner 2 MB).
  • Raw data: bench-results/raw.csv, bench-results/raw-w1-novulkan.csv (committed).

Results — corpus (accuracy + cost)

Contender Engine / device WER RTF (wall) CPU-sec per audio-sec Load
W1 base.en (as shipped, 14 thr) whisper.cpp, CPU 2.48 % ⚠ 20.1 ⚠ 232.6 112 ms
W1 base.en (thread-cap fix) whisper.cpp, CPU 2.48 % 0.218 0.827 163 ms
W2 base.en whisper.cpp Vulkan, iGPU 2.64 % 0.071 0.033 248 ms
W3 medium.en whisper.cpp Vulkan, iGPU 3.26 % 0.304 0.088 724 ms
W4 base.en (default) ort ONNX, NPU + iGPU 3.11 % 0.058 0.049 ~4.7 s
P1 parakeet int8, t8 sherpa-onnx, CPU 0.47 % 0.047 0.396 ~4.6 s
P1 parakeet int8, t4 sherpa-onnx, CPU 0.47 % 0.049 0.224 ~4.6 s
  • Parakeet: 3 errors in 644 words, vs 20 for the current default — an 85 % relative WER improvement — while also posting the best RTF in the field, on the CPU alone.
  • Parakeet at 4 threads costs ≈0.22 CPU-cores sustained (~1.6 % of this machine's 14 threads); the NPU+iGPU Whisper path costs ≈0.05.
  • ✅ W1 anomaly RESOLVED (2026-07-16 follow-up): root cause was n_threads = available_parallelism() (14) putting ggml spin-barrier workers on the two slow LP E-cores — every graph-node barrier waits on the slowest worker while the rest burn CPU. 12 threads is healthy (RTF 0.200), 14 collapses (RTF ~22). The original suspect (opt-level = "z" / unoptimized ggml) was disproven: a verified /O2 build was equally slow. Fix: default capped at 4 threads, whisper.cpp's own upstream default (WA_WHISPER_THREADS env overrides); re-run row above — RTF 20.1 → 0.218 (92×), CPU 232.6 → 0.827 (281×), WER identical. (The build was also silently unoptimized — the cmake crate clobbers MSVC /O2 when no generator is set; fixed via CMAKE_GENERATOR=Ninja in .cargo/config.toml as build hygiene, though it wasn't the bottleneck.) Raw data: bench-results/raw-w1-fixed.csv.
  • W3 (medium) scoring worse than base on this corpus (21 vs 17 errors) is small-sample noise on clean read speech; treat W3's WER as "≈base" here, not as medium being worse in general.

Results — live windows (10 s, n=16)

Contender p50 p95 max
W2 Vulkan base.en 1055 ms 3722 ms 4148 ms
W4 NPU+iGPU (default) 519 ms 630 ms 928 ms
P1 parakeet t8 (CPU) 450 ms 497 ms 514 ms

All hold real-time; Parakeet is fastest and near-flat (64 ms p50→max spread), which would make the live transcript feel noticeably steadier.

Evidence sample (file 1089-134686-0000, 10.4 s)

Reference: HE HOPED THERE WOULD BE STEW FOR DINNER TURNIPS AND CARROTS AND BRUISED POTATOES AND FAT MUTTON PIECES TO BE LADLED OUT IN THICK PEPPERED FLOUR FATTENED SAUCE

  • W4 (default): "He hoped there would be stew for dinner, turnips and carrots and bruised potatoes and fat mutton pieces to be ladled out in thick peppered flour-fatted sauce." (2 err)
  • P1 (parakeet): " He hoped there would be stew for dinner turnips and carrots and bruised potatoes and fat mutton pieces to be ladled out in thick peppered flour fattened sauce" (0 err)

Note the trade visible even here: Whisper punctuates richly; Parakeet's int8 output carries casing but sparse punctuation — a real consideration for notes.md readability (see integration costs).

Qualitative pass — real recording (2026-07-16 follow-up)

bench-corpus/test_wavs/bank_deposits.wav (62 s real recording, 48 kHz stereo — financial disclosure read aloud; downmixed/resampled by the production reader). No reference transcript, so transcripts were compared by eye; raw hypotheses in bench-results/raw-qual-bank-deposits.csv (its WER column is against a placeholder — ignore it).

  • P1 (parakeet t4): cleanest of the three — zero audible-word errors, and the best punctuation, including commas around a parenthetical clause ("…the bank deposit details section, which appears later in this statement, for information…"). The "sparse punctuation" concern from the LibriSpeech sample did not reproduce on this longer real clip.
  • W2 (Vulkan base.en): near-perfect; one function-word slip ("effective for cash balances and your FDIC insured…" — W4 and P1 both hear "in your").
  • W4 (NPU ONNX, shipping default): worst of the three — "details" → "D-Tales" twice, "are SIPC protected" → "or SIPC protected", and a segmentation break at the end ("statement period" → "state. period.").

One clip, one speaker, clean audio — directionally consistent with the corpus WERs (P1 ≥ W2 > W4), and it removes punctuation as an argument against Parakeet. Multi-speaker crosstalk remains untested.

Pre-registered criteria, evaluated

Criterion Result
(a) WER ≥30 % relatively better than W4 PASS — 85 % better (0.47 % vs 3.11 %)
(b) CPU rung: beat W1 on WER and CPU-seconds PASS — vs the fixed W1 (follow-up): 5.3× better WER (0.47 % vs 2.48 %), 3.7× less CPU (0.224 vs 0.827 cpu-sec/audio-sec)
(b) NPU rung: accelerator execution or CPU ≤ W4 FAIL (strictly) — 0.224 vs 0.049 CPU-sec/audio-sec; accelerated Parakeet not achieved (below)
(c) Live windows hold real-time PASS — best in field, p95 497 ms

Phase-4 finding (accelerated Parakeet, timeboxed): the sherpa route is packaging-blocked — sherpa-rs's directml feature is mutually exclusive with download-binaries (compile_error! in sherpa-rs-sys 0.6.8), and escaping via SHERPA_LIB_PATH + a version-matched prebuilt DirectML archive would require replacing the base dependency configuration for the whole branch, with transducer-on-DirectML performance unproven (and a prior DirectML-on-Intel-GPU crash on record). The genuine route is a TDT decode loop on our own ort/OpenVINO stack (istupakov-style ONNX exports) — estimated days, not hours, and only worth it if the NPU rung must flip. The bench-directml cargo feature + p2-parakeet-dml contender remain in the harness as the documented attempt.

Recommendation (per hardware rung)

  1. CPU-only machines: change the default to Parakeet TDT 0.6B v2 int8. Even against the fixed W1 (thread cap, follow-up above) it is ~5× more accurate and 3.7× cheaper in CPU-seconds at comparable wall RTF. This is the unambiguous win — just no longer by three orders of magnitude, since the W1 pathology itself is fixed.
  2. NPU+iGPU machines (this machine): the pre-registered rule says keep Whisper-ONNX — Parakeet-on-CPU costs 4.6× the CPU of the current near-idle path. The override argument the numbers support: 0.22 cores is still objectively tiny, and it buys 85 % fewer errors plus the steadiest live latency. That trade is the user's call, not the benchmark's; if low-CPU stays the hard constraint, the NPU rung flips only after the ort/OpenVINO TDT work lands.
  3. GPU (Vulkan) machines without NPU: W2 is the cheapest-CPU option (0.033), but Parakeet's accuracy gap applies here too — same judgment call as the NPU rung, cheaper to revisit after the integration items below.

Integration costs before any default flip (not solved by this bench): timestamped segments (sherpa-rs's transducer API returns text only; the C API exposes timestamps — small upstream PR or direct -sys call), punctuation restoration (sparse on short LibriSpeech clips, though the real-recording qualitative pass showed good punctuation on longer audio — may be a non-issue), English-only v2 (v3 is multilingual — the whisper multilingual path must remain for non-English), a ~660 MB model download, and the model catalog/dispatch work (same shape as the 0.7.3 merged-decoder swap).

Caveats

  • LibriSpeech is clean read speech; WERs are comparative, not absolute product claims. The qualitative pass over a real recording was run as a follow-up (section above) — still single-speaker; multi-speaker meeting audio with crosstalk remains untested.
  • int8 quantization slightly penalizes Parakeet vs published fp32 figures — it still won by 85 %; the fp32 bundle exists if a tighter number is ever needed.
  • Single machine, single run per cell (30 files each). Differences here are far larger than run-to-run noise, but treat third-decimal RTF differences as noise.