Workflow-Werkstatt
Tutorials

Parakeet auf ONNX-Runtime mit DirectML: schnelle lokale Spracherkennung ohne NVIDIA

Ein modernes STT-Modell lokal auf AMD/Intel-GPU: Parakeet TDT 0.6b v3 über onnxruntime-directml. ~0,3 s Warm-Latenz, 600 MB Erstladung — und ein fieser Versions-Stolperstein.

Für meine Diktier-App VoiceFlow brauchte ich eine Sache, die überraschend schwer zu bekommen ist: schnelle, lokale Spracherkennung auf einem Rechner ohne NVIDIA-Karte. Die meisten guten STT-Stacks gehen stillschweigend von CUDA aus. Ich habe hier aber eine AMD/Intel-GPU — und genau dafür gibt es einen unterschätzten Weg: ONNX-Runtime mit dem DirectML-Execution-Provider. Das nutzt jede DirectX-12-GPU, egal von welchem Hersteller.

Das Setup: Parakeet TDT 0.6b v3 via onnx-asr

Mein Modell der Wahl ist NVIDIAs Parakeet TDT 0.6b v3 — ironischerweise ein NVIDIA-Modell, das hier komplett ohne NVIDIA-Hardware läuft. Es ist multilingual (Deutsch + Englisch mit Auto-Erkennung), was für mich entscheidend war, weil ich beim Diktieren ständig zwischen den Sprachen wechsle.

Die Anbindung übernimmt das Paket onnx-asr. Die ganze Engine ist erfreulich kurz. Der Kern sieht so aus:

MODEL_ID = "nemo-parakeet-tdt-0.6b-v3"
DEFAULT_PROVIDERS = ["DmlExecutionProvider", "CPUExecutionProvider"]

import onnx_asr
self.model = onnx_asr.load_model(
    MODEL_ID, quantization=quantization, providers=self.providers
)

Der providers-Trick ist die ganze Magie: DmlExecutionProvider zuerst, CPUExecutionProvider als Fallback. ONNX-Runtime nimmt den ersten, der auf der Maschine verfügbar ist. Geht DirectML aus irgendeinem Grund nicht, fällt es still auf die CPU zurück — die App stürzt nicht ab, sie wird nur langsamer.

Ein Detail aus der transcribe-Methode, das ich erst nach einem kryptischen Fehler eingebaut habe: Das Audio muss zwingend ein zusammenhängendes float32-Array sein, sonst meckert die Runtime.

audio = np.ascontiguousarray(audio, dtype=np.float32)
if audio.ndim > 1:  # versehentliche (n, 1)-Formen platt machen
    audio = audio.reshape(-1)

sounddevice liefert je nach Konfiguration gern mal eine (n, 1)-Form statt (n,). Das reshape(-1) macht das platt, bevor es Probleme gibt.

Die Zahlen: 600 MB einmal, dann 0,3 Sekunden

Beim allerersten Start lädt Parakeet einmalig ~600 MB von Hugging Face und cacht das Modell danach lokal. Das ist eine spürbare Wartezeit beim ersten Lauf, aber eben nur einmal.

Danach ist die Latenz das eigentliche Verkaufsargument: warm rund 0,3 Sekunden pro Diktat. Push-to-talk Taste loslassen, kurz blinzeln, Text ist da. Für eine Diktier-App, die sich nicht im Weg stehen darf, ist das genau die Schwelle, ab der es sich „instant" anfühlt.

Zum Verifizieren habe ich einen kleinen Smoke-Test (_smoke()) in die Datei gepackt, der ganz ohne Mikrofon auskommt — er feuert 2 Sekunden leises Rauschen durch die Pipeline und misst Load- und Inferenzzeit getrennt:

audio = (rng.standard_normal(SAMPLE_RATE * 2) * 0.01).astype(np.float32)
out = engine.transcribe(audio)

So konnte ich den Load+Inferenz-Pfad schon validieren, bevor überhaupt Audio-Capture stand. Solche De-Risk-Tests am Anfang eines Milestones haben mir hier viel Frust erspart.

Der Stolperstein, der mich am meisten gekostet hat

Wenn ihr nur eine Sache aus diesem Post mitnehmt, dann diese:

Installiert nur EINE onnxruntime-Variante. Niemals onnxruntime neben onnxruntime-directml.

Beide Pakete liefern dasselbe Python-Modul onnxruntime. Hat man beide im venv, gewinnt mal das eine, mal das andere — und plötzlich ist der DmlExecutionProvider nicht mehr da, alles läuft still auf der CPU, und man wundert sich, warum „die GPU-Beschleunigung" auf einmal weg ist. Der Fehler ist heimtückisch, weil nichts crasht. Es wird einfach langsam.

Zweiter Versions-Fallstrick, den ich mir notiert habe: onnxruntime-directml 1.24.1 funktioniert nicht — es braucht mindestens 1.24.2. Bei 1.24.1 sprang der DirectML-Provider bei mir nicht an. Eine Minor-Patch-Version Unterschied, und der ganze GPU-Pfad ist tot.

Fazit

Der Stack — onnx-asr + onnxruntime-directml + Parakeet TDT 0.6b v3 — gibt mir lokale, mehrsprachige Spracherkennung mit ~0,3 s Latenz auf Hardware, die offiziell „kein KI-Rechner" ist. Kein CUDA, keine Cloud, keine API-Kosten, alles bleibt auf dem Gerät.

Die Lehre: Die Technik selbst ist erstaunlich unkompliziert — die echte Arbeit steckt in der Paket-Hygiene. Eine saubere requirements.txt mit genau einer onnxruntime-Variante und gepinnter Mindestversion ist hier wichtiger als jede Codezeile.