SpeechTTS: 高效的ONNX推理文本转语音系统


前言
作者:CrazyNET
概述
SpeechTTS 是一个轻量级、高效的文本转语音 (TTS) 系统,基于 SAMBERT-HiFiGAN 架构,优化用于 ONNX 运行时推理。它将原始中文文本转换为自然发音的语音,使用预训练模型导出为 ONNX 格式。该系统利用 KAN-TTS 框架进行语言处理和声学建模,并结合 HiFiGAN 生成高保真波形。
该项目专注于低延迟推理,适用于实时应用,如虚拟助手、有声读物或配音。它支持情感语音合成(例如,使用 “zhizhe_emo” 声音),并且可以轻松扩展到其他声音或语言。
主要特性
- 输入:原始中文文本(例如,“你好,我是智能助手。”)
- 输出:音频波形(16kHz 采样率,单声道)
- 模型大小:紧凑的 ONNX 模型(~10-20MB,取决于导出)
- 推理速度:CPU 上实时能力(例如,现代硬件上 ~0.5-1x RTF);GPU 上更快
- 情感支持:在情感数据集上预训练,用于富有表现力的语音
- 自定义:通过配置文件轻松切换声音或调整参数
安装
先决条件
- Python 3.8+
- ONNX Runtime:
pip install onnxruntime(或onnxruntime-gpu以支持 CUDA) - PyTorch:
pip install torch(用于加载 SAMBERT) - 其他依赖:
pip install pyyaml librosa numpy sounddevice ttsfrd(ttsfrd 用于前端处理) - 环境:推荐使用虚拟环境,如 Conda 或 venv。
依赖项
absl-py==2.3.1
addict==2.4.0
aiohappyeyeballs==2.4.4
aiohttp==3.10.11
aiosignal==1.3.1
alias-free-torch==0.0.6
aliyun-python-sdk-core==2.16.0
aliyun-python-sdk-kms==2.16.5
annotated-types==0.7.0
antlr4-python3-runtime==4.9.3
async-timeout==5.0.1
attrs==25.3.0
audioread==3.0.1
autopep8==2.3.1
bitarray==3.6.0
bitstring==4.3.1
cachetools==5.5.2
certifi==2025.8.3
cffi==1.17.1
cfgv==3.4.0
charset-normalizer==3.4.2
click==8.1.8
coloredlogs==15.0.1
contourpy==1.1.1
crcmod==1.7
cryptography==45.0.6
cycler==0.12.1
datasets==3.1.0
decorator==5.2.1
dill==0.3.8
Distance==0.1.3
distlib==0.4.0
editdistance==0.8.1
einops==0.8.1
espnet-tts-frontend==0.0.3
et_xmlfile==2.0.0
filelock==3.16.1
flatbuffers==25.2.10
fonttools==4.57.0
frozenlist==1.5.0
fsspec==2024.9.0
funasr==1.2.6
g2p==2.2.2
g2p-en==2.1.0
google-auth==2.40.3
google-auth-oauthlib==1.0.0
greenlet==3.1.1
grpcio==1.70.0
h5py==3.11.0
hdbscan==0.8.40
hf-xet==1.1.7
huggingface-hub==0.34.3
humanfriendly==10.0
hydra-core==1.3.2
HyperPyYAML==1.2.2
identify==2.6.1
idna==3.10
importlib_metadata==8.5.0
importlib_resources==6.4.5
inflect==7.0.0
jaconv==0.4.0
jamo==0.4.1
jedi==0.19.2
jieba==0.42.1
Jinja2==3.1.6
jmespath==0.10.0
joblib==1.4.2
kaldiio==2.18.1
-e git+https://github.com/modelscope/KAN-TTS.git@66202d052189a3dcca5f75c718c47a7a5cc68116#egg=kantts
kiwisolver==1.4.7
kwsbp==0.1.0
lazy_loader==0.4
librosa==0.10.1
llvmlite==0.41.1
local-attention==1.10.0
lxml==6.0.0
Markdown==3.7
markdown-it-py==3.0.0
MarkupSafe==2.1.5
matplotlib==3.7.5
mdurl==0.1.2
MinDAEC==0.1.0
mir_eval==0.8.2
modelscope==1.24.1
mpmath==1.3.0
ms-funcodec==0.2.0
msgpack==1.1.1
multidict==6.1.0
multiprocess==0.70.16
munkres==1.1.4
networkx==3.1
nltk==3.9.1
nodeenv==1.9.1
numba==0.58.1
numpy==1.24.4
nvidia-cublas-cu12==12.1.3.1
nvidia-cuda-cupti-cu12==12.1.105
nvidia-cuda-nvrtc-cu12==12.1.105
nvidia-cuda-runtime-cu12==12.1.105
nvidia-cudnn-cu12==9.1.0.70
nvidia-cufft-cu12==11.0.2.54
nvidia-curand-cu12==10.3.2.106
nvidia-cusolver-cu12==11.4.5.107
nvidia-cusparse-cu12==12.1.0.106
nvidia-nccl-cu12==2.20.5
nvidia-nvjitlink-cu12==12.9.86
nvidia-nvtx-cu12==12.1.105
oauthlib==3.3.1
omegaconf==2.3.0
onnx==1.17.0
onnxruntime==1.19.2
onnxsim==0.4.36
openpyxl==3.1.5
oss2==2.19.1
packaging==25.0
pandas==2.0.3
panphon==0.20.0
parso==0.8.4
pexpect==4.9.0
phaseaug==1.0.1
pickleshare==0.7.5
pillow==10.4.0
platformdirs==4.3.6
pooch==1.8.2
pre-commit==3.5.0
prompt_toolkit==3.0.51
propcache==0.2.0
protobuf==5.29.5
ptflops==0.7.3
ptyprocess==0.7.0
py-sound-connect==0.1.0
pyarrow==17.0.0
pyasn1==0.6.1
pyasn1_modules==0.4.2
pycodestyle==2.12.1
pycparser==2.22
pycryptodome==3.23.0
pydantic==2.8.2
pydantic_core==2.20.1
Pygments==2.19.2
pynndescent==0.5.13
pyparsing==3.1.4
pypinyin==0.44.0
python-dateutil==2.9.0.post0
pytorch-wavelets==1.3.0
pytorch-wpe==0.0.1
pytz==2025.2
PyWavelets==1.4.1
PyYAML==6.0.2
regex==2024.11.6
requests==2.32.4
requests-oauthlib==2.0.0
resampy==0.4.3
rich==14.1.0
rotary-embedding-torch==0.8.9
rsa==4.9.1
ruamel.yaml==0.18.14
ruamel.yaml.clib==0.2.8
safetensors==0.5.3
scikit-learn==1.3.2
scipy==1.10.1
sentencepiece==0.2.0
simplejson==3.20.1
six==1.17.0
sortedcontainers==2.4.0
sounddevice==0.5.2
soundfile==0.13.1
sox==1.5.0
soxr==0.3.7
speechbrain==1.0.3
sympy==1.13.3
tensorboard==2.14.0
tensorboard-data-server==0.7.2
tensorboardX==2.6.2.2
text-unidecode==1.3
thop==0.1.1.post2209072238
threadpoolctl==3.5.0
tokenizers==0.20.3
tomli==2.2.1
torch==2.4.0
torch-complex==0.4.4
torchaudio==2.4.0
torchvision==0.19.0
tqdm==4.67.1
traitlets==5.14.3
transformers==4.46.3
triton==3.0.0
ttsfrd==0.2.1
typeguard==2.13.3
typing_extensions==4.13.2
tzdata==2025.2
umap-learn==0.5.7
unicodecsv==0.14.1
Unidecode==1.4.0
urllib3==2.2.3
virtualenv==20.33.1
wcwidth==0.2.13
Werkzeug==3.0.6
xxhash==3.5.0
yarl==1.15.2
zipp==3.20.2
设置步骤
-
获取源码(公众号):
cd SpeechTTS -
安装依赖:
pip install -r req.info(如果没有
req.info,请创建并添加上述包。) -
下载模型:
- 将预训练模型放置在
models/origins/Speech-Sambert-hifigan-TTS-16K/中 - 如果需要,使用
export.py导出 ONNX 模型(见使用部分)。
- 将预训练模型放置在
-
提取资源:
- 在
models/origins/Speech-Sambert-hifigan-TTS-16K/中解压resource.zip以获取语言字典。
- 在
使用
快速开始
使用提供的 run.py 运行推理:
python run.py
这将处理样本文本 “你好,我是智能助手。” 并使用 sounddevice 播放音频。输出将被重采样到 48kHz 以便播放。
自定义推理
修改 run.py 以输入自定义文本:
audio = ort_model.ExecInfer(["您的自定义中文文本。"])
- 播放音频:使用
sounddevice进行实时播放。 - 保存音频:添加保存逻辑,例如
import soundfile as sf; sf.write('output.wav', audio, 16000)。
导出 ONNX 模型
使用 export.py 将 PyTorch HiFiGAN 转换为 ONNX:
python export.py
- 输入:Mel 谱图(形状:[1, 80, T])
- 输出:波形(形状:[1, 1, T * hop_length])
配置参数
配置从 YAML 文件加载(例如,SAMBERT 使用 am/config.yaml,HiFiGAN 使用 voc/config.yaml)。
关键 SAMBERT 参数 (am/config.yaml)
embedding_dim: 512(语言嵌入维度)encoder_num_layers: 8(Transformer 编码器层)decoder_num_layers: 12(Transformer 解码器层)num_mels: 80(Mel 滤波器组)outputs_per_step: 3(长度调节器因子)speaker_units: 32(说话者嵌入维度)emotion_units: 32(情感嵌入维度)- 语言单元:在
dict/中定义(例如,sy_dict.txt用于音节)
关键 HiFiGAN 参数 (voc/config.yaml)
upsample_rates: [10,5,2,2](上采样因子)resblock_kernel_sizes: [3,7,11](残差块内核)sampling_rate: 16000(输出音频采样率)n_fft: 2048(FFT 窗口大小)hop_size: 200(STFT 跳跃长度)
推理参数
- 设备:CPU/GPU(自动检测;在
model.py中设置device='cuda'以使用 GPU) - 批处理大小:1(单个语句;可扩展为批处理)
- 说话者:“M7”(用于 zhizhe_emo;在
model.py中可配置) - 资源路径:从配置自动检测;确保
resource/已提取。
模型细节
架构
SpeechTTS 使用两阶段管道:
-
SAMBERT (声学模型):从语言特征生成 Mel 谱图。
- 输入:文本 → 语言编码(音节、声调、情感、说话者)
- 输出:Mel 谱图(80 维,可变长度)
- 特性:基于 Transformer,具有方差预测器(持续时间、音高、能量)
-
HiFiGAN (声码器):将 Mel 谱图转换为波形。
- 输入:Mel 谱图
- 输出:原始音频波形(16kHz)
- 特性:多感受野融合 (MRF) 用于高保真合成
模型导出为 ONNX 以支持跨平台推理,支持可变长度输入的动态形状。
预训练模型
- 声音:zhizhe_emo(情感男性声音)
- 采样率:16kHz
- 训练数据:带有情感注释的专有数据集
- ONNX 模型:
simplify_model_zhizhe_emo.onnx(简化以提高效率)
语言处理
使用 ttsfrd 前端处理原始文本:
- 将中文文本转换为 MIT 符号(例如,“{ni3 t o n e 3 tone3 tone3s_begin w o r d b e g i n word_begin wordbeginemotion_neutral$M7}”)
- 编码为 SAMBERT 输入向量(音节、声调等)。
算法分析
SAMBERT: 自注意力多频带编码器 TTS
SAMBERT 是一个端到端的声学模型,从文本预测 Mel 谱图。
-
语言编码:
- 文本 → 前端 (ttsfrd):带有韵律、情感、说话者标签的音素序列。
- 嵌入:音节 (sy)、声调、标志嵌入为向量。
-
编码器:
- 多头自注意力 Transformer(8 层)处理语言序列。
- 输出上下文表示。
-
方差适配器:
- 使用 FSMN-RNN 预测持续时间、音高、能量。
- 长度调节器:将编码器输出扩展以匹配 Mel 长度。
-
解码器:
- 混合注意力解码器(12 层)生成 Mel 特征。
- PostNet:使用 FSMN 层细化 Mel。
-
损失:Mel 重构的 MAE,韵律(持续时间/音高/能量)。
效率:每个层 O(N) 由于注意力;ONNX 优化为每秒语音 ~100-200ms(CPU)。
HiFiGAN: 高保真生成对抗网络声码器
HiFiGAN 从 Mel 谱图合成波形。
-
生成器:
- 上采样层将 Mel 扩展到波形分辨率。
- 多感受野融合 (MRF) 块捕获多样模式。
-
判别器:
- 多周期和多尺度判别器确保真实性。
-
训练:对抗损失 + 特征匹配以实现高质量。
效率:并行生成;推理 ~0.2x RTF(CPU),GPU 上更快。
整体管道效率
- 实时因子 (RTF):~0.5-1.0(语音持续时间 / 处理时间)在 i7 CPU 上;RTX GPU 上 <0.1。
- 内存使用:~200-500MB(ONNX 模型 + 运行时)。
- 延迟:短语句 200-500ms。
- 基准(i7-12700H 示例,16GB RAM):
- 10s 语句:SAMBERT ~150ms,HiFiGAN ~100ms,总计 ~250ms (RTF 0.025)。
- 优化:ONNX 简化减少操作 20-30%。
算法实现详解
整体架构
SpeechTTS 采用两阶段流水线架构:
- SAMBERT(声学模型):将语言特征转换为梅尔频谱图
- HiFiGAN(声码器):将梅尔频谱图转换为音频波形
核心算法流程
1. 文本预处理与语言特征提取
# 来自 model_classes/model.py 的核心实现
def ExecInfer(self, text):
if isinstance(text, list):
text = text[0]
# 导入文本处理函数
from kantts.utils.ling_unit import text_to_mit_symbols
# 文本预处理:将原始文本转换为MIT符号序列
texts = [text] # text_to_mit_symbols 期望文本列表
symbols_list = text_to_mit_symbols(texts, self.resource_dir, self.speaker)
# 处理符号序列:提取索引和符号
processed_symbols = []
for item in symbols_list:
parts = item.strip().split('\t')
if len(parts) >= 2:
symbol_part = parts[1].strip()
if symbol_part:
processed_symbols.append(symbol_part)
# 合并所有符号
symbols = " ".join(processed_symbols)
算法说明:
text_to_mit_symbols函数将中文文本转换为包含音素、音调、情感和说话人信息的MIT符号序列- 输出格式:
"{ni3$tone3$s_begin$word_begin$emotion_neutral$M7}" - 支持情感语音合成(通过
emotion_neutral等标签)
2. 语言单元编码
# 语言特征编码
ling_data = self.ling_unit.encode_symbol_sequence(symbols)
# 分解为不同的语言特征
sy = torch.from_numpy(ling_data[0]).unsqueeze(0).long().to(self.device) # 音节
tone = torch.from_numpy(ling_data[1]).unsqueeze(0).long().to(self.device) # 音调
syllable_flag = torch.from_numpy(ling_data[2]).unsqueeze(0).long().to(self.device) # 音节标志
word_segment = torch.from_numpy(ling_data[3]).unsqueeze(0).long().to(self.device) # 词分段
inputs_emotion = torch.from_numpy(ling_data[4]).unsqueeze(0).long().to(self.device) # 情感
inputs_speaker = torch.from_numpy(ling_data[5]).unsqueeze(0).long().to(self.device) # 说话人
# 组合语言特征输入
inputs_ling = torch.stack([sy, tone, syllable_flag, word_segment], dim=2)
算法说明:
KanTtsLinguisticUnit负责将符号序列编码为数值特征向量- 包含6种语言特征:音节、音调、音节标志、词分段、情感、说话人
- 每种特征都被编码为整数索引,便于模型处理
3. SAMBERT声学模型推理
# SAMBERT模型推理
with torch.no_grad():
res = self.sambert(inputs_ling, inputs_emotion, inputs_speaker, input_lengths)
mel = res['postnet_outputs'].cpu().numpy()
mel = np.transpose(mel, (0, 2, 1))
SAMBERT架构详解:
-
编码器(Encoder):
- 8层Transformer多头自注意力机制
- 处理输入的语言特征序列
- 输出上下文相关的表示
-
方差适配器(Variance Adaptor):
- 使用FSMN-RNN预测时长、音高、能量
- 长度调节器:将编码器输出扩展到与梅尔频谱长度匹配
-
解码器(Decoder):
- 12层混合注意力解码器
- 生成梅尔频谱特征
- PostNet使用FSMN层进行精细化处理
4. HiFiGAN声码器推理
# ONNX推理
input_feed = {self.input_name[0]: mel}
audio = self.onnx_session.run(self.output_name, input_feed=input_feed)[0].squeeze()
HiFiGAN架构详解:
-
生成器(Generator):
- 上采样层:将梅尔频谱上采样到波形分辨率
- 多感受野融合(MRF)块:捕获不同尺度的模式
- 上采样率:[10, 5, 2, 2],总倍数为200
-
判别器(Discriminator):
- 多周期判别器:确保周期性特征的真实性
- 多尺度判别器:确保不同尺度下的音频质量
关键算法参数
SAMBERT参数(来自 am/config.yaml)
Model:
KanTtsSAMBERT:
params:
embedding_dim: 512 # 嵌入维度
encoder_num_layers: 8 # 编码器层数
decoder_num_layers: 12 # 解码器层数
num_mels: 80 # 梅尔滤波器数量
outputs_per_step: 3 # 长度调节器因子
speaker_units: 32 # 说话人嵌入维度
emotion_units: 32 # 情感嵌入维度
HiFiGAN参数(来自 voc/config.yaml)
upsample_rates: [10, 5, 2, 2] # 上采样率
resblock_kernel_sizes: [3, 7, 11] # 残差块核大小
sampling_rate: 16000 # 采样率
n_fft: 2048 # FFT窗口大小
hop_size: 200 # STFT跳长
性能优化算法
1. ONNX优化
# 来自 model_classes/model.py 的ONNX会话优化
def __InitSession(self):
self.sess_options = onnxruntime.SessionOptions()
self.sess_options.intra_op_num_threads = 4
self.sess_options.execution_mode = onnxruntime.ExecutionMode.ORT_SEQUENTIAL
self.sess_options.graph_optimization_level = onnxruntime.GraphOptimizationLevel.ORT_ENABLE_ALL
优化策略:
- 图优化级别:
ORT_ENABLE_ALL(启用所有优化) - 线程数:4个内部操作线程
- 执行模式:顺序执行模式
2. 实时性优化
- 实时因子(RTF):约0.5-1.0(在i7 CPU上)
- 内存使用:约200-500MB
- 延迟:短语句200-500ms
算法复杂度分析
SAMBERT复杂度
- 时间复杂度:O(N×L),其中N为层数,L为序列长度
- 空间复杂度:O(d×L),其中d为嵌入维度
- 推理时间:约100-200ms/秒语音
HiFiGAN复杂度
- 时间复杂度:O(T×C),其中T为时间步长,C为通道数
- 空间复杂度:O(C×K),其中K为核大小
- 推理时间:约50-100ms/秒语音
完整推理流程示例
# 来自 run.py 的完整使用示例
from model_classes.model import ONNXModel
import sounddevice as sd
import numpy as np
from model_utils.any_utils import resample_audio
# 初始化模型
ort_model = ONNXModel(
onnx_path="./models/exports/speech_sambert_hifigan_tts_16k_onnx/simplify_model_zhizhe_emo.onnx",
sambert_cfg="./models/origins/Speech-Sambert-hifigan-TTS-16K/voices/zhizhe_emo/am/config.yaml",
sambert_ckpt="./models/origins/Speech-Sambert-hifigan-TTS-16K/voices/zhizhe_emo/am/ckpt/checkpoint_0.pth",
)
# 执行推理
audio = ort_model.ExecInfer(["你好,我是智能助手。我的名字叫KanTTS"])
# 音频播放
out_stream = sd.OutputStream(samplerate=48000, blocksize=1024, device=8, channels=1, dtype=np.float32)
out_stream.start()
audio = resample_audio(audio, 16000, 48000) # 重采样到48kHz
out_stream.write(audio)
out_stream.stop()
out_stream.close()
这个实现展示了从文本到音频的完整流水线,包括文本预处理、语言特征提取、声学模型推理、声码器合成和音频播放等关键步骤。
联系方式
- 公众号:“CrazyNET”
- 邮箱:1145570610@qq.com
模型及其源码获取
- 公众号回复“kantts-onnx”
683

被折叠的 条评论
为什么被折叠?



