Skip to content

Fix detection on onnxruntime 1.27.0 / arm64 (KleidiAI batch-1 conv bug) - #91

Merged
pham-tuan-binh merged 1 commit into
mainfrom
fix-onnxruntime-1.27-arm64
Sep 30, 2026
Merged

pham-tuan-binh merged 1 commit into
mainfrom
fix-onnxruntime-1.27-arm64

Conversation

@pham-tuan-binh

Copy link
Copy Markdown
Collaborator

Problem

On onnxruntime 1.27.0 on arm64 (Apple silicon), wake words are never detected: every score is ~0. The reference positive.wav (the clip the Rust and Swift ports test against) scores 0.004 with examples/resources/hey_livekit.onnx; on 1.20–1.26 it scores 0.98.

1.27.0 is hard to avoid: reachy-mini ≥ 1.10, for example, pins onnxruntime==1.27.0.

Cause

onnxruntime 1.27.0 runs batch-1 convolutions on arm64 through its KleidiAI kernels, which return wrong results. WakeWordModel.predict runs the speech-embedding model one 76-frame window per call (batch 1), so the embedding is garbage from the first Conv on. The mel spectrogram and the classifiers are unaffected.

What I measured, on the embedding model with the positive clip's windows (max abs error against 1.26):

onnxruntime batch 1 batch ≥ 2
1.26.0 0 3e-5
1.27.0 36.3 3e-5
1.27.0 + mlas.disable_kleidiai=1 2.7e-5 3e-5

Graph optimization levels make no difference. A single standalone Conv reproduces it too (errors of 20–35 at batch 1). The Swift port batches its windows, which is why its tests pass on the same model.

Fix

  • livekit.wakeword.session.session_options() returns the caller's SessionOptions (or new defaults) with mlas.disable_kleidiai=1, unless the caller already set that entry. It is used by every session the package opens: mel, embedding, classifiers and eval. Older onnxruntime versions accept the entry and ignore it (checked 1.17.3, 1.20.1, 1.27.0).
  • tests/test_pretrained.py: the shipped hey_livekit model on tests/fixtures/{positive,negative}.wav (copied from the Swift fixtures). It needs only numpy + onnxruntime, so it runs with --noconftest.
  • CI: a new inference-arm64 job on macos-latest runs that test, because the existing Ubuntu x86 job can't reproduce the bug.

Tested (macOS arm64)

  • onnxruntime 1.27.0 without the fix: test_positive_clip_detected fails (0.004).
  • onnxruntime 1.27.0 and 1.26.0 with the fix: all 4 tests pass.
  • ruff check / ruff format --check are clean on the touched files. (Two long lines in inference/model.py predate this change.)

The upstream bug belongs in an onnxruntime issue too. The standalone repro is a single Conv, (1,1,76,32) input, 24 output channels, 3×3 kernel, pads=[0,1,0,1], on arm64 CPU.

onnxruntime 1.27.0 on arm64 (e.g. Apple silicon) runs batch-1 convolutions
through its KleidiAI kernels, which return wrong results. The speech-embedding
model runs one window per call, so every score collapsed to ~0: the reference
positive.wav scored 0.004 (0.98 on 1.20-1.26). Every session the package opens
now sets mlas.disable_kleidiai=1 unless the caller set it; older versions
accept and ignore it (checked 1.17-1.27).

Adds tests/test_pretrained.py (the shipped hey_livekit model on the Rust/Swift
reference clips, no torch needed) and runs it on macos-latest (arm64) in CI,
since the x86 runner can't reproduce this.
@pham-tuan-binh
pham-tuan-binh merged commit 69324ba into main Sep 30, 2026
7 checks passed
@pham-tuan-binh
pham-tuan-binh deleted the fix-onnxruntime-1.27-arm64 branch September 30, 2026 20:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant