ESC
开源 3 分钟阅读

hayamimi: 早耳 - 仅CPU实时多语言语音转文字,无需GPU,无需云端

hayamimi(早耳)是一款仅依赖CPU的实时多语言语音转文字工具,支持实时字幕、浏览器仪表盘、说话人标签和翻译,无需GPU或云端API,内存占用低于2GB。它通过sherpa-onnx路由到多语言专用模型,在日语广播数据上CER仅5.8%,远低于whisper-large-v3-turbo的13.8%。项目开源,MIT许可。

来源:GitHub

hayamimi (早耳)

tests license release

仅CPU即可实现实时多语言语音转文字。 实时字幕、浏览器仪表盘、说话人标签、即时翻译——无需GPU,无需云端API,内存占用低于2GB。

日本語版 README は README.ja.md にあります。

“早耳” (hayamimi) 在日语中意为"快耳"——指反应迅速的人。这正是本项目的设计目标:你还在说话时,部分字幕就已出现;停下后约100毫秒内,最终字幕行即完成定格。

为什么

大多数仅CPU实时转录方案会退而采用单一通用模型(如Whisper),并接受其准确率上限。hayamimi则根据每句话的语言,将其路由到最合适的专用模型,全部以量化(INT8)ONNX模型运行于sherpa-onnx之上——无需PyTorch,无需CUDA。

在真实的日语广播音频测试中(见docs/SCORECARD.md),这种路由方案获得了5.8%的字错误率(CER),不到whisper-large-v3-turbo在相同片段上13.8%错误率的一半,同时在一颗6核桌面CPU上以10-50倍实时速度运行。

特性

特性说明
5路语言目录ja/zh/ko/yue/en+24种欧洲语言分别路由到各语言最优模型;其余语言(约1600种)回退到Meta的Omnilingual ASR
部分字幕说话过程中约每0.5秒更新一次进行中的草稿文本
快速最终结果停止说话后,最终字幕行通常约100毫秒内出现(日语;其他语言见docs/GOALS.md)
两遍精炼静音2秒后,对最近的语音段进行批量重新解码,获得更高准确率的"干净"转录(日语真实广播CER从15.5%降至12.0%)
说话人标签--speakers使用CAM++说话人嵌入为每句话标记S1/S2/…(区分轮流说话,非完整说话人分离)
翻译--translate en,zh,ko实时翻译日语字幕行(英译通过FuguMT,中/韩译通过M2M-100)
热词/用户词典--hotwords使解码偏向专有名词(目前对日语层级无效——见局限性);--replace进行事后查找替换,在所有语言下均有效
OBS叠加层+仪表盘--serve启动本地HTTP服务器,提供浏览器源叠加层和实时仪表盘
网络音频输入--input ws接受通过WebSocket传输的麦克风音频(手机、ESP32/stackchan),并送入同一处理管线,包括--serve的仪表盘/叠加层
内存受限LRU模型驱逐机制使驻留模型保持在可配置上限内(默认总内存<2GB)
仅CPU每个模型都通过sherpa-onnx以量化ONNX运行;无需GPU或PyTorch

演示界面

--serve启动一个本地服务器,提供三个视图:

  • http://localhost:8833/dashboard —— 实时仪表盘:进行中语音的部分文本条、带语言徽章的最终字幕流、说话人标签、每行延迟、每行下方的内联翻译,以及第二列显示精炼(两遍)转录结果。
  • http://localhost:8833/ —— 极简OBS浏览器源叠加层(在OBS中添加此URL作为浏览器源即可生成直播字幕)。
  • http://localhost:8833/transcript —— 纯滚动字幕历史。

dashboard

🎬 观看演示视频 —— 真实4语言音频(ja/en/ko/zh)实时转录,从录制会话中逐帧精确回放。

网络音频输入

--input ws启动WebSocket接收端点,而不是读取本地麦克风,因此手机或stackchan级ESP32开发板可以通过局域网传输麦克风音频,并在hayamimi的正常管线中完成转录:

.venv/Scripts/python scripts/realtime_transcribe.py --input ws --serve
# -> ws://<host>:8766/ingest接受音频;http://localhost:8833/dashboard显示结果

协议:连接到/ingest,发送一个JSON文本帧({"sr": 16000, "format": "pcm_s16le", "channels": 1}),然后以二进制帧流式传输原始pcm_s16le音频。服务器会对非16kHz音频进行重采样,并以仪表盘SSE流相同的JSON事件格式回复部分/最终/翻译/精炼结果,因此客户端也可以自行显示字幕。同时只接受一个音频生产客户端;scripts/ws_mic_client.py是一个无依赖的参考客户端(以实时速度流式传输wav文件),同时可作为手机/ESP32实现的模板。

要求

Python 3.10+,且ffmpeg在PATH中。在Windows 11上开发和测试;macOS/Linux预计也可运行(所有运行时均跨平台),但尚未完成端到端CI测试——欢迎反馈。

快速开始

python -m venv .venv

# Windows
.venv\Scripts\pip install -r requirements.txt
.venv\Scripts\python scripts\download_models.py

