返回 文章 apply CMS 文章

AnySpeechRecognizer 与 AnyDecisionModel:为 Apple 平台带来开放的语音与决策模型

用 Swift 在 Apple 设备上灵活运行多种语音识别和决策模型,不再局限于苹果自带引擎。

SwiftApple语音识别决策模型
成长分 / 100 68 综合收获、行动、留存与影响

AnySpeechRecognizer 与 AnyDecisionModel:为 Apple 平台带来开放的语音与决策模型
为什么值得读了解如何在不改变现有 API 的情况下,为 Apple 的语音识别框架替换或增加引擎。

学习一种基于概率而非生成文本的决策模型调用方式,获得类型化结果和置信度。

关键洞察
  1. AnySpeechRecognizer 镜像了 Apple 的 SpeechAnalyzer 等 API,只需更改 import 即可切换引擎,默认使用苹果引擎,也可选用 Parakeet 或 Qwen3-ASR。
  2. Parakeet 模型在 LibriSpeech 测试集上词错误率低于苹果转录器,且文件转录速度快,内存占用约 100 MB。
  3. Qwen3-ASR 支持 30 种语言和 22 种中文方言,但速度较慢,适合文件转录而非实时输入。
转成行动

深入阅读

正文与原文对照

原文保真覆盖:全文原文字符:19707

语言模型能做很多事。它们能写作、总结、翻译和调用工具。但它们只是机器学习能力的一小部分。

假设有客户拨打你的支持热线。首先,你想知道他们说了什么(语音识别)。然后,你想决定把他们转接到哪里(决策)。你可以把这两件事都交给语言模型,但针对每件事都有专门构建的模型,它们更好、更快、更便宜。

在我们的AnyLanguageModel 1.0 回顾中,我们回顾了 Swift 中本地和托管模型的一年。随着 AnyLanguageModel 和 swift-huggingface 现已发布 1.x 版本,我们正在将同样的方法扩展到语音和决策:熟悉的 Swift API,并可选择模型。

今天我们宣布两个包:

  • AnySpeechRecognizer 实现了 Apple 新的语音转文本 API,并可选识别引擎,包括在 Core ML 上本地运行的 NVIDIA Parakeet 和阿里巴巴 Qwen3-ASR,还增加了说话人分离(speaker diarization)来跟踪谁在何时说话。
  • 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,更改导入就足够了。要改用 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 上:

每个仓库都记录其源检查点以及每个文件的校验和,包将其固定到特定修订版本。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 在 Core ML 上运行 NVIDIA 的 Streaming Sortformer,与 Apple 或 Parakeet 转写器在同一时间线上并行运行:

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 用更多会议数据训练了它,在我们的测量中,它在会议场景下也更准确。

效果如何?

我们在搭载 macOS 27.0.1 的 M4 Max Mac Studio 上,用该软件包的 hear 命令行工具进行了测量。以下是 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>,包含选中的 case、每个选项的概率以及一个置信度值。anger 是一个 Score,包含预期的 level、每个 level 的概率以及一个置信度值。由于 Department 遵循 Choosable,team.value 是一个 Department,你可以在穷举式 switch 中使用它。

如果你用过 Foundation Models 或 AnyLanguageModel,模型与会话的模式会看起来很熟悉。一个 DecisionSession 持有单一状态,该状态可以是文本或 JSON,每个问题都针对该状态。问题相互独立:答案永远不会成为后续问题的输入。要一次问多个问题,请将它们作为一批传给 decide(_:)。

对于每个问题,MLX 后端会为每个允许的答案构建一个带单 token 标签的提示,然后从一次前向传播中读取每个标签的概率。

试一试:Emoji Magnet

▶ Emoji Magnet sorting emoji for seven phrases, from "things you wear" to "junk food"

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 时,应用使用 MLX 后端在你的 Mac 上运行 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 位模型 是 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,或任何实现了该 API 的服务器,例如 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 芯片。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 特性无法与 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 以安装所需资源。

在你的应用中试用它们,并告诉我们哪些有效、哪些无效。欢迎在两个仓库中提交问题和拉取请求。

链接

感谢 NVIDIA 提供 Parakeet、Sortformer 和 NeMo,感谢 Moondream 提供 Parakeet Ultra,感谢 FluidInference,其 FluidAudio 和 mobius 项目记录了 Parakeet 运行时和 Core ML 转换所遵循的约定,感谢 Cloudflare 提供 Clef,并感谢所有为 AnyLanguageModel 做出贡献的人,两个包都借鉴了其模式。