- 人工智能
- 大模型
- 机器学习
- 深度学习
- 本地部署
- 模型推理服务
【免费下载链接】candle
Minimalist ML framework for Rust
语音活动检测(Voice Activity Detection, VAD)是音频处理中最基础也最关键的环节之一,它决定了"这段音频里人到底有没有在说话"。本文基于当前仓库中的 candle-examples/examples/silero-vad/README.md 与对应示例源码,完整讲解如何用 Rust 与 Candle 加载 Silero VAD v5 的 ONNX 模型,通过命令行管道(stdin)消费流式 PCM 音频,逐帧输出说话概率预测。读完本文,你将掌握该示例的全部命令行参数、两种麦克风采集方案(arecord 与 SoX)、帧/上下文(frame/context)机制的源码级原理,以及如何在 Candle 的 ONNX 推理链路中维护 VAD 的循环状态(state)。
Silero VAD 是什么,为什么选择在 Candle 中运行
Silero VAD v5 是一个开源的语音活动检测模型,专为流式音频场景设计:它按固定长度的帧(chunk)接收音频,对每一帧输出一个 0 到 1 之间的"说话概率",并维护一个跨帧传递的内部状态(state),从而让模型能感知历史音频上下文,在噪声、静音与人声交界处做出更稳定的判断。由于模型以 ONNX 格式分发,恰好与 Candle 的 ONNX 推理能力(candle-onnx)天然契合——不需要训练或转换权重,直接从 Hugging Face Hub 拉取 ONNX 文件即可在纯 Rust 程序中完成推理。
当前仓库中的示例使用 onnx-community/silero-vad 可以看到,默认情况下它会通过 Candle 的 hub 封装(candle-examples/src/hub.rs)自动下载该模型;也支持通过--model-id直接指定本地模型文件路径,绕过在线下载。
构建与运行前的准备
该示例并不是默认 feature,需要显式启用onnx特性,这一约束在 candle-examples/Cargo.toml 中有明确声明:
[[example]] name = "silero-vad" required-features = ["onnx"]因此构建命令必须带上--features onnx:
cargo run --example silero-vad --release --features onnx -- --sample-rate 16000几点补充说明:
onnx特性对应candle-onnx依赖(见 candle-examples/Cargo.toml),它负责解析 ONNX protobuf 并执行计算图求值。- 如果你打算用 CUDA 加速,可以再加上
--features cuda(在 candle-examples/Cargo.toml 中,cuda特性会联动启用candle/cuda与cudaforge);macOS 上则可选用--features metal。 - 编译
candle-onnx时要求系统中有protoc可用,如果报 "Could not findprotocinstallation",需要先安装 protoc 并加入PATH,详见 candle-onnx/README.md。 - 首次运行会自动从 Hugging Face Hub 下载模型(缓存机制与进度显示见 candle-examples/src/hub.rs),之后会命中本地缓存,不再重复下载。
命令行参数逐一解析
主程序使用clap解析参数(见 main.rs),全部参数如下表:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
--cpu | 布尔 | false | 强制在 CPU 上运行,而非 GPU |
--tracing | 布尔 | false | 开启 Chrome tracing,生成trace-timestamp.json性能分析文件 |
--input | 字符串 | 无 | 指定输入源(当前实现中音频从 stdin 读取) |
--sample-rate | 枚举 | 无(必填) | 采样率,仅支持8000或16000 |
--model-id | 字符串 | 无 | 本地 ONNX 模型文件路径;不传则自动从 Hub 下载 |
--config-file | 字符串 | 无 | 配置文件路径 |
--which | 枚举 | silero | 选择模型,目前仅有silero一个取值 |
其中最关键的是--sample-rate。它直接决定了两组核心参数(见 main.rs):
- 8 kHz:帧大小(frame_size)= 256,上下文大小(context_size)= 32;
- 16 kHz:帧大小 = 512,上下文大小 = 64。
--which枚举中保留了一个量化的SileroQuantized分支,但源码中以注释明确标注 "candle-onnx doesn't support Int8 dtype"(main.rs),因此当前只有 FP32 的silero模型可以实际运行。
方式一:用 arecord 采集麦克风
arecord是 ALSA(Advanced Linux Sound Architecture)自带的命令行录音工具。原文档给出的命令:
$ arecord -t raw -f S16_LE -r 16000 -c 1 -d 5 - | cargo run --example silero-vad --release --features onnx -- --sample-rate 16000命令拆解:
-t raw:输出原始(无容器)PCM 数据;-f S16_LE:16 位有符号小端整数(signed 16-bit little-endian),这与示例中I16Frames的解析逻辑一一对应;-r 16000:采样率 16 kHz;-c 1:单声道;-d 5:录制 5 秒;- 末尾的
-:表示将音频写到 stdout,通过管道|送入示例程序的 stdin。
注意示例程序约定 stdin 输入的采样率必须与--sample-rate一致:如果录音用了 8 kHz,运行参数就应改为--sample-rate 8000,程序会按 256 帧长处理。
方式二:用 SoX 采集并重采样
如果声卡/麦克风不支持直接输出 16 kHz,或者你想用 48 kHz 的通用采集配置,原文档提供的 SoX 双段管道可以把采样率转换为模型所需的 16 kHz:
$ rec -t raw -r 48000 -b 16 -c 1 -e signed-integer - trim 0 5 | sox -t raw -r 48000 -b 16 -c 1 -e signed-integer - -t raw -r 16000 -b 16 -c 1 -e signed-integer - | cargo run --example silero-vad --release --features onnx -- --sample-rate 16000命令拆解:
- 第一段
rec(SoX 的录音命令):以 48 kHz、16 bit、单声道、signed-integer 格式录制原始 PCM,trim 0 5表示截取开头 5 秒,输出到 stdout; - 第二段
sox:把 48 kHz 的原始 PCM 流读入(-t raw -r 48000 -b 16 -c 1 -e signed-integer -),重采样到 16 kHz 后以同样的原始格式写出(-t raw -r 16000 -b 16 -c 1 -e signed-integer -); - 最后送入
cargo run --example silero-vad ... --sample-rate 16000。
这套写法完全兼容示例程序的输入约定:无论采集端是多少采样率,最终喂给模型的都是 16 kHz / S16_LE / 单声道的原始 PCM。
源码剖析:从 stdin 字节流到说话概率
理解了命令后,我们再深入 main.rs 看它内部是如何一步步完成推理的,这既是理解该示例的关键,也是后续将其嵌入自有音频管线的基础。
1. 字节流 → i16 → f32 的帧迭代器
I16Frames(main.rs)是一个自定义迭代器,负责从任意std::io::Read(这里是 stdin)中按帧读取数据。它的工作方式:
- 初始化时按
frame_size * 2字节(每个 i16 占 2 字节)分配缓冲区; - 每次读取累积到一帧完整大小后,把每 2 个字节按小端序还原为
i16(i16::from_le_bytes),再除以i16::MAX归一化到[-1.0, 1.0]的 f32 范围; - 若音频流提前结束且不足一帧,则直接丢弃不足部分(对应 main.rs 中
chunk.len() < frame_size时的continue)。
2. 模型加载与设备选择
模型加载分两步:
- 若未指定
--model-id,通过candle_examples::hub::Api从onnx-community/silero-vad仓库获取onnx/model.onnx(main.rs); - 用
candle_onnx::read_file读取并解码 ONNX protobuf,得到ModelProto(对应 candle-onnx/src/lib.rs 中的 prost 解码逻辑)。
设备选择则交给candle_examples::device(args.cpu)(candle-examples/src/lib.rs):--cpu强制 CPU;否则按 CUDA → Metal → CPU 的优先级自动选择可用设备,并在回退到 CPU 时打印提示(如 "Running on CPU, to run on GPU(metal), build this example with--features metal")。
3. 循环状态(state)与上下文(context)的维护
VAD 的核心在于跨帧的循环状态。示例用State结构体封装了三样东西(main.rs):
sample_rate:作为输入张量传给 ONNX 图的sr输入;state:形状为(2, 1, 128)的 F32 张量,初始化为全零,承载模型的循环记忆;context:形状为(1, context_size)的张量,保存上一帧末尾的音频样本,用于帧间平滑衔接。
每帧推理的流程(main.rs):
- 将当前帧
chunk转为形状(1, frame_size)的张量; - 用
Tensor::cat把上一帧的context拼到当前帧前面,得到(1, frame_size + context_size)的输入; - 构造输入字典
{"input": ..., "sr": ..., "state": ...},调用candle_onnx::simple_eval执行 ONNX 计算图; - 从图输出中取出两个结果:第一个输出作为本帧说话概率(标量),第二个输出作为新的
state写入下一轮; - 取当前帧末尾
context_size个样本作为下一轮的context。
simple_eval的实现见 candle-onnx/src/eval.rs,它会先把图中的 initializer 常量填充进值表,再按输入张量校验形状与 dtype,最后顺序执行算子。从代码注释(eval.rs)可以看出,当前实现是"每次求值直接跑一遍 proto 计算图"的直译模式,尚未引入中间表示缓存优化,因此在实时场景中单次推理的开销主要来自图求值本身。
4. 输出解读
每帧推理后程序打印一行vad chunk prediction: {output},其中 output 是 0~1 的说话概率(程序用assert_eq!(output.len(), 1)确认模型输出确实是标量)。全部帧处理完后,还会打印所有帧预测的算术平均:
vad chunk prediction: 0.0143 vad chunk prediction: 0.9876 ... vad average prediction: 0.5123如果你的应用只需要"某段时间是否有人说话"这类粗粒度判断,可以直接看平均预测;如果需要逐帧门限(比如帧概率 > 0.5 判定为语音段),可以基于每帧输出自行叠加阈值逻辑——示例本身只负责输出原始概率,不做门限判定。
另外,程序启动时还会打印一行 SIMD 能力摘要:
avx: true, neon: false, simd128: false, f16c: false这是通过candle::utils::with_avx()等查询编译时启用的指令集特性(见 main.rs),可用于确认当前构建是否启用了 AVX 等 CPU 加速。
5. Tracing 与性能定位
带--tracing运行时,程序会通过tracing_chrome::ChromeLayerBuilder构建 Chrome tracing 层,输出trace-timestamp.json性能分析文件(main.rs)。在排查"哪一步耗时高"(模型下载、模型加载、逐帧推理)时,可以借助它逐段分析;代码中已经用Instant分别记录了下载、加载、推理三段耗时并打印。
运行结果预期与常见问题
- 输出流:程序从 stdin 读入原始 PCM,每处理完一帧输出一行预测,音频结束后输出平均预测。5 秒 @ 16 kHz 对应 160000 个样本,按 512 帧长约产生 312 帧预测输出。
- 静音与说话概率:Silero VAD 对静音帧通常输出接近 0 的概率,对人声帧输出接近 1 的概率;数值表现取决于具体音频与噪声环境,示例不提供阈值调节参数。
- 量化模型暂不可用:
onnx-community/silero-vad同时提供了model_quantized.onnx,但受限于 candle-onnx 暂不支持 Int8 权重(main.rs 中的 TODO 注释),示例只使用 FP32 模型。 - 管道断裂:如果在运行中提前结束录音程序(如 Ctrl+C),stdin EOF 后迭代器会正常终止并输出平均预测,不会产生错误。
小结
通过这个示例,你可以看到 Candle 处理流式音频模型的一条完整通路:hf-hub拉取 ONNX 权重 →candle_onnx::read_file解析模型 →simple_eval逐帧求值 → 手动维护 state/context 实现跨帧记忆。它既是一个开箱即用的 VAD 命令行工具,也是一个很好的"如何用 Rust + Candle 跑带循环状态的 ONNX 模型"的参考模板。如果你想进一步改造,可以把I16Frames换成任意来源的音频块(文件、网络流、语音引擎回调),只需保持 S16_LE 单声道与--sample-rate对应的帧长即可无缝接入。
延伸阅读:完整的示例代码见 candle-examples/examples/silero-vad/main.rs,运行说明见 candle-examples/examples/silero-vad/README.md;ONNX 推理引擎的解析与求值实现见 candle-onnx/src/lib.rs 与 candle-onnx/src/eval.rs;模型下载与缓存逻辑见 candle-examples/src/hub.rs。
- 人工智能
- 大模型
- 机器学习
- 深度学习
- 本地部署
- 模型推理服务
【免费下载链接】candle
Minimalist ML framework for Rust
相关推荐
LiveKit Agents Silero VAD 插件实战:基于 ONNX 的实时语音活动检测与轮次控制
LiveKit Agents Silero VAD 插件实战:基于 ONNX 的实时语音活动检测与轮次控制 导读 livekit plugins silero
AI Agent人工智能语音AI 应用多模态Switch 自制系统报 Fatal Error 010000000000002b 怎么办?三步排查彻底修复
Switch 自制系统报 Fatal Error 010000000000002b 怎么办?三步排查彻底修复 先别慌,也别反复按电源键硬开机。玩 Atmosph
固件操作系统嵌入式系统编程国家中小学智慧教育平台电子课本下载:批量存成 PDF 离线用,免费
国家中小学智慧教育平台电子课本下载:批量存成 PDF 离线用,免费 开学要上新课,备课先找教材。tchMaterial parser 帮你在国家中小学智慧教育平
网页爬虫教育
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考