# macOS / Linux
.venv/bin/pip install -r requirements.txt
.venv/bin/python scripts/download_models.py

# 从麦克风进行实时转录
.venv/Scripts/python scripts/realtime_transcribe.py     # Windows
.venv/bin/python scripts/realtime_transcribe.py          # macOS/Linux

# 带仪表盘+OBS叠加层
.venv/Scripts/python scripts/realtime_transcribe.py --serve
# -> 在浏览器中打开 http://localhost:8833/dashboard

scripts/download_models.py会下载约3.1GB的预训练模型到models/目录(已被git忽略)。传入--minimal可仅安装约1.1GB的日/英版本(ReazonSpeech、whisper-tiny、Silero VAD、日语标点)。各模型许可证义务见THIRD_PARTY_NOTICES.md。

CLI参考

所有标志均位于scripts/realtime_transcribe.py:

标志默认值描述
--wav PATH麦克风输入从16kHz单声道WAV文件模拟流式输入,而不是使用麦克风
--no-realtime关配合--wav,在块之间不等待(快速批处理)
--input {mic,wav,ws}麦克风,或给出--wav时为wav音频源;ws通过网络接收音频(见下文)
--ws-host HOST0.0.0.0绑定--input ws的/ingest端点的主机
--ws-port PORT8766--input ws的/ingest端点端口
--threads N4每个模型的推理线程数
--no-partial关禁用进行中的草稿字幕
--min-silence SEC0.35结束一段语音的静音时长;越小则最终结果越敏捷,但分割更多
--max-speech SEC12.0连续说话达到该秒数后强制完成一段语音
--max-resident N3驻留的非tier0模型最大数量(LRU驱逐);<=0为无限
--serve [PORT]关,8833在http://localhost:PORT提供服务仪表盘+OBS叠加层
--no-refine关禁用第二遍对语音组的重新解码
--transcript PATH无将精炼后的转录行追加到该文件
--hotwords PATH无热词列表(每行一个),使解码偏向专有名词——目前对日语层级无效(ReazonSpeech的字节级BPE tokens.txt无法编码它们;启动时会警告有多少失败)。日语专有名词请改用--replace
--replace PATH无用户词典:每行错误=正确,应用于所有输出
--lang-switch-guard SEC2.0将短于该时长的新语言检测视为噪声:它永远不能计入确认切换(见--lid-switch-confirm),并会在空解码时抑制omnilingual回退(0禁用)
--lid-switch-confirm N2会话实际切换语言前需要连续的新语言检测次数(每次均>=--lang-switch-guard);提高该值可让单语言会话更稳定
--speakers关为语音段标记说话人ID(S1、S2、…)
--translate [LANGS]关,en将日语字幕行翻译为这些逗号分隔的语言(en/zh/ko)

架构

                          ┌─────────────┐
  mic / wav ───────────▶ │  Silero VAD │  0.35s语音结束 + 0.8s预卷
                          └──────┬──────┘
                                 │ 语音段
                                 ▼
                   ┌───────────────────────────┐
                   │  whisper-tiny 口语LID      │  在语音段仍输入期间,对前约4秒运行
                   │  (+ 字符集仲裁)            │
                   └─────────────┬─────────────┘
                                 │ 语言标签
                 ┌───────────────┼────────────────┬─────────────┬──────────────┐
                 ▼               ▼                ▼             ▼              ▼
             ┌───────┐      ┌─────────┐      ┌──────────┐  ┌─────────┐   ┌──────────┐
             │  ja   │      │   zh    │      │  ko/yue  │  │ en + 24 │   │  ~1600   │
             │ Reazon│      │Paraformer│      │SenseVoice│  │EU langs │   │  other   │
             │Speech │      │   -zh   │      │  small   │  │Parakeet │   │Omnilingual│
             │Zipform│      │         │      │          │  │TDT v3   │   │  ASR     │
             └───┬───┘      └────┬────┘      └────┬─────┘  └────┬────┘   └────┬─────┘
                 └───────────────┴────────────────┴─────────────┴─────────────┘
                                                │
                    部分字幕 (约每0.5秒)         │      最终结果 (语音结束后约0.1秒)
                     ◀───────────────────────────┴───────────────────────▶
                                                │
                          ┌─────────────────────┼─────────────────────┐
                          ▼                     ▼                     ▼
                 ┌────────────────┐   ┌──────────────────┐   ┌────────────────┐
                 │ 日语标点恢复    │   │ 说话人标签        │   │ 翻译            │
                 │ (BERT恢复)      │   │ (CAM++,--speakers)│   │ (FuguMT/M2M-100)│
                 └────────────────┘   └──────────────────┘   └────────────────┘
                                                │
                     2秒静音:对最近语音段进行批量重新解码(两遍精炼)
                                                │
                                                ▼
                              dashboard / OBS叠加层 / 字幕文件

模型在首次使用时惰性加载;LRU缓存会驱逐最近最少使用的非日语模型(--max-resident),因此无论会话涉及多少种语言,内存都保持有界。

实测性能

