Whistle:16.9 MB 极致轻量,CPU 端实现多语言语音转文字
今天我们发布 Whistle,一个面向手机、可穿戴设备、机器人、智能家居、汽车和微控制器的语音识别模型。整个模型只有一个 16.9 MB 的文件,纯 CPU 运行、零依赖,并且与 Needle 加载到同一个 C++ 引擎,共用同一套容器和量化方案。
按下麦克风,随便说点什么。
Whistle 能做三件事,全部在设备本地完成:
- 转写。 16 kHz 单声道音频,单次最长 30 秒,支持英语、德语、法语、西班牙语、意大利语、荷兰语和波兰语。除非你明确指定语言,否则会自动检测。
- 词级时间戳。 输出每个词的起始、结束时间和概率,由解码器的注意力机制对齐。
- 语音嵌入。 直接输出编码器结果,每 80 ms 一帧对应一行,无需解码出文字。
模型结构
--audio-depth 可选择解码器层数;编码器始终跑满全部八层。前端。 16 kHz 单声道音频按 25 ms 窗口、10 ms 步长分帧,提取 80 维 log-mel 特征,频带限制在 250-3500 Hz,并逐通道归一化。30 秒音频对应 3,000 帧。一个 128 通道、卷积核为 9 的卷积 stem 将帧数减半三次,剩下 375 帧,即每 80 ms 一帧。之后的所有阶段都按这个速率运行,embed 每帧输出一行。
编码器。 八个 Simple Attention 模块:采用四条 mHC 残差通道,用 Monarch Hadamard MLP 替代前馈网络,与 Needle 所用的模块完全相同。注意力是非因果的——第 3 秒的帧可以直接关注第 12 秒的帧。
解码器。 包含 8 个阶梯式简单注意力(Laddered Simple Attention)模块,宽度为 512;8 个查询头对应 2 个键值头;查询和键的维度为 48,值的维度为 64;在 Q、K、V 上应用 3 抽头因果卷积;并在第 3 层和第 7 层通过 18,432 个槽位执行 engram 查找。这基本上就是 Needle 的模块清单,只是层数不同。
与语音相关的部分在于每层增加了一次处理。每个解码层通过门控交叉注意力读取编码器,公式为 x ← x + σ(g) · softmax(q̂ K̂ᵀ/√d) V。其中门控值(gate)由每层学习得到,而 K 和 V 取自音频片段。当片段到达时,这些投影只运行一次(8 层共 375 帧),随后在整个解码过程中保持不变。因此,5 束搜索只需要 5 个短转录缓存,而不是对音频进行 5 遍处理。
解码。 使用长度归一化对数概率对 5 束进行搜索打分。关键词偏置与束搜索并行,基于你传入的短语遍历 Aho-Corasick 自动机,随着自动机推进提升这些词的对数概率。转录结果上限为 320 个 token。词表包含 8,192 个文本片段加上 7 个语言标记(每种语言一个),因此检测到的语言会作为 token 输出,而不是通过带外方式返回。
阶梯结构位于解码器。 从 2 层到最大深度的所有配置都作为独立模型进行过训练,加载时通过 --audio-depth 参数选择特定深度。编码器永远不会被切片:在每种深度下,全部 8 个模块都会运行。
静音处理。 在解码器启动前,引擎会测量片段的响度范围。如果低于阈值,系统将直接返回空转录和空语言结果,根本不进入束搜索。
基准测试
在 LibriSpeech test-clean、test-other、SPGISpeech、Earnings-22 以及 FLEURS 平均值上,Whistle 表现领先。在 TED-LIUM、AMI 以及 MLS 平均值上,Whisper base 表现更好,其体积为 145.3 MB,而 Whistle 仅为 16.9 MB。
每个模型均运行在官方默认的运行时上:Whistle 使用 C++ 引擎,beam 数为 5;openai-whisper 和 moonshine-voice 则对完整音频进行非流式处理。首个 token 生成时间指从音频输入到产生第一个 token 的耗时。解码速率由扣除该时间后的剩余墙钟时间除以生成的 token 数计算得出,因此编码器部分未被重复计算。Whisper 会将所有输入填充至 30 秒,因此其首个 token 生成时间随音频长度变化保持恒定。而 Whistle 的该时间则随音频时长增加:5 秒时约为 5.9 毫秒,10 秒时约为 11.1 毫秒,30 秒时约为 36.3 毫秒。
词错误率(WER)基于 Whisper 的归一化器进行评估。Whistle 的数据在 86,174 条语音样本上测得。Whisper 和 Moonshine 的数据则直接引用其作者发布的数值,这些数值源自多语言模型检查点,而非仅限英语的检查点。Whistle 的训练集或验证集中不包含任何测试音频,这一点通过比对每个报告中测试集的音频校验和与说话人 ID 得到验证。
一个引擎,三种加载方式
needle_load 能够读取 .cact 文件中包含的任意模型,因此同一个二进制文件既可以处理语音,也可以处理文本,或者两者兼有:
needle --model whistle.cact --audio clip.wav
needle --model needle3.cact --tools tools.json --prompt "turn off the kitchen lights"
needle --model needle3.cact --model whistle.cact --tools tools.json --audio clip.wav第三行中,needle_complete 直接接收音频片段。引擎将其转录,然后根据转录文本调用工具,并返回一个包含调用详情和语音字段的 JSON 对象,其中语音相关字段以前缀 audio_ 标记。调用方无需单独处理转录文本。
{"function_calls":[{"name":"set_lights","arguments":{"room":"kitchen","on":false}}],
"confidence":0.94,
"audio_text":"turn off the kitchen lights",
"audio_language":"en"}快速开始
pip install cactus-needle
import needle
print(needle.transcribe("clip.wav")["text"])
# turn off the kitchen lights16 kHz 的 WAV 或原始采样数据,基础安装就能直接用。其他采样率和麦克风采集需要安装 [mic] 扩展,它会额外引入 soxr 和 sounddevice。
每次调用都会返回文本、语言、首个 token 的毫秒耗时,以及之后解码器的 token 处理速度。word_timestamps=True 可以返回每个词及其时间戳和概率。keywords=["Siobhan", "Krzysztof"] 会在搜索过程中提高这些短语的 log 概率。language="de" 可以强制指定语言而不是自动检测。needle.Whistle() 则以对象形式提供同一个模型,方便调用 embed(audio) 或加载一个微调过的 .cact 文件。
needle whistle playground 可以在终端里通过麦克风实时转录,needle whistle compare 则把同一段音频分别送入 Whistle、Whisper 和 Moonshine,对比各自的耗时。
部署
引擎预编译支持十七个目标平台,涵盖 macOS、Linux、Android、iOS、watchOS、Windows on ARM、RISC-V、MIPS、浏览器以及 WASI 组件。每个目录都包含 needle 可执行文件、libneedle.a 和 needle.h,可以加载任意指定的 .cact 模型。
needle download macos-arm64
needle download whistle
./macos-arm64/needle --model whistle.cact --audio clip.wav --audio-word-timestampsneedle_load、needle_transcribe 和 needle_embed 就是全部的 C API。引擎不读取任何环境变量,所有行为要么是编译期默认值,要么通过显式参数指定。
模型权重托管在 Hugging Face,引擎及各平台目录在 Cactus-Compute/needle3,源码则在 GitHub 上。