流式听写

Parakeet-EOU-120M 是一个小型 RNN-T 流式 ASR 模型,带显式的句末(EOU)头,专为 Apple Silicon Neural Engine 上的实时听写而构建。本指南同时介绍 DictateDemo——macOS 菜单栏参考应用,它把流式模型与 Silero VAD 串起来,实现免手、可粘贴到任意应用的听写体验。

它是什么

架构

每个音频 chunk 依次通过 3 个 CoreML 模型:

组件说明
EncoderCache-aware Conformer。接收一个 64 帧的 mel chunk(640 ms)加上 6 个状态张量——注意力 KV cache、depthwise conv cache,以及一个 pre_cache mel 回环,用来在 chunk 边界前面接上最近的历史音频,让 FFT 看到连续信号。
Decoder单步 LSTM 预测网络。消费前一个非 blank token,输出一个 embedding 和更新后的 (h, c) 状态。
Joint + EOU 头将编码器和解码器输出融合为 vocab + blank + EOU 上的 logits。EOU 类别是模型给出的一段话已结束的硬信号。

为什么需要单独的 EOU token

普通 RNNT 在静音时会发出 blank,解码器会愉快地吸收它,却并不发出"一段话已结束"的信号。独立的 EOU 头让模型可以做出硬切,把 partial 提交为 final,重置标点/大小写状态,并触发诸如粘贴到应用等下游动作。

EOU 在现实环境下是嘈杂的

键盘敲击、鼠标移动,以及"安静"停顿中的房间底噪,都可能让 joint 偶尔发出一个非 blank token,从而重置 EOU 去抖计时器,卡住提交。生产流水线会把 joint 的 EOU 与外部 VAD 驱动的 forceEndOfUtterance() 兜底配对——见下方的 DictateDemo。

模型

模型大小HuggingFace
Parakeet-EOU-120M (CoreML INT8)~120 MBaufklarer/Parakeet-EOU-120M-CoreML-INT8

性能

指标
权重内存~120 MB (INT8)
推理峰值内存~200 MB
Chunk 延迟(M 系列)~30 ms 计算 / 640 ms 音频(RTF ~0.056)
端到端 partial 延迟~340 ms(一个 chunk)
提交延迟(VAD 路径)停止说话后 ~1 s
计算目标Neural Engine (CoreML)

快速开始 — 批量转写

流式模型同样符合 SpeechRecognitionModel,因此可以直接替换任何需要通用 STT 模型的代码:

import ParakeetStreamingASR

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

快速开始 — 异步流式

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

每个 PartialTranscript 携带 textisFinalconfidenceeouDetected(joint 触发 vs. 强制提交),以及单调递增的 segmentIndex

长生命周期会话 API(麦克风输入)

在实时听写中,创建一次 session,然后在麦克风音频到来时逐块推给它。session 会在内部缓冲,并在累计到足够的样本后运行编码器,因此你可以推送任意大小的 chunk:

let session = try model.createSession()

// 每个麦克风 chunk:
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()

VAD 强制提交模式

如果你的流水线中已经在运行 Silero VAD,就用它驱动一个兜底提交,这样背景噪声就无法卡住 EOU 去抖计时器:

if hasPendingUtterance && !vadSpeechActive && vadSilentChunks >= 30 {
    // 按 Silero 计算,大约 960 ms 持续静音
    if let forced = session.forceEndOfUtterance() {
        commit(forced.text)
    }
    hasPendingUtterance = false
}

// 保护措施:joint 已经触发 EOU 时不要重复提交
if partials.contains(where: { $0.isFinal }) {
    hasPendingUtterance = false
}

DictateDemo — macOS 菜单栏参考应用

DictateDemo 是一个完整的 macOS 菜单栏 agent,基于流式 session 构建。它作为后台 app 运行,实时从麦克风转写并显示部分结果,在 EOU 或 VAD 静音时自动提交,并将结果粘贴到最前面的应用。

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

完整实现位于 Examples/DictateDemo/DictateDemo/DictateViewModel.swift:带锁保护缓冲区的非主线程音频 sink、300 ms 定时器的抽取逻辑、带残余样本携带的 Silero VAD,以及带保护的强制提交。匹配的回归测试见 Examples/DictateDemo/Tests/DictateDemoTests.swift,覆盖多段话、EOU 卡住和嘈杂静音等场景。

流式 Parakeet vs 批量 Parakeet

Parakeet-EOU-120M(流式)Parakeet TDT 0.6B(批量)
用途实时听写、实时字幕文件转写、离线任务
解码器RNN-T + EOU 头Token-and-Duration Transducer
Chunk 大小640 ms 流式整个文件批处理
权重内存~120 MB500 MB
吞吐量~18 倍实时~32 倍实时
延迟~340 ms partial仅在文件结束时
什么时候选择流式模型

当你需要在用户说完之前就得到部分结果时,选流式模型。对于音频文件的批量转写,更大的 Parakeet TDT 0.6B 在端到端上更快,也更准确。两个模型共享同一个 SentencePiece 词表,因此可以无缝切换而不改变分词。