端到端(LID -> 路由 -> 解码 -> 日语标点),真实语音,无预卷/两遍(单片段)。en使用WER,其他语言使用CER(yue经过t2s标准化)。完整方法论见docs/SCORECARD.md。

语言片段数LID准确率路由平均错误率平均RTF
ja1515/15ReazonSpeech7.5%0.071
en1515/15Parakeet v32.3%0.109
zh1212/12Paraformer-zh5.3%0.102
ko1212/12SenseVoice8.1%0.062
yue1212/12SenseVoice6.1%0.061

所有路由的RTF(实时因子)均远低于0.2,意味着每条路由在纯CPU上运行速度比实时快9-16倍——完整目标表见docs/GOALS.md,完整迭代日志(30+项测量改动,延迟/内存/准确率权衡及各自取舍原因)见docs/BENCHMARKS.md。

该日志中的关键数据:

  • 日语CER 5.8%(束搜索)在真实广播音频上,对比whisper-large-v3-turbo相同片段的13.8%——错误率不到一半。
  • 平均最终延迟约100毫秒(日语,含标点);启用所有特性、5种语言浸泡测试下平均236毫秒/最大552毫秒。
  • <2GB内存 在--max-resident 3时(--max-resident 2时为1.35GB)。

局限性(诚实清单)

  • 不支持句内代码切换。 路由器为每句话选择一种语言;一句话内混合日语和英语时,次要语言部分会被破坏或丢弃。句级切换(例如口译员交替说完整句子)效果良好;句内词级切换不支持。
  • 过场音效/提示音/背景音乐后出现的极短语音可能被错误路由。 语言切换保护(--lang-switch-guard,配合--lid-switch-confirm)可缓解此问题,但会话的第一句话(在建立会话语言之前)以及自信但错误的LID+解码组合(乱码文本恰好匹配错误语言字符集)是已知盲区——量化前后对比见docs/BENCHMARKS.md迭代#29。
  • --hotwords目前对日语(ReazonSpeech)层级无效。 ReazonSpeech的tokens.txt是字节级BPE,与hayamimi用于热词的modeling_unit=cjkchar编码不兼容,因此每个热词都无法编码(sherpa-onnx仅以stderr警告报告,且仍以状态码0退出——见GitHub issue #1)。hayamimi现在会打印启动警告,告知有多少热词编码失败;日语专有名词请改用--replace。真正的修复需要ReazonSpeech发布版附带的匹配bpe.model(目前未提供),或从零编写字节BPE热词编码器——列为后续工作。
  • 两个重叠的说话人不会被分离。 --speakers进行的是轮流说话标注(每个VAD段一个嵌入,最近质心分配),而非真正的说话人分离——同时说话只会得到一个标签。
  • 翻译质量有实际上限,不仅是调参问题。 FuguMT(ja->en)和M2M-100(ja->zh/ko)是小模型;重复循环被抑制但未消除,且数字在ja->zh/ko翻译中不能可靠保留(依赖此功能处理任何数字或金融内容前,请先参阅docs/TRANSLATE.md和docs/TRANSLATE_M2M.md中的实测失败案例)。
  • 端到端麦克风管线尚未经项目自身测试以外的独立验证——见docs/GOALS.md的剩余工作部分。如果你的结果与上述数字不同,请提交issue。

许可证

源代码为MIT(LICENSE,版权归oboroge0)。本仓库不包含任何模型权重——scripts/download_models.py在安装时从原始发布者处获取,每个模型都有各自的许可证(THIRD_PARTY_NOTICES.md中有完整表格)。

有一个模型不是宽松许可证: ja->en翻译模型(mojicast-fugumt-ja-en-ct2,由--translate en使用)为CC BY-SA 4.0(相同方式共享)。如果你重新分发该模型的权重,必须保留署名,并同样以CC BY-SA 4.0许可分发。这不影响hayamimi自身代码的许可证,也不影响--translate zh,ko(M2M-100,MIT)。

致谢

hayamimi构建于以下项目之上,没有它们就没有hayamimi:

  • k2-fsa/sherpa-onnx —— 这里所有模型都通过它进行ONNX Runtime推理。
  • ReazonSpeech(Reazon人类交互实验室)—— 支撑本项目准确率主张的日语ASR模型。
  • NVIDIA NeMo / Parakeet —— 英语+24种欧洲语言。
  • Meta AI Omnilingual ASR —— 约1600种语言的回退,让"多语言"不是空话。
  • FunASR / SenseVoice(阿里巴巴达摩院) —— 中文、韩语和粤语ASR。
  • Mojicast(ishiki-emo)—— 实时字幕管线的设计灵感,以及本项目使用的转换后标点/翻译模型工件的来源。Mojicast本身是一款完整的离线实时字幕应用,值得一试。
  • Silero VAD —— 语音活动检测。
  • 3D-Speaker(阿里巴巴达摩院) —— --speakers背后的CAM++说话人嵌入模型。
  • Kiwi —— 韩语形态分词器,用于修复SenseVoice的按空格分隔韩语输出。

贡献

见CONTRIBUTING.md。