Streaming-Diktat

Parakeet-EOU-120M ist ein kleines RNN-T-Streaming-ASR-Modell mit explizitem End-of-Utterance-Head (EOU), gebaut für Echtzeit-Diktat auf der Neural Engine von Apple Silicon. Diese Anleitung behandelt auch DictateDemo, die macOS-Menüleisten-Referenz-App, die das Streaming-Modell mit Silero VAD zu einem freihändigen Diktat mit "Einfügen überall" verdrahtet.

Was es ist

Architektur

Drei CoreML-Modelle werden pro Audio-Chunk in einer Pipeline ausgeführt:

KomponenteBeschreibung
EncoderCache-bewusster Conformer. Nimmt einen 64-Frame-Mel-Chunk (640 ms) plus sechs Zustandstensoren entgegen — Attention-KV-Cache, Depthwise-Conv-Cache und ein pre_cache-Mel-Loopback, der Audio aus der jüngsten Vergangenheit voranstellt, damit die FFT ein kontinuierliches Signal über Chunk-Grenzen hinweg sieht.
DecoderEinschritt-LSTM-Prediction-Netzwerk. Konsumiert das vorherige Non-Blank-Token und gibt ein Embedding plus aktualisierten (h, c)-Zustand aus.
Joint + EOU-HeadFusioniert Encoder- und Decoder-Ausgaben zu Logits über vocab + blank + EOU. Die EOU-Klasse ist das harte Signal des Modells, dass eine Äußerung abgeschlossen ist.

Warum ein separates EOU-Token

Einfaches RNNT gibt in Pausen Blanks aus, die der Decoder widerspruchslos absorbiert, ohne "Äußerung beendet" zu signalisieren. Ein dedizierter EOU-Head erlaubt dem Modell einen harten Schnitt, um den Partial zu einem Finalen zu committen, den Zeichensetzungs-/Großschreibungs-Zustand zurückzusetzen und nachgelagerte Aktionen wie "in App einfügen" auszulösen.

EOU ist in der realen Welt verrauscht

Tastaturklicks, Mausbewegungen und Raumton während einer "stillen" Pause können dazu führen, dass der Joint gelegentlich ein Non-Blank-Token ausgibt, was den EOU-Debounce-Timer zurücksetzt und den Commit verzögert. Produktive Pipelines koppeln das Joint-EOU mit einem externen VAD-gesteuerten forceEndOfUtterance()-Backstop — siehe DictateDemo unten.

Modell

ModellGrößeHuggingFace
Parakeet-EOU-120M (CoreML INT8)~120 MBaufklarer/Parakeet-EOU-120M-CoreML-INT8

Leistung

MetrikWert
Gewichtsspeicher~120 MB (INT8)
Spitzen-Inferenzspeicher~200 MB
Chunk-Latenz (M-Serie)~30 ms Compute / 640 ms Audio (RTF ~0,056)
Partial-Latenz Ende-zu-Ende~340 ms (ein Chunk)
Commit-Latenz (VAD-Pfad)~1 s nach Sprechende
Compute-ZielNeural Engine (CoreML)

Schnellstart — Batch-Transkription

Das Streaming-Modell erfüllt auch SpeechRecognitionModel und funktioniert damit als Drop-in-Ersatz für jeden Code, der ein generisches STT-Modell erwartet:

import ParakeetStreamingASR

let model = try await ParakeetStreamingASRModel.fromPretrained()
let text = try model.transcribeAudio(audioSamples, sampleRate: 16000)

Schnellstart — Async-Streaming

for await partial in model.transcribeStream(audio: samples, sampleRate: 16000) {
    if partial.isFinal { print("FINAL: \(partial.text)") }
    else               { print("... \(partial.text)") }
}

Jedes PartialTranscript enthält text, isFinal, confidence, eouDetected (Joint hat ausgelöst vs. Force-Finalized) und einen monotonen segmentIndex.

Langlebige Session-API (Mikrofoneingang)

Für Live-Diktat legst du einmal eine Session an und fütterst sie mit Chunks, sobald sie vom Mikrofon eintreffen. Die Session puffert intern und führt den Encoder aus, wenn genügend Samples angesammelt sind, sodass du beliebige Chunk-Größen pushen kannst:

let session = try model.createSession()

// je Mikrofon-Chunk:
let partials = try session.pushAudio(float32Chunk16kHz)
for p in partials {
    if p.isFinal { commit(p.text) }
    else          { showPartial(p.text) }
}

// wenn der Stream endet:
let trailing = try session.finalize()

VAD-Force-Finalize-Muster

Wenn in deiner Pipeline bereits ein Silero VAD läuft, nutze es als Fallback-Commit, damit Hintergrundgeräusche den EOU-Debounce-Timer nicht blockieren können:

if hasPendingUtterance && !vadSpeechActive && vadSilentChunks >= 30 {
    // ~960 ms anhaltende Stille laut Silero
    if let forced = session.forceEndOfUtterance() {
        commit(forced.text)
    }
    hasPendingUtterance = false
}

// Sicherung: kein doppeltes Commit, wenn der Joint bereits EOU ausgelöst hat
if partials.contains(where: { $0.isFinal }) {
    hasPendingUtterance = false
}

DictateDemo — macOS-Menüleisten-Referenz-App

DictateDemo ist ein vollständiger macOS-Menüleisten-Agent, der auf der Streaming-Session aufbaut. Er läuft als Hintergrund-App, transkribiert vom Mikrofon mit Live-Teilergebnissen, committet Äußerungen automatisch bei EOU oder VAD-Stille und fügt Ergebnisse in die vorderste App ein.

cd Examples/DictateDemo
swift build
.build/debug/DictateDemo

Die vollständige Implementierung lebt in Examples/DictateDemo/DictateDemo/DictateViewModel.swift: eine Off-Main-Audio-Senke mit lock-geschütztem Puffer, ein 300-ms-Timer-Tick, der ihn leert, Silero VAD mit Übertragung restlicher Samples und ein abgesichertes Force-Finalize. Die passenden Regressionstests in Examples/DictateDemo/Tests/DictateDemoTests.swift decken Mehrfach-Äußerungen, hängenbleibende EOU und Szenarien mit lauter Stille ab.

Streaming- vs. Batch-Parakeet

Parakeet-EOU-120M (Streaming)Parakeet TDT 0.6B (Batch)
AnwendungsfallLive-Diktat, Echtzeit-UntertitelungDateitranskription, Offline-Jobs
DecoderRNN-T + EOU-HeadToken-and-Duration-Transducer
Chunk-Größe640 ms StreamingBatch über ganze Datei
Gewichtsspeicher~120 MB500 MB
Durchsatz~18-fache Echtzeit~32-fache Echtzeit
Latenz~340 ms TeilergebnisseNur am Dateiende
Wähle das Streaming-Modell, wenn…

…du Teilergebnisse brauchst, bevor der Benutzer fertig spricht. Für Batch-Transkription von Audiodateien ist das größere Parakeet TDT 0.6B Ende-zu-Ende schneller und genauer. Beide Modelle teilen sich dasselbe SentencePiece-Vokabular, sodass du zwischen ihnen wechseln kannst, ohne die Tokenisierung zu ändern.