moonshine-tts 中文简体(zh_hans)G2P 数据包解析:词典、RoBERTa UPOS 模型与可复现构建流程
2026/9/15 19:04:05 网站建设 项目流程

moonshine-tts 中文简体(zh_hans)G2P 数据包解析:词典、RoBERTa UPOS 模型与可复现构建流程

【免费下载链接】moonshineVery low latency speech to text, intent recognition, and text to speech, for building voice agents and interfaces项目地址: https://gitcode.com/GitHub_Trending/moonshine3/moonshine

本文围绕moonshine-tts简体中文(zh_hans)G2P(字素到音素)数据包展开,逐项拆解其两份核心资产——dict.tsv普通话 IPA 词典与roberta_chinese_base_upos_onnx/ONNX 模型束,说明它们在中文文本转语音流水线中的实际作用、数据来源,以及从原始语料到可部署 ORT 权重对的完整复现命令。读完本文,你将掌握该语言数据包的目录结构、C++ 侧消费它的三个关键类、split-model-weights.py拆分 ORT 权重对的原理,以及修改模型后必须执行的配套步骤(wasm 算子配置与归档重建)。

数据包组成:词典 + 词性标注模型的双通道设计

简体中文包位于 core/moonshine-tts/data/zh_hans/,是moonshine-tts按语言组织的数据包之一(同级还有日语、韩语、阿拉伯语等十余个语言包,总览见 core/moonshine-tts/data/README.md)。它由两部分组成,分别解决中文 G2P 的两个难点:

资产内容与格式在 G2P 流程中的角色
dict.tsv词/短语 → 普通话 IPA 的制表符分隔词典提供词级读音,配合词性感知(POS-aware)消歧处理常见多音字
roberta_chinese_base_upos_onnx/RoBERTa 词元分类 ONNX 模型束(WordPiece 分词 + UPOS 通用词性标注)在 C++ 侧由ChineseTokPosOnnx/ChineseOnnxG2p消费,负责分词词性特征提取

两条通道的分工在源码中体现得非常清楚。ChineseOnnxG2p(见 chinese-onnx-g2p.h)内部持有两个对象:

  • ChineseTokPosOnnx tok_:跑 ONNX 模型,对输入文本做 BIO 序列标注,返回一列(surface, UPOS)二元组;
  • ChineseRuleG2p lex_:加载dict.tsv词典,对每个词执行基于规则的多音字消歧与 IPA 生成。

ChineseOnnxG2p::text_to_ipa的调用链(见 chinese-onnx-g2p.cpp)是:先对输入做 NFC 规范化(借助 utf8proc),再由tok_.annotate()得到(词, 词性)对,最后逐词调用lex_.word_to_ipa_with_pos(word, pos)——词性正是这里的关键输入。ChineseOnnxRuleG2p则是将其包装成统一的RuleBasedG2p接口(见 rule-based-g2p-factory.cpp 中的kG2pChineseOnnxDirKey注册逻辑),并声明与纯规则版ChineseRuleG2p相同的方言 ID 集合:zhzh-Hanszh_CNzh-CNzh_hansztcmnChinese(见 chinese.cpp)。

POS 感知的多音字消歧:词典的深度用法

dict.tsv之所以强调“配合 POS-aware 消歧”,是因为中文多音字必须结合句法角色才能定音。ChineseRuleG2p::disambiguate_heteronym(见 chinese.cpp)内置了一组经典多音字的手写规则,例如:

  • :名词性(noun_like_pos集合)取xɑŋ/xɤŋ,动词性(verb_like_pos集合)取ɕɪŋ
  • AS/SP/ETC/PART等助词位取,动词位VV/VERBljɑʊ
  • :动词性取meɪ,名词性取/
  • 着 / 地 / 得 / 长 / 数等均按verb_like_posnoun_like_pos以及DEV/ADV/DER等专用标签分派。

其中verb_like_pos集合包含VV, VA, VE, VC, LB, BA, SB, MSP, AS, DER, DEV, DEC, VERB, AUX, ADVnoun_like_pos包含NN, NR, NT, LC, OD, M, CD, DT, PN, NOUN, PROPN, ADJ, DET, PRON, NUM,另有skip_phonetic_posPU, SP, URL, EM, NOI, PUNCT, SYM, X)用于跳过无需注音的词元(见 chinese.cpp)。这些标签恰好就是 RoBERTa UPOS 模型输出的 UPOS/BIO 标签体系,两条通道因此是严丝合缝的上下游。

