Потоковая диктовка
Parakeet-EOU-120M — это небольшая потоковая ASR-модель RNN-T с явной головой end-of-utterance (EOU), созданная для диктовки в реальном времени на Neural Engine Apple Silicon. Это руководство также охватывает DictateDemo — референсное приложение в меню-баре macOS, которое связывает потоковую модель с Silero VAD для диктовки без рук со вставкой в любое место.
Что это
- Живые частичные результаты — текст обновляется по мере речи, ~340 мс после каждого чанка
- Явный EOU — модель сама решает, когда заканчивается реплика, без ручной кнопки
- Force-finalize по VAD — Silero как запасной вариант подтверждает реплики, даже когда EOU зависает на фоновом шуме
- 120 МБ INT8 CoreML — работает на Neural Engine, оставляя GPU свободным для других моделей
- 25 европейских языков — та же семья словарей, что и у исходного NeMo Parakeet TDT
Архитектура
Три CoreML-модели, работающие в пайплайне для каждого аудиочанка:
| Компонент | Описание |
|---|---|
| Encoder | Cache-aware Conformer. Принимает 64-кадровый мел-чанк (640 мс) плюс шесть тензоров состояния — KV-кеш attention, кеш depthwise-свёртки и pre_cache мел-обратной связи, который добавляет аудио из недавнего прошлого, чтобы FFT видел непрерывный сигнал на границах чанков. |
| Decoder | Одношаговая LSTM-сеть предсказания. Принимает предыдущий не-blank токен, выдаёт эмбеддинг плюс обновлённое состояние (h, c). |
| Joint + EOU head | Сливает выходы encoder и decoder в логиты по vocab + blank + EOU. Класс EOU — это жёсткий сигнал модели о том, что реплика закончена. |
Зачем отдельный EOU-токен
Обычный RNNT выдаёт blank-токены во время тишины, которые decoder спокойно поглощает, не сигнализируя «реплика завершена». Выделенная EOU-голова позволяет модели сделать жёсткий срез для подтверждения частичного результата как финального, сброса состояния пунктуации/регистра и запуска последующих действий вроде вставки в приложение.
Клики клавиатуры, движения мыши и комнатный шум во время «тихой» паузы могут приводить к тому, что joint изредка выдаёт не-blank токен, сбрасывая таймер дебаунса EOU и останавливая коммит. Продакшн-пайплайны сочетают EOU от joint с внешним forceEndOfUtterance() на основе VAD — см. DictateDemo ниже.
Модель
| Модель | Размер | HuggingFace |
|---|---|---|
| Parakeet-EOU-120M (CoreML INT8) | ~120 МБ | aufklarer/Parakeet-EOU-120M-CoreML-INT8 |
Производительность
| Метрика | Значение |
|---|---|
| Память весов | ~120 МБ (INT8) |
| Пиковая память инференса | ~200 МБ |
| Задержка чанка (M-серия) | ~30 мс вычислений / 640 мс аудио (RTF ~0.056) |
| Сквозная задержка частичных результатов | ~340 мс (один чанк) |
| Задержка коммита (путь VAD) | ~1 с после окончания речи |
| Цель вычислений | Neural Engine (CoreML) |
Быстрый старт — пакетная транскрипция
Потоковая модель также соответствует SpeechRecognitionModel, так что она работает как замена для любого кода, принимающего универсальную STT-модель:
import ParakeetStreamingASR
let model = try await ParakeetStreamingASRModel.fromPretrained()
let text = try model.transcribeAudio(audioSamples, sampleRate: 16000)
Быстрый старт — async-потоковый режим
for await partial in model.transcribeStream(audio: samples, sampleRate: 16000) {
if partial.isFinal { print("FINAL: \(partial.text)") }
else { print("... \(partial.text)") }
}
Каждый PartialTranscript содержит text, isFinal, confidence, eouDetected (joint сработал или force-finalize) и монотонный segmentIndex.
API долгоживущей сессии (вход с микрофона)
Для живой диктовки создайте сессию один раз и подавайте ей чанки по мере их поступления с микрофона. Сессия внутренне буферизирует и запускает encoder, когда накопится достаточно сэмплов, поэтому вы можете отправлять чанки произвольного размера:
let session = try model.createSession()
// каждый чанк с микрофона:
let partials = try session.pushAudio(float32Chunk16kHz)
for p in partials {
if p.isFinal { commit(p.text) }
else { showPartial(p.text) }
}
// когда поток заканчивается:
let trailing = try session.finalize()
Паттерн force-finalize по VAD
Когда в вашем пайплайне уже работает Silero VAD, используйте его для управления запасным коммитом, чтобы фоновый шум не останавливал таймер дебаунса EOU:
if hasPendingUtterance && !vadSpeechActive && vadSilentChunks >= 30 {
// ~960 мс непрерывной тишины по Silero
if let forced = session.forceEndOfUtterance() {
commit(forced.text)
}
hasPendingUtterance = false
}
// защита: не делать двойной коммит, если joint уже выдал EOU
if partials.contains(where: { $0.isFinal }) {
hasPendingUtterance = false
}
DictateDemo — референсное приложение в меню-баре macOS
DictateDemo — это полноценный агент в меню-баре macOS, построенный поверх потоковой сессии. Работает как фоновое приложение, транскрибирует с микрофона с живыми частичными результатами, автоматически подтверждает реплики по EOU или тишине VAD и вставляет результат в активное приложение.
- Приложение в меню-баре с глобальным хоткеем
Cmd+Shift+D - Живые частичные результаты с плавающим HUD и индикатором уровня звука
- Force-finalize с защитой по VAD (продакшн-паттерн выше)
- Вставка в активное приложение по
Cmd+Shift+V - Модель автоматически скачивается при первом запуске (~120 МБ)
cd Examples/DictateDemo
swift build
.build/debug/DictateDemo
Полная реализация находится в Examples/DictateDemo/DictateDemo/DictateViewModel.swift: аудио-приёмник не на главном потоке с буфером под lock, тик таймера на 300 мс, который его сливает, Silero VAD с переносом остаточных сэмплов и защищённый force-finalize. Соответствующие регрессионные тесты в Examples/DictateDemo/Tests/DictateDemoTests.swift покрывают сценарии нескольких реплик, залипшего EOU и шумной тишины.
Потоковый vs пакетный Parakeet
| Parakeet-EOU-120M (потоковый) | Parakeet TDT 0.6B (пакетный) | |
|---|---|---|
| Сценарий | Живая диктовка, субтитры в реальном времени | Транскрипция файлов, офлайн-задачи |
| Декодер | RNN-T + EOU-голова | Token-and-Duration Transducer |
| Размер чанка | 640 мс, потоковый | Весь файл, пакетно |
| Память весов | ~120 МБ | 500 МБ |
| Пропускная способность | ~18x быстрее реального времени | ~32x быстрее реального времени |
| Задержка | ~340 мс частичные | Только в конце файла |
…нужны частичные результаты до того, как пользователь закончит говорить. Для пакетной транскрипции аудиофайлов более крупная Parakeet TDT 0.6B быстрее end-to-end и точнее. Обе модели используют один и тот же словарь SentencePiece, так что можно переключаться между ними без изменения токенизации.