sherpa-onnx MeloTTS 模型转换指南:从 PyTorch 导出中英双语与多说话人英文 TTS 模型为 ONNX
【免费下载链接】sherpa-onnxSpeech-to-text, text-to-speech, speaker diarization, speech enhancement, source separation, and VAD using next-gen Kaldi with onnxruntime without Internet connection. Support embedded systems, Android, iOS, HarmonyOS, Raspberry Pi, RISC-V, RK NPU, Axera NPU, Ascend NPU, x86_64 servers, websocket server/client, support 12 programming languages项目地址: https://gitcode.com/GitHub_Trending/sh/sherpa-onnx
导读
本文围绕 scripts/melo-tts 目录下的整套转换工具链,完整讲解如何将 MyShell.ai 开源的 MeloTTS 模型转换为 sherpa-onnx 可加载的 ONNX 格式:既包含支持中文与英文混读的单说话人模型(export-onnx.py),也包含拥有 5 位英文女声的多说话人模型(export-onnx-en.py)。读完本文,你将掌握词表(lexicon)、音素(tokens)文件的生成原理、ONNX 导出时的动态轴与元数据设计,以及如何在本地用 onnxruntime 验证转换结果。
模型背景与目录说明
scripts/melo-tts目录下的模型均从 MeloTTS 项目转换而来。MeloTTS 是 MyShell.ai 开源的多语言文本转语音库,其转换产物带有model_type: melo-vits的元数据标识,被 sherpa-onnx 的离线 VITS TTS 实现(offline-tts-vits-model.cc)专门识别与加载。
该目录包含 5 个文件,构成一条完整的"依赖安装 → 模型导出 → 信息校验 → 本地推理验证"流水线:
| 文件 | 作用 |
|---|---|
| run.sh | 一键安装依赖并依次执行全部导出与验证步骤 |
| export-onnx.py | 导出中文+英文双语 TTS 模型,仅 1 位女声 |
| export-onnx-en.py | 导出纯英文TTS 模型,含 5 位女声 |
| show-info.py | 打印 ONNX 模型的输入/输出张量信息与自定义元数据 |
| test.py | 用 onnxruntime 对导出的模型做端到端合成验证 |
两个模型在说话人数量上的差异是理解本目录的关键(对应 README.md 的核心说明):
- 中文+英文模型:整个模型中只有1 位女声(
n_speakers: 1,speaker_id: 1); - 英文模型:包含5 位女声,说话人映射为
{'EN-US': 0, 'EN-BR': 1, 'EN_INDIA': 2, 'EN-AU': 3, 'EN-Default': 4}(见 export-onnx-en.py 头部注释)。
环境准备:一键安装脚本
run.sh 中的install()函数负责搭建完整转换环境,关键依赖如下:
pip install torch==2.3.1+cpu torchaudio==2.3.1+cpu -f https://download.pytorch.org/whl/torch_stable.html pushd /tmp git clone https://github.com/myshell-ai/MeloTTS cd MeloTTS pip install -r ./requirements.txt pip install soundfile onnx==1.15.0 onnxruntime==1.16.3 python3 -m unidic download popd要点说明:
- 使用 CPU 版 PyTorch 即可完成导出,无需 GPU;
- 从源码安装 MeloTTS 后,通过
export PYTHONPATH=/tmp/MeloTTS:$PYTHONPATH将其导入路径注入当前环境,这是两个导出脚本能够from melo.api import TTS的前提; unidic(日语词典)虽与中文/英文 TTS 无直接关系,但属于 MeloTTS 安装时的连带依赖,需一并下载;- ONNX 与 onnxruntime 版本被固定为
1.15.0/1.16.3,保证torch.onnx.export与后续InferenceSession的行为一致。
安装完成后,脚本依次执行./export-onnx.py、./show-info.py、./test.py,并把产物按zh_en/与en/两个目录归档。
导出中文+英文双语模型(export-onnx.py)
export-onnx.py 的main()流程分为四步:生成词表与音素表、实例化 MeloTTS 模型、执行 ONNX 导出、写入自定义元数据。
1. 生成 lexicon.txt 与 tokens.txt
generate_lexicon()会同时覆盖三种词源并写入lexicon.txt:
- 英文字典
eng_dict:经refine_syllables()拆分为音素(phones)与音节调号(tones),每个音节的调号加上language_tone_start_map["EN"]偏移后,按单词 音素... 调号...的格式逐行写出; - 汉字拼音字典
pinyin_dict:仅收录0x4E00 ~ 0x9FA5(CJK 统一汉字区)的字符,通过get_initial_final_tone()用 pypinyin 拆出声母、韵母与声调; - 多字词库
phrases_dict:逐词生成音素与调号,并断言两者长度严格相等。
get_initial_final_tone()内部实现了教科书级的拼音→音素规整逻辑,值得注意的映射规则包括:
- 有介音时的韵母缩写:
uei → ui、iou → iu、uen → un; - 零声母音节改写:
ing → ying、i → yi、in → yin、u → wu; - 单韵母改写:
v → yu、i → y、u → w; - 声调用
1~5表示,最终每个音素都对应一个调号。
generate_tokens(symbol_list)则把 MeloTTS 的全部符号(含标点、空白符_、音素符号等)按行写入tokens.txt,格式为符号 编号。
2. 封装模型与构造示例输入
ModelWrapper用全零张量替掉了 MeloTTS 推理路径中依赖的bert(形状[N, 1024, L])与ja_bert(形状[N, 768, L]),并按lang_id[:, 1::2] = self.lang_id的规则把语言 ID 铺到偶数位 token 上,从而把完整的model.model.infer(...)调用收敛成 7 个标量/向量输入,方便 ONNX 导出。
示例输入使用随机 token:x形状(60,),tones全零,sid = 1,三个控制标量noise_scale、length_scale、noise_scale_w均初始化为1.0。
3. ONNX 导出参数
opset_version = 18 input_names = ["x", "x_lengths", "tones", "sid", "noise_scale", "length_scale", "noise_scale_w"] output_names = ["y"] dynamic_axes = { "x": {0: "N", 1: "L"}, "x_lengths": {0: "N"}, "tones": {0: "N", 1: "L"}, "y": {0: "N", 1: "S", 2: "T"}, }opset 选用 18,输入输出全部开启动态轴:N为 batch、L为 token 序列长度、输出y的S与T分别为说话人数与时间帧数。这意味着推理时文本长度不受限制,与 sherpa-onnx 运行时对变长文本的支持保持一致。
4. 写入自定义元数据
导出完成后,add_meta_data()会清空并重写 ONNX 模型的metadata_props。中英模型的元数据如下:
{ "model_type": "melo-vits", "comment": "melo", "version": 2, "language": "Chinese + English", "add_blank": int(model.hps.data.add_blank), # 1 "n_speakers": 1, "jieba": 1, # 中文侧启用 jieba 分词 "sample_rate": model.hps.data.sampling_rate, # 44100 "bert_dim": 1024, "ja_bert_dim": 768, "speaker_id": 1, "lang_id": language_id_map["ZH"], # 3 "tone_start": language_tone_start_map["ZH"], # 0 "url": "...", "license": "MIT license", "description": "MeloTTS is a high-quality multi-lingual text-to-speech library by MyShell.ai", }这些字段正是 sherpa-onnx 运行时判定模型类型与选择前端的关键依据(见下文"源码级原理"一节)。
导出多说话人英文模型(export-onnx-en.py)
export-onnx-en.py 与中英脚本结构基本一致,差异集中在四点:
- 语言与说话人:
language = "EN",n_speakers = len(model.hps.data.spk2id)(即 5),speaker_id固定为 0; - opset 版本降为 13:英文模型的算子集更简单,无需 18;
jieba: 0:纯英文模型不经过中文分词,运行时因此走"纯英文 MeloTtsLexicon"分支(见源码分析);- 词表仅含英文:
generate_lexicon()只遍历eng_dict,不涉及拼音与短语字典,因此lexicon.txt规模远小于中英版。
英文模型同样保留bert_dim: 1024与ja_bert_dim: 768的占位信息,便于运行时统一构造全零 BERT 输入。
校验导出结果:show-info.py
show-info.py 通过 onnxruntime 加载model.onnx并打印所有输入输出与元数据。以中英模型为例,其输出(脚本底部注释中保留了实测结果)为:
NodeArg(name='x', type='tensor(int64)', shape=['N', 'L']) NodeArg(name='x_lengths', type='tensor(int64)', shape=['N']) NodeArg(name='tones', type='tensor(int64)', shape=['N', 'L']) NodeArg(name='sid', type='tensor(int64)', shape=[1]) NodeArg(name='noise_scale', type='tensor(float)', shape=[1]) NodeArg(name='length_scale', type='tensor(float)', shape=[1]) NodeArg(name='noise_scale_w', type='tensor(float)', shape=[1]) ----- NodeArg(name='y', type='tensor(float)', shape=['N', 'S', 'T'])实测元数据为sample_rate: 44100、n_speakers: 1、language: Chinese + English、lang_id: 3。建议每次导出后都运行该脚本,重点核对language、n_speakers、sample_rate、jieba四项,它们直接影响后续运行时前端的选择。
端到端验证:test.py
test.py 用纯 onnxruntime 复现了 sherpa-onnx 的文本前端逻辑,验证转换结果可用,其要点包括:
- 词表加载:从
lexicon.txt/tokens.txt构建word → (phones, tones)映射;对英文模型额外把v映射到V(token ID 14)以对齐 MeloTTS 的post_replace_ph行为;补齐常用标点! ? … , . ' -与空格_; - 中英混排特例:
呣 → 母、嗯 → 恩,处理生僻拟声词; - 中文标点归一:
,。!?分别归一为, . ! ?; - jieba 分词:
jieba.cut(text, HMM=True)先对整句分词,再逐词查表; - add_blank 插空:若元数据
add_blank = 1,则在每个音素前后补0(new_phones[1::2] = phones),这是 VITS 模型训练时引入的停顿位; - 推理参数:
noise_scale = 0.6、length_scale = 1.0、noise_scale_w = 0.8,x_lengths取 token 长度; - 输出落盘:合成结果
y以model.sample_rate(44100 Hz)写入test.wav。
测试文本本身就是一条极佳的验证用例,混合了中英双语与多种标点:
这是一个使用 next generation kaldi 的 text to speech 中英文例子. Thank you! 你觉得如何呢? are you ok? Fantastic! How about you?
源码级原理:sherpa-onnx 如何加载 melo-vits 模型
模型识别
在 offline-tts-vits-model.cc 中,模型加载时检查元数据中的comment字段:
if (comment.find("melo") != std::string::npos) { meta_data_.is_melo_tts = true; }该标志与jieba、language字段一同保存在 offline-tts-vits-model-meta-data.h 的OfflineTtsVitsModelMetaData中。
前端选择
在 offline-tts-vits-impl.h 的InitFrontend()中,运行时按优先级决定采用哪种文本前端:
frontend == "characters"→ 字符级前端;jieba && is_melo_tts→MeloTtsLexicon(中英双语模型命中此分支,melo-tts-lexicon.cc 实现);jieba || use_g2pw→ 通用 CharacterLexicon;is_melo_tts && language == "English"→MeloTtsLexicon(纯英文模型命中此分支)。
也就是说,无论中文还是英文模型,最终都由专门的MeloTtsLexicon类(melo-tts-lexicon.h)完成从文本到(token_ids, tone_ids)的转换,这与 test.py 中的纯 Python 前端逻辑一一对应,也印证了转换脚本中lexicon.txt/tokens.txt两个副产物的必要性——它们是运行时推理不可缺少的一部分。
产物归档与在 sherpa-onnx 中的使用
按 run.sh 的约定,最终产物按目录归档:
zh_en/:中英双语模型(model.onnx+lexicon.txt+tokens.txt+README.md),n_speakers=1;en/:英文多说话人模型(同构文件),n_speakers=5。
在 sherpa-onnx 中使用时,需将三个文件一并提供给离线 TTS 配置:model指向model.onnx,tokens指向tokens.txt,lexicon指向lexicon.txt,并在配置中声明model-type为 VITS 系列。可参照 python-api-examples/offline-tts.py 与 python-api-examples/offline-tts-play.py 的完整调用方式;多说话人英文模型还可在生成时通过sid指定 5 位女声中的任意一位(0~4)。由于推理阶段不再依赖 PyTorch 与 MeloTTS 环境,仅需 onnxruntime,因此导出的模型可无缝用于 Android、iOS、嵌入式等无 Python 环境或离线的部署场景。
常见问题与注意事项
- 说话人数量容易混淆:中英模型只有 1 个说话人,英文模型才有 5 个,二者不可混用元数据,导出后务必用
show-info.py核对n_speakers; lexicon.txt与tokens.txt必须配套:它们在导出时基于模型内部符号表生成,与model.onnx一一对应,部署时三者缺一不可;- 版本锁定:
onnx==1.15.0、onnxruntime==1.16.3、torch==2.3.1+cpu是仓库验证过的组合,升级大版本可能改变算子导出行为; - 自定义英文单词:若业务词表包含 MeloTTS 的
cmudict.rep未收录的词(如kaldi、SF),可在两个导出脚本的add_new_english_words()中按lexicon["word"] = [["phone1", "phone2"], ...]的方式就地补充,脚本注释给出了完整的示例写法。
【免费下载链接】sherpa-onnxSpeech-to-text, text-to-speech, speaker diarization, speech enhancement, source separation, and VAD using next-gen Kaldi with onnxruntime without Internet connection. Support embedded systems, Android, iOS, HarmonyOS, Raspberry Pi, RISC-V, RK NPU, Axera NPU, Ascend NPU, x86_64 servers, websocket server/client, support 12 programming languages项目地址: https://gitcode.com/GitHub_Trending/sh/sherpa-onnx
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考