此外,词典查询失败时还有三级回退(chinese.cpp):逐汉字查单字读音 → 阿拉伯/全角数字转汉字后注音 → 纯 ASCII 字母按小写原样输出。

数据来源(Provenance)

数据包 README 明确记录了每份资产的出处:

  • dict.tsv:源自 open-dict-data/ipa-dict 的data/zh_hans.txt(MIT 协议),由scripts/download_multilingual_ipa_lexicons.py拉取生成;
  • ONNX 模型束:源自 Hugging Face 上的 KoichiYasuoka/chinese-roberta-base-upos 模型。

这意味着词典本体是开放许可数据,模型则是把公开的 RoBERTa UPOS 检查点导出为 ONNX 后按需裁剪,两条链路都可从源头重新构建。

完整复现流程(Recreating)

复现流程分三步:先重建词典,再导出 ONNX,最后拆分为可部署的 ORT 权重对。

第一步:重建词典

python scripts/download_multilingual_ipa_lexicons.py --only zh_hans

--only zh_hans表示只拉取简体中文这一份,避免全量下载其余十个语言包。该脚本在 core/moonshine-tts/data/README.md 的“再生验证”一节被列为确定性配方——2026-03-30 的验证中,de/fr/it/ja/ko/nl/pt_br/pt_pt/ru/vi/zh_hans十一个dict.tsv均与仓库树中文件逐字节一致。

第二步:导出 RoBERTa UPOS ONNX

pip install torch onnx transformers numpy python scripts/export_chinese_roberta_upos_onnx.py

导出脚本默认输出到data/zh_hans/roberta_chinese_base_upos_onnx/,会同时产出model.onnxvocab.txttokenizer_config.jsonmeta.json及若干可选的 tokenizer 附属文件。执行前请留意脚本内关于 PyTorch /transformers版本兼容性的注释。

第三步:拆分为 ORT 权重对

python scripts/split-model-weights.py \ data/zh_hans/roberta_chinese_base_upos_onnx/model.onnx

这一步会写入两个文件(脚本本体就在仓库中,见 scripts/split-model-weights.py):

  • model.model.ort:融合后的计算图,权重声明为图输入、本身不携带权重数据
  • model.weights.ort:int8 权重 + 反量化链,在加载时反量化一次

为什么.onnx只是中间产物、不提交进仓库?README 给出的理由是:wasm 运行时是一个裁剪到极致的 ORT 构建,完全无法读取.onnx格式,必须使用 ORT 格式。其底层机制详见 core/moonshine-tts/src/split-weights.h 与 scripts/split-model-weights.py:

  • ORT 格式在转换时就把图优化(graph optimization)烘焙进文件,加载时不再重新做优化;
  • 对以 int8 存储、经Cast -> Mul -> Add链反量化的权重,直接折叠成 float32 会使文件膨胀约 4 倍,而保留反量化链则每次推理都要重复执行;
  • 拆分的折中方案:model.model.ort保留全部算子融合但把权重改成输入,model.weights.ort只保留 int8 数据与反量化链,运行时启动时执行一次权重模型,把 float32 结果喂给计算图,之后每次推理都复用(run_split_weights_model执行完即释放权重会话,只保留 float32 结果常驻内存)。

值得注意,split-weights.h 同时定义了kSplitWeightsMinSequenceLength = 32:当权重是图输入时,ORT 无法在加载时对常量MatMul操作数做分块预打包,短序列会落入非打包内核导致推理明显变慢;调用方应把短序列 padding 到该长度再掩码掉填充。ChineseTokPosOnnx头文件中的max_sequence_length_ = 512pad_id_ = 1(见 chinese-tok-pos-onnx.h)正是这一机制的配套约束。

第四步:确认 C++ 侧加载所需的最小文件集

C++ 加载器至少需要以下文件:

  • model.model.ort
  • model.weights.ort
  • vocab.txt
  • tokenizer_config.json
  • meta.json

(导出时产生的额外 tokenizer 文件对 C++ 加载器是可选的。)同时加载器也兼容磁盘上单个model.ortmodel.onnx的形态。随后把dict.tsv与模型目录按需复制进data/zh_hans/即可——这正是resolve_chinese_dict_pathmodel_root/zh_hans/dict.tsv)与resolve_chinese_onnx_model_dirmodel_root/zh_hans/roberta_chinese_base_upos_onnx)所约定的目录布局(见 chinese.cpp)。

