Dictée en streaming

Parakeet-EOU-120M est un petit modèle ASR streaming RNN-T avec une tête explicite de fin d'énoncé (EOU), conçu pour la dictée en temps réel sur le Neural Engine Apple Silicon. Ce guide couvre également DictateDemo, l'application de référence en barre de menus macOS qui relie le modèle streaming à Silero VAD pour une dictée mains libres et collable partout.

De quoi il s'agit

Architecture

Trois modèles CoreML pipelinés par chunk audio :

ComposantDescription
EncodeurConformer sensible au cache. Prend un chunk mel de 64 trames (640 ms) plus six tenseurs d'état — cache KV d'attention, cache de conv depthwise et un pre_cache mel en boucle qui préfixe l'audio récent pour que la FFT voie un signal continu entre les frontières de chunks.
DécodeurRéseau de prédiction LSTM à étape unique. Consomme le token non-blanc précédent, émet un embedding plus un état (h, c) mis à jour.
Joint + tête EOUFusionne les sorties de l'encodeur et du décodeur en logits sur vocab + blank + EOU. La classe EOU est le signal dur du modèle qu'un énoncé est terminé.

Pourquoi un token EOU séparé

Un RNNT ordinaire émet des blancs pendant le silence, que le décodeur absorbe volontiers sans signaler « énoncé terminé ». Une tête EOU dédiée permet au modèle de faire une coupure dure pour commettre le partiel en final, réinitialiser l'état de ponctuation/majuscules et déclencher des actions en aval comme le collage dans une app.

L'EOU est bruité dans le monde réel

Les clics de clavier, les mouvements de souris et les tonalités de la pièce pendant une pause « silencieuse » peuvent faire que le joint émette occasionnellement un token non-blanc, réinitialisant le timer de debounce EOU et stoppant le commit. Les pipelines de production couplent le joint EOU à un filet de sécurité forceEndOfUtterance() piloté par VAD externe — voir DictateDemo ci-dessous.

Modèle

ModèleTailleHuggingFace
Parakeet-EOU-120M (CoreML INT8)~120 Moaufklarer/Parakeet-EOU-120M-CoreML-INT8

Performance

MétriqueValeur
Mémoire des poids~120 Mo (INT8)
Mémoire d'inférence en pic~200 Mo
Latence par chunk (série M)~30 ms de calcul / 640 ms d'audio (RTF ~0,056)
Latence des partiels de bout en bout~340 ms (un chunk)
Latence de commit (chemin VAD)~1 s après arrêt de la parole
Cible de calculNeural Engine (CoreML)

Démarrage rapide — transcription par batch

Le modèle streaming se conforme également à SpeechRecognitionModel, donc il fonctionne en remplacement direct pour tout code qui prend un modèle STT générique :

import ParakeetStreamingASR

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

Démarrage rapide — streaming async

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

Chaque PartialTranscript porte text, isFinal, confidence, eouDetected (joint déclenché vs force-finalized) et un segmentIndex monotone.

API de session longue durée (entrée micro)

Pour la dictée en direct, créez une session une fois et alimentez-la en chunks à mesure qu'ils arrivent du micro. La session bufferise en interne et exécute l'encodeur lorsqu'assez d'échantillons s'accumulent, vous permettant donc de pousser des chunks de taille arbitraire :

let session = try model.createSession()

// chaque chunk micro :
let partials = try session.pushAudio(float32Chunk16kHz)
for p in partials {
    if p.isFinal { commit(p.text) }
    else          { showPartial(p.text) }
}

// quand le flux se termine :
let trailing = try session.finalize()

Motif de force-finalize via VAD

Lorsqu'un Silero VAD tourne déjà dans votre pipeline, utilisez-le pour piloter un commit de repli afin que le bruit de fond ne puisse pas stopper le timer de debounce EOU :

if hasPendingUtterance && !vadSpeechActive && vadSilentChunks >= 30 {
    // ~960 ms de silence soutenu selon Silero
    if let forced = session.forceEndOfUtterance() {
        commit(forced.text)
    }
    hasPendingUtterance = false
}

// garde-fou : ne pas commettre deux fois si le joint a déjà déclenché EOU
if partials.contains(where: { $0.isFinal }) {
    hasPendingUtterance = false
}

DictateDemo — application de référence en barre de menus macOS

DictateDemo est un agent complet de barre de menus macOS construit au-dessus de la session streaming. Il s'exécute en arrière-plan, transcrit depuis le micro avec des partiels en direct, auto-commet les énoncés sur EOU ou silence VAD, et colle les résultats dans l'app au premier plan.

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

L'implémentation complète se trouve dans Examples/DictateDemo/DictateDemo/DictateViewModel.swift : un sink audio hors du main thread avec un buffer protégé par verrou, un tick de timer à 300 ms qui le draine, Silero VAD avec report des échantillons restants, et un force-finalize gardé. Les tests de régression correspondants dans Examples/DictateDemo/Tests/DictateDemoTests.swift couvrent les scénarios multi-énoncés, EOU bloqué et silence bruité.

Streaming vs batch Parakeet

Parakeet-EOU-120M (streaming)Parakeet TDT 0.6B (batch)
Cas d'usageDictée en direct, sous-titrage temps réelTranscription de fichiers, jobs hors ligne
DécodeurRNN-T + tête EOUToken-and-Duration Transducer
Taille de chunkStreaming 640 msBatch sur fichier entier
Mémoire des poids~120 Mo500 Mo
Débit~18× temps réel~32× temps réel
LatencePartiels ~340 msFin de fichier uniquement
Choisissez le modèle streaming quand…

…vous avez besoin de partiels avant que l'utilisateur ait fini de parler. Pour la transcription par batch de fichiers audio, le plus grand Parakeet TDT 0.6B est plus rapide de bout en bout et plus précis. Les deux modèles partagent le même vocabulaire SentencePiece, vous pouvez donc basculer entre eux sans changer la tokenisation.