在 Windows 上实现 Sherpa-ONNX 生产就绪的实用方案
Jan 29, 2026
ASR 与 Sherpa-ONNX
自动语音识别 (ASR) 是现代语音驱动类应用(如虚拟助手、实时转录系统和基于语音的用户界面)的核心技术。尽管模型准确性和推理速度备受关注,但生产部署往往会因一些更微不足道的问题(例如构建系统、运行时库和特定平台的限制)而失败或停滞。
Sherpa-ONNX 是一个功能强大且灵活的开源工具包,用于通过 ONNX Runtime 部署 ASR 模型。然而,在将 Sherpa-ONNX 集成到基于 Windows 的现有生产系统时,由于需要使用 MT(静态 CRT)运行时,而大多数生产级应用使用 MD(动态 CRT)运行时,这就会导致冲突。
本博客将解释此问题出现的原因、其在实际项目中的表现形式,并提供一种在 MD 模式下构建 Sherpa-ONNX 的实用工程方案。该方案将使 Sherpa-ONNX 能够无缝集成到生产环境中,而无需打乱已有的构建配置。
Visual Studio 中的 MT 与 MD 运行时库模式
在 Windows 上,Visual Studio 提供两种运行时库选项,用于控制 C/C++ 运行时 (CRT) 的链接方式:
- MD(多线程动态运行时)
通过共享 DLL 动态链接 CRT。 - MT(多线程静态运行时)
将 CRT 静态链接到每个二进制文件中。
MD 与 MT 的比较
维度 |
MD |
MT |
CRT 实例 |
在所有 EXE 和 DLL 之间共享 |
每个 EXE/DLL 拥有专属的 CRT |
跨 DLL 内存管理 |
可跨 DLL 安全调用 malloc/free 和 new/delete |
内存必须在同一模块中释放 |
第三方库兼容性 |
高 |
通常需要重建依赖关系 |
二进制文件大小 |
较小 |
较大 |
以上对比表明,MD 模式是实际生产环境中的标准选择,尤其适用于涉及 DLL、插件或第三方库的项目。
Sherpa-ONNX 默认使用 MT 模式
默认情况下,Sherpa-ONNX 以 MT 模式构建,而且其构建系统未提供更改此行为的配置选项。
这一点可从官方代码库的以下代码片段中得到验证:https://github.com/k2-fsa/sherpa-onnx/blob/master/CMakeLists.txt#L137
if(MSVC)
add_compile_options(
$<$<CONFIG:>:/MT> #---------|
$<$<CONFIG:Debug>:/MTd> #---|-- Statically link the runtime libraries
$<$<CONFIG:Release>:/MT> #--|
$<$<CONFIG:RelWithDebInfo>:/MT>
$<$<CONFIG:MinSizeRel>:/MT>
)
endif()
如上所示,Sherpa-ONNX 对所有目标(包括库和示例应用)强制使用 MT 运行时。因此,任何链接到 Sherpa-ONNX 的下游项目也必须在 MT 模式下构建。
如果 Sherpa-ONNX 使用其默认配置构建,然后集成到客户项目中,则客户将被迫更改其现有构建的运行时库链接模式。在实际生产环境中,此类更改几乎是不可接受的。运行时库设置通常是标准化且严格受控的,偏离默认或推荐配置会引入不必要的风险和潜在的不稳定性。因此,这一要求成为了在生产系统中采用 Sherpa-ONNX 的重大障碍。
实际示例:在自定义项目中使用 Sherpa-ONNX
为说明此问题及其产生的实际影响,我们将通过一个具体示例进行演示。
1) 准备模型
下载本示例中使用的标点模型并解压到某个目录:
# wget https://github.com/k2-fsa/sherpa-onnx/releases/download/punctuation-models/sherpa-onnx-punct-ct-transformer-zh-en-vocab272727-2024-04-12-int8.tar.bz2
2) 在 Windows 上构建 Sherpa-ONNX
按照官方指南克隆代码库并构建 Sherpa-ONNX:https://k2-fsa.github.io/sherpa/onnx/install/windows.html?highlight=windows
构建步骤示例:
# git clone https://github.com/k2-fsa/sherpa-onnx
# cd sherpa-onnx
# cmake -G "Visual Studio 17 2022" -A x64 -B build . \
-DCMAKE_BUILD_TYPE=Release \
-DSHERPA_ONNX_ENABLE_BINARY=OFF \
-DSHERPA_ONNX_ENABLE_WEBSOCKET=OFF \
-DSHERPA_ONNX_ENABLE_TTS=OFF
# cd build
# cmake --build . --config release -j 16
生成的库文件可在以下位置找到:
build\lib\Release
3) 创建示例项目
项目结构:
include\
sherpa-onnx\
csrc\
offline-punctuation.h
offline-punctuation-model-config.h
parse-options.h
src\
test_punctuation.cpp
lib\
sherpa-onnx-core.lib
onnxruntime.lib
onnxruntime.dll
CMakeLists.txt
准备步骤:
- 将 Sherpa-ONNX 源码中的三个头文件复制到项目的 include\ 目录。
- 将 sherpa-onnx\build\lib\Release\ 中的 sherpa-onnx-core.lib 复制到 lib\ 目录。
- 按照官方说明安装 Ryzen AI 软件:https://ryzenai.docs.amd.com/en/latest/inst.html
- 将 C:\Program Files\RyzenAI\1.7.0\onnxruntime\bin 中的 onnxruntime.dll 复制到 lib\ 目录。
- 将 C:\Program Files\RyzenAI\1.7.0\onnxruntime\lib 中的 onnxruntime.lib 复制到 lib\ 目录。
示例 CMakeLists.txt:
cmake_minimum_required(VERSION 3.18.1)
project(test_punctuation)
set(CMAKE_CXX_STANDARD 20)
add_compile_options($<$<CONFIG:>:/MT> $<$<CONFIG:Release>:/MT>)
add_executable(test_punctuation ${CMAKE_SOURCE_DIR}/src/test_punctuation.cpp)
target_link_directories(test_punctuation PRIVATE ${CMAKE_SOURCE_DIR}/lib )
target_include_directories(test_punctuation PRIVATE ${CMAKE_SOURCE_DIR}/include )
target_link_libraries(test_punctuation PRIVATE sherpa-onnx-core onnxruntime)
test_punctuation.cpp 的示例代码:
#include <iostream>
#include <fstream>
#include "sherpa-onnx/csrc/offline-punctuation.h"
int32_t main(int32_t argc, char *argv[]) {
sherpa_onnx::OfflinePunctuationConfig cfg;
cfg.model.ct_transformer = argv[1];
cfg.model.num_threads = 1;
cfg.model.debug = false;
cfg.model.provider = "cpu";
std::string stringv = "你好吗how are you Fantasitic 谢谢我很好你怎么样呢";
auto punct = std::make_unique<sherpa_onnx::OfflinePunctuation>(cfg);
auto text_with_punct = punct->AddPunctuation(stringv);
std::cout<< text_with_punct <<"\n";
return 0;
}
4) 构建并运行
# cmake -G "Visual Studio 17 2022" -A x64 -S . -B build
# cd build
# cmake --build . --config release
# copy ..\lib\onnxruntime.dll .
# release\test_punctuation.exe some_dir\model.int8.onnx
输出:
你好吗?how are you Fantasitic?谢谢我很好,你怎么样呢?
程序能够成功运行,但这仅仅是因为项目被明确强制使用 MT 模式。
MT/MD 不匹配问题
从示例项目的 CMakeLists.txt 中移除以下行:
add_compile_options($<$<CONFIG:>:/MT> $<$<CONFIG:Release>:/MT>)
在默认的 MD 模式下重新构建项目,此时将出现以下链接器错误:
sherpa-onnx-core.lib(offline-punctuation.obj) : error LNK2038: mismatch detected for 'RuntimeLibrary': value 'MT_Static
Release' doesn't match value 'MD_DynamicRelease' in test_punctuation.obj [test_punct
uation.vcxproj]
此错误清楚地表明应用与 sherpa-onnx-core.lib 之间存在运行时库不匹配问题。
使 Sherpa-ONNX 使用 MD 模式
在 sherpa-onnx\build 目录中搜索“$<$<CONFIG:>:/MT>”,它出现在 sherpa-onnx\build\_deps\kaldi_decoder-src\CMakeLists.txt.
这意味着:依赖的 kaldi Decoder 项目使用了 MT 模式,这导致 Sherpa-onnx 项目必须使用 MT 模式。
要解决此问题,必须在 Kaldi Decoder 构建配置中启用 MD 模式。
在 sherpa-onnx\cmake\kaldi-decoder.cmake 中,定义了以下配置:
set(kaldi_decoder_URL "https://github.com/k2-fsa/kaldi-decoder/archive/refs/tags/v0.2.10.tar.gz")
set(kaldi_decoder_URL2 "https://hf-mirror.com/csukuangfj/sherpa-onnx-cmake-deps/resolve/main/kaldi-decoder-0.2.10.tar.gz")
set(kaldi_decoder_HASH "SHA256=a3d602edc1f422acfe663153faf3f0a716305ec1f95b8fcf9d28d301d6827309")
此配置表明 Sherpa-ONNX 依赖于 kaldi-decoder 版本 v0.2.10。
构建 Sherpa-ONNX 后,相应的源码归档文件会缓存在本地:
`sherpa-onnx\build\_deps\kaldi_decoder-subbuild\kaldi_decoder-populate-prefix\srcv0.2.10.tar.gz`.
修改步骤:
1.将归档文件传输到 Linux 环境(例如通过 SSH),并解压: # tar zxvf v0.2.10.tar.gz
2.在解压后的目录中,编辑 kaldi-decoder-0.2.10\CMakeLists.txt,注释掉以下行(大约从第 59 行开始)以禁用 MT 运行时:
# add_compile_options(
# $<$<CONFIG:>:/MT> #---------|
# $<$<CONFIG:Debug>:/MTd> #---|-- Statically link the runtime libraries
# $<$<CONFIG:Release>:/MT> #--|
# )
3. 重新打包修改后的源码目录:
# tar zcvf kaldi-decoder-0.2.10.tar.gz ./ kaldi-decoder-0.2.10
4. 计算更新后归档文件的 SHA256 校验和:# sha256sum kaldi-decoder-0.2.10.tar.gz 记录新生成的 SHA256 值。
5.将更新后的归档文件传输回 Windows 环境,并将其放置在 Sherpa-ONNX 根目录中。这将强制 Sherpa-ONNX 构建使用修改后的 kaldi-decoder 包,而不是从互联网下载。
6.更新 sherpa-onnx\cmake\kaldi-decoder.cmake 中的 SHA256 值: set(kaldi_decoder_HASH "SHA256=NEW_SHA256_VALUE_FROM_SHA256SUM")
将原始校验和替换为上一步中获得的值。
7.修改 sherpa-onnx\CMakeLists.txt,注释掉以下代码块,以允许 Sherpa-ONNX 本身在 MD 模式下构建:
# if(MSVC)
# add_compile_options(
# $<$<CONFIG:>:/MT> #---------|
# $<$<CONFIG:Debug>:/MTd> #---|-- Statically link the runtime libraries
# $<$<CONFIG:Release>:/MT> #--|
# $<$<CONFIG:RelWithDebInfo>:/MT>
# $<$<CONFIG:MinSizeRel>:/MT>
# )
# endif()
8.删除 sherpa-onnx\build 目录,并从头开始重新构建 Sherpa-ONNX。
9.将新生成的 sherpa-onnx-core.lib 复制到示例项目的 lib 目录中。
10.重新构建示例项目。现在,应该可以成功完成构建,而不会出现任何 MT/MD 运行时不匹配错误。
11.再次运行项目。输出应与使用 MT 模式时获得的结果相同。
总结
将 ASR 框架集成到生产系统中时,常常会遇到除模型设计或推理速度以外的其他挑战。运行时库配置,尤其是在 Windows 上,在决定技术是否可用方面起着至关重要的作用。
在本博客中,我们:
- 解释了什么是 Sherpa-ONNX,以及为什么运行时库配置在生产环境中至关重要
- 演示了生产场景中遇到的实际 MT/MD 运行时不匹配问题
- 提出了一种在 MD 模式下构建 Sherpa-ONNX 的实用工程方案
- 在无需更改现有构建配置的情况下,使 Sherpa-ONNX 能够集成到生产环境中
这种方案使 Sherpa-ONNX 能够在 AMD Ryzen AI 平台上与大型语言和语音模型紧密协作,确保在实际生产系统中实现稳定、可靠的部署。