字节稳定性:哪些能复现,哪些不能

数据包 README 对“重新导出是否与提交产物一致”给出了审慎的结论,并建议以meta.json+ tokenizer 资产 + 一致性测试作为契约,而非追求逐字节一致:

  • meta.jsonvocab.txt:多次观察均能逐字节复现;
  • tokenizer_config.json:可能因transformers版本差异出现小的 JSON 差异;
  • model.onnx:由于 PyTorch ONNX 后端、int8 收缩(shrink)与 opset 路径的影响,不能保证逐字节一致

这一结论在 core/moonshine-tts/data/README.md 的“再生验证(2026-03-30)”表格中被进一步证实:export_chinese_roberta_upos_onnx.py的复现结果被标记为Partial——meta.jsonvocab.txt匹配,tokenizer_config.json仅差一个空的extra_special_tokens键,而model.onnx在 torch 2.10 下因图结构/int8 收缩路径不同而存在差异。相比之下,词典配方是确定性的,这也是为何“词典逐字节一致、Transformer 导出允许字节漂移”成为多语言包的通用经验法则。

一致性测试:golden 文件验证

仓库通过 golden 文件测试锁定行为契约,见 chinese-tok-pos-onnx-test.cpp:

  • 单句测试:对"上海是一座城市。"调用annotate,把format_annotated_line的输出与 golden 参考文件(位于tests/data/zh_hans/下的tok_pos_sample.txt)逐字符比对;
  • 语料测试:取zh_hans/wiki-text.txt前 100 行,与tok_pos_wiki_filtered.txt逐行比对,并刻意跳过混合脚本 / NFC 边缘情况的行(与 Python 端 WordPiece 路径的行为保持一致);
  • 结构约束:当磁盘上是拆分权重对时,uses_split_weights()必须为真——测试明确注释“回退到单文件虽能通过 golden 检查,但会失去加载期收益”,直接约束了加载路径必须真正启用拆分机制。

修改模型后的配套动作:wasm 算子配置与归档重建

这是数据包 README 的最后一条硬性提醒,也是实际维护中最容易踩坑的一步:

修改该模型后,必须重新生成 wasm 算子配置并重建归档,否则浏览器端构建将无法加载该模型。

原因是 wasm 运行时是裁剪后的最小 ORT 构建,只包含列入白名单的算子,新增算子或变更图结构都可能超出白名单。具体操作细节参见 language-bindings/wasm/README.md 中 “The minimal build, and what it costs you” 一节,以及仓库中的 scripts/generate-ort-op-config.py(生成算子白名单配置)与 scripts/convert-models-to-ort.py(ORT 格式转换的总入口)。也就是说,一次完整的模型升级流程是:重新导出 ONNX → 拆分 ORT 权重对 → 更新generate-ort-op-config.py产出的算子配置 → 重建 wasm 归档 → 用 golden 测试验证行为一致。

小结:一份数据包,三处工程取舍

回顾整个zh_hans数据包,可以提炼出三个贯穿始终的工程决策,也适用于其他语言包:

  1. 双通道设计:词典解决“读音”,ONNX 词性标注解决“该读哪个音”,两者通过 UPOS 标签体系耦合,兼顾了覆盖面(词典覆盖不到的走逐字回退)与准确率(多音字按句法消歧);
  2. ORT 拆分而非直接提交 ONNX:既绕开了 wasm 最小构建无法解析.onnx的限制,又避免了 int8 反量化链每次推理重复执行的代价——反量化被压缩为“加载时执行一次”;
  3. 字节稳定性分级对待:词典确定性复现,Transformer 模型以“meta.json + tokenizer + 一致性测试”为契约,允许版本漂移,用 golden 测试而非字节哈希来守护行为。

对于需要定制中文语音合成或自建中文 G2P 的开发者,这个数据包本身就是一个可完整复现的参考实现:从开放词典、公开模型检查点,到仓库内的拆分脚本与一致性测试,全链路闭环,可按本文的命令逐步重建属于自己的版本。

【免费下载链接】moonshineVery low latency speech to text, intent recognition, and text to speech, for building voice agents and interfaces项目地址: https://gitcode.com/GitHub_Trending/moonshine3/moonshine

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询