Đọc chính tả streaming
Parakeet-EOU-120M là một mô hình ASR streaming RNN-T nhỏ với một đầu ra phát hiện kết thúc câu nói (EOU) rõ ràng, được xây dựng cho đọc chính tả thời gian thực trên Neural Engine của Apple Silicon. Hướng dẫn này cũng đề cập đến DictateDemo, ứng dụng tham chiếu trên thanh menu macOS kết nối mô hình streaming với Silero VAD để đọc chính tả rảnh tay, dán-mọi-nơi.
Đây là gì
- Bản tạm thời trực tiếp — văn bản cập nhật khi bạn nói, ~340 ms sau mỗi chunk
- EOU rõ ràng — mô hình quyết định khi nào một câu nói kết thúc, không cần nút thủ công
- Force-finalize điều khiển bởi VAD — Silero làm chỗ dựa đẩy commit câu nói ngay cả khi EOU bị đứng vì tiếng ồn nền
- 120 MB INT8 CoreML — chạy trên Neural Engine, để GPU rảnh cho các mô hình khác
- 25 ngôn ngữ châu Âu — cùng họ vocabulary với NeMo Parakeet TDT gốc
Kiến trúc
Ba mô hình CoreML được nối ống theo từng chunk âm thanh:
| Thành phần | Mô tả |
|---|---|
| Bộ mã hoá | Conformer có cache. Nhận một chunk mel 64 frame (640 ms) cùng sáu tensor trạng thái — cache KV attention, cache conv depthwise, và một loopback mel pre_cache chèn âm thanh quá khứ gần đây vào phía trước để FFT thấy tín hiệu liên tục qua các biên chunk. |
| Bộ giải mã | Mạng dự đoán LSTM một bước. Nhận token non-blank trước đó, sinh ra embedding cùng với trạng thái (h, c) đã cập nhật. |
| Joint + đầu ra EOU | Hợp nhất đầu ra của bộ mã hoá và bộ giải mã thành logit trên vocab + blank + EOU. Lớp EOU là tín hiệu rõ ràng của mô hình rằng một câu nói đã kết thúc. |
Tại sao cần một token EOU riêng
RNNT thuần sinh ra blank trong khi im lặng, mà bộ giải mã vui vẻ hấp thụ mà không phát tín hiệu "câu nói đã kết thúc". Một đầu ra EOU chuyên dụng cho phép mô hình thực hiện một cú cắt dứt khoát để commit phần tạm thời thành phần cuối cùng, reset trạng thái dấu câu/viết hoa, và kích hoạt các hành động downstream như dán-vào-ứng-dụng.
Tiếng gõ phím, cử động chuột, và tiếng ồn phòng trong một khoảng "im lặng" có thể khiến joint thi thoảng sinh ra một token non-blank, reset bộ đếm debounce của EOU và làm đứng commit. Pipeline production ghép EOU của joint với một chỗ dựa forceEndOfUtterance() bên ngoài điều khiển bởi VAD — xem DictateDemo bên dưới.
Mô hình
| Mô hình | Kích thước | HuggingFace |
|---|---|---|
| Parakeet-EOU-120M (CoreML INT8) | ~120 MB | aufklarer/Parakeet-EOU-120M-CoreML-INT8 |
Hiệu năng
| Chỉ số | Giá trị |
|---|---|
| Bộ nhớ trọng số | ~120 MB (INT8) |
| Bộ nhớ suy luận đỉnh | ~200 MB |
| Độ trễ mỗi chunk (chip series M) | ~30 ms tính toán / 640 ms âm thanh (RTF ~0.056) |
| Độ trễ từng phần end-to-end | ~340 ms (một chunk) |
| Độ trễ commit (đường VAD) | ~1 s sau khi ngừng nói |
| Đích tính toán | Neural Engine (CoreML) |
Bắt đầu nhanh — phiên âm theo lô
Mô hình streaming cũng tuân thủ SpeechRecognitionModel, nên nó hoạt động như một thay thế trực tiếp trong bất kỳ mã nào nhận một mô hình STT chung:
import ParakeetStreamingASR
let model = try await ParakeetStreamingASRModel.fromPretrained()
let text = try model.transcribeAudio(audioSamples, sampleRate: 16000)
Bắt đầu nhanh — streaming bất đồng bộ
for await partial in model.transcribeStream(audio: samples, sampleRate: 16000) {
if partial.isFinal { print("FINAL: \(partial.text)") }
else { print("... \(partial.text)") }
}
Mỗi PartialTranscript mang text, isFinal, confidence, eouDetected (do joint kích hoạt vs. force-finalize), và một segmentIndex đơn điệu.
API session dài hạn (đầu vào micro)
Cho đọc chính tả trực tiếp, tạo session một lần rồi đẩy chunk vào khi nhận được từ micro. Session đệm bên trong và chạy bộ mã hoá khi tích luỹ đủ mẫu, nên bạn có thể đẩy chunk với kích thước tuỳ ý:
let session = try model.createSession()
// mỗi chunk từ micro:
let partials = try session.pushAudio(float32Chunk16kHz)
for p in partials {
if p.isFinal { commit(p.text) }
else { showPartial(p.text) }
}
// khi stream kết thúc:
let trailing = try session.finalize()
Mẫu force-finalize với VAD
Khi đã có một Silero VAD chạy trong pipeline, hãy dùng nó để kích hoạt một commit dự phòng để tiếng ồn nền không thể làm đứng bộ đếm debounce của EOU:
if hasPendingUtterance && !vadSpeechActive && vadSilentChunks >= 30 {
// ~960 ms im lặng kéo dài theo Silero
if let forced = session.forceEndOfUtterance() {
commit(forced.text)
}
hasPendingUtterance = false
}
// chốt chặn: đừng commit hai lần nếu joint đã kích hoạt EOU
if partials.contains(where: { $0.isFinal }) {
hasPendingUtterance = false
}
DictateDemo — ứng dụng tham chiếu trên thanh menu macOS
DictateDemo là một agent hoàn chỉnh trên thanh menu macOS được xây trên session streaming. Nó chạy như một ứng dụng nền, phiên âm từ micro với bản tạm thời trực tiếp, tự động commit câu nói khi EOU hoặc khi VAD im lặng, và dán kết quả vào ứng dụng đang ở phía trước.
- Ứng dụng trên thanh menu với phím tắt toàn cục
Cmd+Shift+D - Bản tạm thời trực tiếp với HUD nổi và chỉ báo mức âm thanh
- Force-finalize được VAD bảo vệ (mẫu production ở trên)
- Dán vào ứng dụng phía trước với
Cmd+Shift+V - Mô hình tự động tải về khi lần đầu khởi chạy (~120 MB)
cd Examples/DictateDemo
swift build
.build/debug/DictateDemo
Toàn bộ phần triển khai nằm trong Examples/DictateDemo/DictateDemo/DictateViewModel.swift: một sink âm thanh ngoài luồng main với một buffer được bảo vệ bằng lock, một tick timer 300 ms để rút buffer, Silero VAD với truyền tiếp các mẫu thừa, và một force-finalize có bảo vệ. Các test hồi quy tương ứng trong Examples/DictateDemo/Tests/DictateDemoTests.swift bao gồm các kịch bản nhiều câu nói, EOU bị kẹt, và im lặng có nhiễu.
Streaming so với Parakeet theo lô
| Parakeet-EOU-120M (streaming) | Parakeet TDT 0.6B (lô) | |
|---|---|---|
| Tình huống dùng | Đọc chính tả trực tiếp, phụ đề thời gian thực | Phiên âm tệp, công việc offline |
| Bộ giải mã | RNN-T + đầu ra EOU | Token-and-Duration Transducer |
| Kích thước chunk | Streaming 640 ms | Theo lô toàn tệp |
| Bộ nhớ trọng số | ~120 MB | 500 MB |
| Throughput | ~18× thời gian thực | ~32× thời gian thực |
| Độ trễ | Bản tạm thời ~340 ms | Chỉ ở cuối tệp |
…bạn cần các bản tạm thời trước khi người dùng nói xong. Cho phiên âm theo lô các tệp âm thanh, Parakeet TDT 0.6B lớn hơn nhanh hơn end-to-end và chính xác hơn. Hai mô hình dùng chung cùng một vocabulary SentencePiece, nên bạn có thể chuyển đổi giữa chúng mà không cần thay đổi tokenization.