AnySpeechRecognizer 与 AnyDecisionModel 发布:面向 Apple 平台的语音与决策开源模型
Introducing AnySpeechRecognizer and AnyDecisionModel: Open Models for Speech and Decisions on Apple Platforms
AnySpeechRecognizer 与 AnyDecisionModel 两个 Swift 包发布,前者在 Apple 的 SpeechAnalyzer API 下可切换 Parakeet、Qwen3-ASR 等识别引擎并新增说话人分离,后者以概率方式回答是非、多选和评分问题。
两个 Swift 包把语音识别与决策模型统一到可切换引擎的 API 下,并给出在 Apple 设备本地运行的实测词错率。
语言模型能做很多事。 它们能写作、总结、翻译和调用工具。 但它们只是机器学习能力的一小部分。
假设有客户拨打你的支持热线。 首先,你想知道他们说了什么(语音识别)。 然后,你想决定把他们转接到哪里(决策)。 你可以把这两件事都交给语言模型, 但有一些专为各自任务构建的模型, 它们更好、更快、成本更低。
在我们的AnyLanguageModel 1.0 回顾中, 我们回顾了 Swift 中本地和托管模型的一年。 随着 AnyLanguageModel 和 swift-huggingface 现已发布 1.x 版本, 我们将同样的方法扩展到语音和决策: 熟悉的 Swift API,可自由选择模型。
今天我们宣布两个软件包:
- AnySpeechRecognizer 实现了 Apple 新的语音转文本 API,并可选择识别引擎, 包括在 Core ML 上本地运行的 NVIDIA Parakeet 和阿里巴巴 Qwen3-ASR, 并新增说话人分离功能,以追踪谁在何时说话。
- AnyDecisionModel 回答是/否、多项选择和评分量表问题, 返回带类型的结果和概率。 它从模型的概率中推导出每个答案, 而不是生成文本, 使用你设备上的 MLX、Core AI 或 llama.cpp 模型, 或来自 Jev 或 Ollama 的决策模型。
两个软件包都可以从 Hugging Face Hub 下载开放模型 并在设备上运行。
AnySpeechRecognizer
计算机转写语音已有数十年历史,
而在过去几年里,它们在这方面已经变得非常出色。
Apple 平台已经为此提供了 API。
macOS 26 和 iOS 26 引入了 SpeechAnalyzer,
这是 Apple 新的语音转文本 API。
你向分析器添加 SpeechTranscriber 和 SpeechDetector 等模块,
给它一个文件或音频缓冲区流,
然后从异步序列中读取到达的结果。
AssetInventory 负责安装模型,
因此由你的应用决定何时下载。
这是一个设计良好的 API, 但它背后唯一的引擎是 Apple 的。 AnySpeechRecognizer 保留该 API,并让你选择引擎:
- import Speech
+ import AnySpeechRecognizer
该软件包镜像了 SpeechAnalyzer、SpeechTranscriber、DictationTranscriber、
SpeechDetector 和 AssetInventory,
并以 Apple 的 Speech 框架作为默认引擎。
如果你的代码使用该共享 API,只需更改 import 即可。
要改用 Parakeet,
只需更改创建转写器的方式:
let transcriber = SpeechTranscriber(
model: ParakeetSpeechModel(variant: .v3),
transcriptionOptions: [],
reportingOptions: [.volatileResults],
attributeOptions: [.audioTimeRange]
)
安装资源、运行分析器 和读取结果的步骤保持不变。 README 包含完整示例, 并列出了与 Apple 框架的差异。
认识这些模型
Parakeet v3 是 NVIDIA 的 Parakeet TDT 0.6B v3。 它是一个多语种模型: 它能识别 25 种欧洲语言,并判断出听到的是哪一种, 因此你无需传入区域设置。
Parakeet Ultra 是 Moondream 对 v3 的后训练版本。 在我们的大多数测量中,它更准确。
Parakeet Unified 是 NVIDIA 的 仅支持英语的 Parakeet。
三个 Parakeet 模型都运行在该软件包自有的 Core ML 运行时上, 支持文件和实时输入、易变结果、词级时间戳 以及词级置信度。
Qwen3-ASR 是阿里巴巴的语音模型, 有 0.6B 和 1.7B 两种规模。 它能识别 30 种语言和 22 种中文方言, 包括普通话、粤语、日语和韩语,这些是 Parakeet 不覆盖的。 它会自动检测语言,你也可以手动设置:
let transcriber = SpeechTranscriber(
model: QwenSpeechModel(variant: .large, language: Locale.Language(identifier: "yue")),
transcriptionOptions: [],
reportingOptions: [],
attributeOptions: []
)
Qwen3-ASR 对每段最长 40 秒的音频只给出一个最终结果,没有中间波动结果、词级时间戳或置信度。它运行在 GPU 上,速度比 Parakeet 慢,因此更适合转写文件,而不是实时输入。
要添加另一个引擎,需为其配置实现 SpeechRecognitionModel,并为单个模块的会话实现 SpeechRecognitionExecutor。README 中包含一个小型提供方,它会输出占位文本,帮助你上手。
Hub 上的 Core ML 模型
为了在应用中使用这些模型,我们将它们转换为 Core ML 并发布到 Hub 上:
| 模型 | Core ML 转换版本 | 原始检查点 | 许可证 |
|---|---|---|---|
| Parakeet TDT 0.6B v3 | mattt/parakeet-tdt-0.6b-v3-coreml | nvidia/parakeet-tdt-0.6b-v3 | CC BY 4.0 |
| Parakeet Ultra | mattt/parakeet-ultra-coreml | moondream/parakeet-ultra | CC BY 4.0 |
| Parakeet Unified EN 0.6B | mattt/parakeet-unified-en-0.6b-coreml | nvidia/parakeet-unified-en-0.6b | NVIDIA Open Model License |
| Qwen3-ASR 0.6B | mattt/qwen3-asr-0.6b-coreml | Qwen/Qwen3-ASR-0.6B-hf | Apache 2.0 |
| Qwen3-ASR 1.7B | mattt/qwen3-asr-1.7b-coreml | Qwen/Qwen3-ASR-1.7B-hf | Apache 2.0 |
| Streaming Sortformer 4spk v2 | mattt/diar-streaming-sortformer-4spk-v2-coreml | nvidia/diar_streaming_sortformer_4spk-v2 | CC BY 4.0 |
| Streaming Sortformer 4spk v2.1 | mattt/diar-streaming-sortformer-4spk-v2.1-coreml | nvidia/diar_streaming_sortformer_4spk-v2.1 | NVIDIA Open Model License |
每个仓库都记录了其源检查点以及每个文件的校验和,并且包将其固定到某个特定修订版本。Parakeet 仓库额外提供了一个用于实时输入的 8 秒编码器,可将实时分析时间缩短约三分之一,代价是词错误率略有升高。该仓库还包含转换工作流,其中大多数遵循 FluidInference 的 mobius 脚本,并将每次转换结果与原始模型进行校验。
下载仅在你的应用请求时才会发生:每个 Parakeet 约 600 MB,外加实时编码器约 590 MB,Qwen3-ASR 为 0.9 至 2.4 GB,Sortformer 约 250 MB。文件存放在标准的 Hugging Face 缓存中,因此沙盒之外的 Mac 应用可以与 Python 及其他 Hugging Face 工具共享这些文件。
说话人分离
转写对话时,你常常想知道哪句话是谁说的。这就是说话人分离,而 Apple 的 Speech 框架没有对应的模块,因此 AnySpeechRecognizer 添加了一个。SpeakerDiarizer 在同一时间轴上,将 NVIDIA 的Streaming Sortformer 与 Apple 或 Parakeet 转写器一起运行在 Core ML 上:
let diarizer = SpeakerDiarizer(model: SortformerDiarizationModel())
if let request = try await AssetInventory.assetInstallationRequest(
supporting: [transcriber, diarizer]
) {
try await request.downloadAndInstall()
}
let analyzer = SpeechAnalyzer(modules: [transcriber, diarizer])
该分离器最多可跟踪四位说话人,即使他们同时说话也能处理。SpeakerTimeline 会收集其结果,并将每个带时间戳的词归属到对应说话人,因此你可以按说话人轮次打印转写文本。与 AnyLanguageModel 中的图像支持一样,这超出了 Apple API 目前提供的能力。
分离器默认使用 Sortformer v2。你也可以选择 v2.1,NVIDIA 用更多会议数据对其进行了训练,在我们的测量中,它在会议场景下也更准确。
效果如何?
我们使用该包的 hear 命令行工具,在运行 macOS 27.0.1 的 M4 Max Mac Studio 上进行了测量。以下是 3 到 8 分钟的 LibriSpeech 文件上的词错误率:
| 转写器 | test-clean | test-other |
|---|---|---|
| Parakeet v3,文件 | 1.55% | 2.78% |
| Parakeet v3,实时 | 4.73% | 4.29% |
| Parakeet Ultra,文件 | 1.66% | 2.55% |
| Parakeet Ultra,实时 | 2.65% | 3.28% |
| Parakeet Unified,文件 | 1.43% | 2.19% |
| Parakeet Unified,实时 | 1.45% | 2.42% |
| Apple 的转写器,文件 | 2.12% | 3.97% |
Parakeet 从文件转写一小时音频 大约需要 20 到 40 秒, 实时转写则大约需要 1.5 到 2.5 分钟, 在 Core ML 编译完模型后大约占用 100 MB 内存。 Qwen3-ASR 慢得多,并且需要几 GB 的内存。 会议对每个引擎来说都很难: 在 AMI 录音中,人们会互相抢话, 我们测试的每个引擎的词错误率都高于 23%。
这些数字来自一台机器和几个数据集,
所以请用你自己的硬件和音频来测量。
hear evaluate 可处理你自己的文件,
仓库中的 natural-speech-eval.py 工具
可复现这些测量结果。
到目前为止,我们只在 macOS 27 上测试过,还没有在 iPhone 上测试。
接下来,我们希望加入更多引擎,包括 macOS 27 和 iOS 27 上的 Core AI。
AnyDecisionModel
AnyDecisionModel 则是另一回事。 没有可映射的 Apple 框架, 而且决策模型严格来说也并不新鲜。
只有来自法国 Décision 地区的才算决策模型; 否则,它只是闪闪发光的零样本分类。
新鲜的是这个名字、大量的炒作, 以及一批为做决策而训练的模型。 我们认为这就是未来的方向, 所以我们想要一个为此准备好的 Swift API。
应用经常让语言模型做决策。 这条消息紧急吗? 这个工单应该由哪个团队处理? 这位客户有多沮丧? 通常的做法是给语言模型一个提示, 然后解析它生成的文本。 这样能给你一个答案, 但不会显示它分配给各个备选答案的概率。
在 Swift 中,一个决策是这样的:
import AnyDecisionModel
enum Department: String, Choosable {
case billing, technical, sales
}
let ticket = "I was charged twice for my order, and now the app logs me out when I try to ask for a refund."
let model = MLXDecisionModel(modelID: "mlx-community/Qwen3-4B-Instruct-2507-4bit")
let session = DecisionSession(model: model, state: .text(ticket))
let urgent = try await session.probability(of: "Does this convey urgency?")
let team = try await session.choice("Which team?", from: Department.self)
let anger = try await session.score(
"How frustrated?",
levels: ["Calm", "Frustrated", "Very angry"]
)
urgent 是一个 Double。
team 是一个 Choice<Department>,包含选中的情况、
每个选项的概率以及一个置信度值。
anger 是一个 Score,包含预期的等级、
每个等级的概率以及一个置信度值。
因为 Department 遵循 Choosable,
team.value 是一个 Department,你可以在穷尽式的 switch 中使用它。
如果你用过 Foundation Models 或 AnyLanguageModel,
模型和会话的模式会看起来很熟悉。
一个 DecisionSession 持有单一状态,可以是文本或 JSON,
每个问题都是关于该状态的。
问题之间相互独立:
答案永远不会成为后续问题的输入。
要一次问多个问题,
请将它们作为一批传给 decide(_:)。
对于每个问题,MLX 后端会构建一个提示, 为每个允许的答案使用一个单 token 标签, 然后从一次前向传播中读取每个标签的概率。
试一试:Emoji Magnet
Emoji Magnet 是一个 macOS 应用,在一堆 343 个 emoji 上方有一个文本输入框。 输入一个短语,例如“things you wear”, 匹配的 emoji 就会从堆里升起。 把它细化为“things you wear in winter”, 不再匹配的 emoji 就会落回去。
这个应用没有内置的关键词规则; 一切都在推理时由模型决定。 当你停止输入时,它会针对每个 emoji 向模型问一个是或否的问题, 例如:
Statement: a heavy winter coat fits the description "things you wear". Is this statement true?
会话的状态保存着这个短语。
上面的演示使用托管的 Jev 后端,
它在一次请求中回答全部 343 个问题,
所以每个短语的 emoji 大约半秒就能稳定下来。
如果没有 TYPESAFE_AI_API_KEY,应用会在你的 Mac 上使用 MLX 后端运行 Qwen3 4B。
该后端使用前缀缓存来评估一次状态,
然后每次前向传播回答 16 个问题。
在 M4 Max 上,模型加载后一组 16 个问题大约需要 0.45 秒。
对全部 343 个 emoji 完整跑一遍大约需要 10 秒,
每组结果一到达就会显示出来。
Emoji Magnet 位于 AnyDecisionModel-Examples 仓库中。
决策头与 Clef
读取下一个 token 的概率适用于通用语言模型, 但这些模型并未经过训练来做出决策。 Cloudflare 的 Clef 模型 代表了一种更新的方法: 一个带有训练好的决策头的模型, 它将状态和带类型问题的 schema 转换为决策, 在一次前向传播中返回所有问题中所有允许选项的概率。 Clef-Flash 是 9B 变体。
AnyDecisionModel 在 Mac 上使用 MLX 运行 Clef-Flash:
let model = MLXHeadDecisionModel(
modelID: "mlx-community/clef-flash-4bit",
head: .jointSchema
)
你使用相同的会话和问题,
但 MLXHeadDecisionModel 从 Clef 训练好的决策头读取每个决策,
而不是从下一个 token 的概率中读取。
4-bit 模型 是一个 6.2 GB 的下载文件,
其模型卡建议使用至少 16 GB 内存的 Mac。
Clef 可以读取图像和视频,
但此后端仅接受文本和 JSON 状态。
校准和检查概率
语言模型给出的原始概率往往过于自信。
本地后端接受一个 Calibration,
它对每个答案应用温度,并对是/否答案应用偏置。
使用单独的数据集来拟合校准并评估结果。
语言模型也可能因为某个选项在列表中的位置而偏向它。
在 MLX、Core AI 和 llama.cpp 下一个 token 后端上,
开启 rotationDebiasing 以对选项的每种轮换各询问一次选择题,
并对概率取平均值。
这需要为每种轮换进行一次提示评估。
每个本地选择和评分还包含一个置信度值, 基于其概率的熵。 它衡量概率的集中程度, 而不是答案是否正确, 因此在选择阈值之前,请在你自己的数据上检查错误率。 下一个 token 后端的诊断报告会显示模型分配给允许答案的概率有多少。 数值低意味着它将大部分概率分配到了其他地方。
后端
AnyDecisionModel 有六个后端:
- MLX 使用 mlx-swift-lm 在 Apple 芯片上运行小型语言模型, 并从 Hub 下载它。
- MLX 决策头 运行带有自己训练好的决策头的模型, 例如 Clef。
- Core AI 在 macOS 27 和 iOS 27 上使用 Core AI 模型执行与 MLX 相同的操作。
- llama.cpp 通过 llama.swift 从本地 GGUF 模型读取决策。
- Jev 通过 System One API 调用 TypeSafe 的 Jev, 或任何实现了它的服务器,例如 Kev。
- Ollama 在本地 Ollama 服务器或 Ollama 的云中调用 System One 决策模型。 它需要 Ollama 0.35.0 或更高版本,云模型需要 0.40.5 或更高版本。
下一个 token 后端将答案标记为“是”和“否”、A 到 Z 或 0 到 9, 因此一个选择题最多可以有 26 个选项,一个评分最多可以有 10 个级别。 决策头没有这样的限制。
与 AnyLanguageModel 一样,该包使用 Swift 包特性。
不使用特性时,它没有第三方依赖。
你可以获得核心类型以及 Jev 和 Ollama 后端,
它们也可以在 Linux 上构建。
为本地后端启用 MLX、CoreAI 或 Llama 特性。
开始使用
两个包都需要 Swift 6.2 或更高版本,以及 macOS 26 或 iOS 26 或更高版本。 Parakeet 和 Sortformer 运行时以及 MLX 后端需要 Apple silicon。 AnyDecisionModel 的 MLX 构建任务使用 Xcode 的 Swift 6.4 或更高版本 以及 Metal 工具链;Core AI 需要 Xcode 27 和 OS 27 运行时。
将包添加到你的 Package.swift:
dependencies: [
.package(url: "https://github.com/mattt/AnySpeechRecognizer", from: "0.1.0"),
.package(
url: "https://github.com/mattt/AnyDecisionModel",
from: "0.6.0",
traits: ["MLX"]
),
]
目前,AnyDecisionModel 的 CoreAI trait 无法与 AnySpeechRecognizer 组合使用,
因为 coreai-kit 0.7.3 要求 swift-huggingface 低于 1.0,
而 AnySpeechRecognizer 要求 1.x。
每个 README 都有更多示例以及关于要求和后端的详细信息。
AnySpeechRecognizer 还包含一个命令行工具 hear,
它可以转写本地文件、标注其中的说话人,并评估结果。
每个 hear 命令都需要 --backend apple、--backend parakeet 或 --backend qwen;
在分析之前,请使用相同的后端和模型选项运行 hear prepare
以安装所需的资源。
在你的应用中试用它们,并告诉我们哪些功能可用、哪些不可用。 欢迎在两个仓库中提交 issue 和 pull request。
链接
- GitHub 上的 AnySpeechRecognizer
- GitHub 上的 AnyDecisionModel
- GitHub 上的 AnyDecisionModel-Examples
- GitHub 上的 AnyLanguageModel
- GitHub 上的 swift-huggingface
感谢 NVIDIA 提供 Parakeet、Sortformer 和 NeMo,感谢 Moondream 提供 Parakeet Ultra, 感谢 FluidInference,其 FluidAudio 和 mobius 项目 记录了 Parakeet 运行时和 Core ML 转换所遵循的约定, 感谢 Cloudflare 提供 Clef, 并感谢所有为 AnyLanguageModel 做出贡献的人, 这两个包都借鉴了它的模式。
来源:Hugging Face Blog · huggingface.co