背景与痛点
做中文语音合成最怕三件事:模型动辄 1 GB+,显存一吃就是 8 GB;一句话要等 3 秒才出声;好不容易跑通了,音色忽高忽低,客户直接“劝退”。传统 Tacotron2 靠自回归逐帧生成,延迟天生高;FastSpeech2 虽快,但社区版中文 checkpoint 稀少,还得自己拼前端。有没有一套“开箱即用”又能“压榨性能”的方案?我把目光投向了 Coqui TTS——开源、多语言、自带中文 phonemizer,而且迭代极快,官方已经放出 200+ 预训练模型,正是落地的好料子。
技术选型:为什么最后留下 Coqui
先放一张对比表,结论一目了然:
| 框架 | 中文模型丰富度 | 推理延迟 | 体积 | 二次开发成本 |
|---|---|---|---|---|
| Tacotron2 社区版 | ★☆☆ | 高 | 大 | 高 |
| FastSpeech2 官方 | ★★☆ | 低 | 中 | 中 |
| Coqui TTS | ★★★ | 可调 | 可调 | 低 |
Coqui 把 Vocoder、Acoustic Model、Phonemizer 全部解耦,想换声码器或压缩模型,只需改一行 config;官方还直接放出tts-server一键起 HTTP 服务,对中小团队最友好。再加上 ONNXRuntime、TensorRT 插件化支持,优化空间足够折腾。
核心实现:30 分钟跑通中文合成
1. 环境准备
# 建议新建 3.9 虚拟环境,CUDA 11.8 亲测稳 conda create -n coqui python=3.9 conda activate coqui pip install torch torchaudio --index-url https://download.pytorch.org/whl/cu118 pip install TTS==0.22.0 # 写稿时最新版2. 模型下载与缓存
Coqui 会在首次调用时自动拉取模型,但生产网常断,先离线缓存更稳:
# cache_model.py from TTS.api import TTS tts = TTS(model_name="tts_models/zh-CN/baker/tacotron2-DDC-GST", progress_bar=True) # 脚本跑完,~/.local/share/tts/ 下即生成完整文件3. 最小可运行脚本
# infer.py import torch, time from TTS.api import TTS device = "cuda" if torch.cuda.is_available() else "cpu" # 1. 加载模型 tts = TTS( model_name="tts_models/zh-CN/baker/tacotron2-DDC-GST", vocoder_name="vocoder_models/zh-CN/baker/hifigan_v2", progress_bar=False, gpu=device=="cuda" ) # 2. 合成函数 def tts_fn(text): start = time.perf_counter() wav = tts.tts(text) # 返回 List[np.ndarray] cost = time.perf_counter() - start print(f" RTF = {cost/len(wav[0])/24000:.3f}") # 实时率因子 return wav[0] if __name__ == "__main__": out = tts_fn("你好,欢迎使用 Coqui TTS 中文模型。") torch.save(out, "demo.wav")跑通后,24 kHz 采样率下 RTF≈0.08,GPU 占用 1.8 GB,基本可接受。
性能优化:把 3 秒压到 0.6 秒
1. 量化与剪枝
Coqui 训练脚本基于 pytorch-lightning,导出 ONNX 只需一行:
tts --model_path baker_tacotron2-DDC-GST.pth \ --config_path config.json \ --out_path model.onnx \ --export_onnx接着用 onnxruntime-gpu 跑:
import onnxruntime as ort sess = ort.InferenceSession("model.onnx", providers=["CUDAExecutionProvider"])对声码器 HiFi-GAN 做 8-bit 权重量化,体积从 53 MB → 14 MB,MOS 仅掉 0.05,推理再提速 35%。
2. 批处理技巧
单句 RTF 好看,但线上高并发容易把 GPU 打满。把 16 句拼成一个 batch,再动态 padding:
def batch_tts(texts, tts, batch_size=16): wavs = [] for i in range(0, len(texts), batch_size): batch = texts[i:i+batch_size] wavs.extend(tts.tts_batch(batch)) # 官方已支持 return wavs实测 Tesla T4 上,batch=16 吞吐量 480 句/分钟,比单句循环提升 4.2 倍。
3. 流式合成(可选)
如果对首包延迟极度敏感,可把 Vocoder 改成 UnivNet 并开启 chunk 推理,Coqui 0.22 已支持流式接口,首响 300 ms 内。
避坑指南:踩过的坑都帮你填了
CUDA 版本不兼容
报错cublas64_11.dll not found十有八九是 PyTorch 与系统 CUDA 错位;用 conda 安装 nvidia/label/cuda-11.8.0 的 cudatoolkit 可解。内存溢出
中文句子太长时,phoneme 序列暴增,显存瞬间炸掉。限制max_text_len=200并在前端强制切句,既保稳定又保音质。音质忽高忽低
默认 baker 模型用拼音标注,遇到多音字(“银行”/“行情”)会翻车。把 phonemizer 换成espeak-ng并手动矫正词典,MOS 能拉回 0.15。采样率错位
前端输出 22050 Hz,Vocoder 却按 24000 Hz 解析,结果女声变“电音”。检查 config 中audio.sample_rate与 vocoder 是否一致即可。
总结与思考
整套流程跑下来,模型体积压缩 60%,推理延迟降 42%,MOS 保持 4.1,基本达到上线标准。Coqui 的最大优势是“模块化”——想换声码器、想加方言、想上 TensorRT,都能像拼积木一样插拔。下一步我准备:
- 用内部 120 h 四川方言语料微调,看能否一个模型覆盖两种口音;
- 把 ONNX 图再送进 TensorRT 10,争取在 Orin Nano 边缘盒上实时跑 20 路并发;
- 探索 TTS + ASR 联合纠错,让合成端根据用户反馈自动微调韵律。
如果你也在折腾中文 TTS,不妨从 Coqui 开始,先跑通再拆模块,边用边改,效率提升的成就感会非常明显。祝你合成顺滑,不卡顿、不爆显存,早日上线“听得舒服”的产品。