✨ Bert-VITS2 Android 版, 推理框架基于 alibaba-MNN.
本工程提供了一个示例,实现了离线推理版本的 Bert-VITS2 (2.3版本),目前已支持中 / 日 / 英三语 TTS 推理,其中部分中文角色还支持中英文混合输入:
- 🧠 蒸馏版多语 BERT 模型 :中文 Bert 使用了一个自制的蒸馏版本,基于 Wikipedia 中文以及 SkyPile 中文数据集,共计约 1000W 条文本进行模型蒸馏,将体积缩减至 30M。(也不知道蒸的咋样反正最后看曲线是收敛了 -.-);日 / 英 BERT 也按类似思路压缩,统一在端上推理。
- 🏗 MNN :基于 MNN 推理框架实现 BV2 的整个推理流程,推理参考自其 onnx 推理代码。(pth 直接转不成功,你没资格啊,你没资格.jpg)
- 🧹 cppjieba / cpptokenizer / openjtalk :用来平替 Python 端的 jieba 分词、huggingface tokenizer 以及日文的 open-jtalk。一些 BV2 独有的文本预处理步骤使用 Kotlin 进行平替实现。(此过程 GPT/Claude 老祖帮了许多)
- 📦 AAR 发布 :推理相关代码已下沉到
bertvits2-infer-wrapper模块,可一键打成 AAR 给其它 App 接入,详见 PUBLISHING.md。
整个过程在 Android 端全程 离线推理 无需任何联网服务.
Input Text (ZH / JP / EN / ZH+EN Mix)
↓
Tokenization + G2P (cppjieba + cpptokenizer + openjtalk + kotlin code)
↓
BERT embedding (distilled ZH / JP / EN model)
↓
Encoder + Emb + DP/SDP + Flow + Decoder (BV2 infer by MNN)
↓
Waveform output (.wav)
下表展示了 demo App 中内置的角色样例,完整角色清单可参考 VoiceViewModel.kt。中文角色基于部分明日方舟语音集、鸣潮语音集、拜年祭视频以及原神语音集等公开数据训练,日 / 英角色基于各自游戏对应公开语音集训练,英文有些示例语音有点破音,日文语音有点无口感,多半是微调数据里面掺了复杂的东西导致学岔了(雾,相信你们炼自己的角色能弄得更好~。
| Character | Language | Sample Rate | Text | Audio |
|---|---|---|---|---|
| 陈 | 中文 | 44100 Hz | 博士,当初在龙门,我不该放你走的。 | 🔊 Play |
| 珐露珊 | 中文 | 44100 Hz | 旅行者,好久不见。 | 🔊 Play |
| 甘雨 | 中文 | 44100 Hz | 工作还没有做完,又要开始搬砖了。 | 🔊 Play |
| 22娘 | 中英混合 | 22050 Hz | RTX 5090 将于明年发布,敬请期待! | 🔊 Play |
| APPLe | 英文 | 44100 Hz | Greetings, madam. I am here. Clouds help predict the weather. | 🔊 Play |
| Sonetto | 英文 | 44100 Hz | Timekeeper, at your service. The stars shine bright tonight. | 🔊 Play |
| Vertin | 英文 | 44100 Hz | The storm is coming. We must prepare ourselves. | 🔊 Play |
| 八重神子 | 日文 | 44100 Hz | たびびと、きょうはどんなおもしろいほんをもってきてくれたの?もしないようがつまらなかったら、わたし、へんしゅうぶに『しげき』にいこうかな~? | 🔊 Play |
| 宵宫 | 日文 | 44100 Hz | こんにちは、皆さん。今日は素晴らしい一日ですね。 | 🔊 Play |
| 椿 | 日文 | 44100 Hz | あなたといると、なぜか落ち着くの。 | 🔊 Play |
| 野兽先辈 | 日文 | 44100 Hz | にじゅうよんさいはがくせいです | 🔊 Play |
新增的 22k 采样率底模放在 base_model_22k/ 目录下:
经过多次测试和取舍,22k 采样率能在性能和效果之间达到最佳的平衡,底模基于原神、鸣潮、绝区零、崩铁四款游戏的角色语音训练,仅供学习交流使用。且对应调整了 decoder 的相关参数,原始训练代码里有一些硬编码逻辑可能需要稍作调整,这里不做赘述。
如需基于该底模做二次训练,请配合 BertVITS2 2.3 版本使用。
下面给出一组在中端旗舰 SoC 上的实测数据,仅供选型时做横向参考:
| 项目 | 数值 |
|---|---|
| 测试机型 | Qualcomm Snapdragon 888 |
| 模型采样率 | 22050 Hz |
| 模型体积 | ≈ 29.7 MB(22050 Hz BV2 全模块 MNN,weight quant int8) |
| 测试文本 | "RTX 5090 将于明年发布,敬请期待"(中英混合,约 10 个中文字 + 1 个英文短串) |
| 端到端耗时 | ≈ 1856 ms(含文本预处理 + Encoder + Flow + Decoder) |
| 合成音频时长 | ≈ 5.20 s(22050 Hz × 114688 frames) |
| RTF(实时率) | ≈ 0.357(< 1 表示推理速度快于音频播放速度,可用于流式 / 实时场景) |
| 估算吞吐 | 每秒可合成约 2.80 s 音频 |
实际表现会随机型 SOC、温度、后台调度策略、文本长度以及 backendConfig 不同而有所波动,以上数值仅作量级参考。骁龙 8 Gen2 / 8 Gen3 等更新平台一般还能再快 30% – 50%。
推理入口模块 bertvits2-infer-wrapper 以及其依赖的 native 模块均已支持 maven-publish,可以一键产出 AAR 给其它 APK 接入。
./gradlew publishAars完整用法(含坐标自定义、本地仓库目录、下游接入示例等)参见 PUBLISHING.md。
GIT_LFS_SKIP_SMUDGE=1 git clone --recurse-submodules git@github.com:Voine/Bert-VITS2-MNN.git
# for windows powershell
$env:GIT_LFS_SKIP_SMUDGE=1; git clone --recurse-submodules git@github.com:Voine/Bert-VITS2-MNN.git
cd Bert-VITS2-MNNIf already cloned:
git submodule update --init --recursive📦 建议使用 Android Studio 进行工程编译,用 IDE 打开根目录即可
# From project root
./gradlew assembleRelease本工程的一些文件如 .mnn ,使用 lfs 进行存储,需要按照如下方式拉代码:
git lfs install
git lfs pullTo track files (if contributing):
git lfs track "*.mnn"| Library | Path |
|---|---|
| MNN | third_party/MNN |
| cppjieba | third_party/cppjieba |
| tokenizer-cpp | third_party/tokenizers-cpp |
中文模型基于 chinese-roberta-wwm-ext-large 进行蒸馏,为适配移动端,大幅缩减了体积。原版直接转换能有 1.2G ...
日 / 英 BERT 也走相同的蒸馏 + 量化思路。
蒸馏代码详见 distill/README.md.
- 如果你需要替换自己的模型尝试验证,首先需要参考 BertVITS2 内的说明进行训练得到桌面端模型,目前仅支持 2.3 版本,本工程基于的 BV2 代码 commit 为 13424595,如需自制模型,建议 BV2 代码版本保持一致;若使用 22k 采样率,请参考
base_model_22k/内的config.json与G_0.pth。 - 将你的 pth 模型转换成 onnx, onnx 导出脚本在 这里
- 使用 MNN Convert 将所有模块的 onnx 模型转成 mnn, 转换命令参考:
./MNNConvert --modelFile your_path_to_onnx.onnx --MNNModel your_path_to_mnn.mnn --framework ONNX --bizCode MNN --weightQuantBits 8 --weightQuantAsymmetric- 放到 bertvits2-jni/src/main/assets 内,加载代码需参考 BertVITS2SimpleInferImpl.kt
目前在 third_party 内的 cppjieba、tokenizer-cpp 以及 MNN 仅是为了提供头文件,若需要自行编译 tokenizer-cpp 并替换产物 libtokenizers_c.a libtokenizers_cpp.a,需修改 huggingface_tokenizer.cc 内的 add_special_tokens 默认为 true
- Bert 模型作为整个系统的输入,很多时候只是起一个辅助的作用,有时候去掉了也不会对推理结果造成毁灭性的影响,可能就会是呆一点?平淡一点或者有点脱线的感觉 :),但 Bert 模型本身哪怕经过蒸馏有时候体积也是很大的,所以在实际工程接入时,可以考虑对其进行取舍~
- 由于在 demo app 里面夹杂了多个语种的模型推理,所以很多逻辑弄成了懒加载,这会导致首次推理的时候会比较慢,实际工程接入的时候可以考虑把这些懒加载过程隐藏到别的流程里或者优化一下懒加载的速度~
├── app/ # Demo App
│ └── src/main/
│ ├── assets # mnn bert model, cppjieba dic, mnn bv2 model
│ └── java/ # UI / ViewModel
├── bertvits2-infer-wrapper/ # 对外推理入口(可打 AAR 给其它 App 接入)
├── bertvits2-jni/ # Bert-VITS2 推理 JNI
├── text-preprocess/ # 中 / 日 / 英 / 混合 文本预处理
├── cppjieba/ # cppjieba interface
├── cpptokenizer/ # cpptokenizer interface
├── openjtalk/ # open-jtalk interface (日文 G2P)
├── base_model_22k/ # 22k 采样率底模
├── distill/ # BERT 蒸馏脚本
└── third_party/ # 三方头文件
本工程基于以下前辈们的贡献做了一些微不足道的搬砖工作,也希望能为后续在端智能推理捣鼓的小伙伴提供一些参考。
- 迁移到移动版老婆聊天器中