简介面向语音合成与自然语言处理研究人员及开发者这份算法工程资料包基于改造的 Tacotron 端到端序列到序列模型系统讲解中文语音合成完整训练方案。内容覆盖中文音节结构与声调特性的输入表示、注意力机制改进、声学特征选取以及 WaveNet/WaveRNN 声码器替换思路并附有 TTS-CORPUS、AISHELL 等开源数据集的获取方式与详细环境搭建教程。资源包共 36 个文件以 18 个 Python 脚本为主体涵盖模型核心代码、训练配置、依赖清单并附可运行的 Notebook 演示与说明文档另有 lib、tests 等辅助模块压缩包整体仅 11.47MB便于快速下载部署。已有 691 人学习下载。通过数据预处理脚本与训练流程对照读者可从零构建中文语音合成系统掌握注意力机制调优、声码器选型及 MOS 评估方法也可为定制中文语音模型提供工程参照适合具备一定深度学习基础、希望实战端到端语音合成的中高级开发者与研究者。1. 把Tacotron改成说中文的系统这份资源到底解决了什么很多跑过英文 Tacotron 的人第一次把模型切到中文语料时合成出来的是那种每个字都读得清楚、但连起来像机器念经的声音。这个资源包就是冲着这个问题来的它基于 PyTorch 版的 Tacotron 做了面向中文的改造压缩包里带完整训练代码、注意力可视化 notebook、推理脚本和 setup.py配套数据集的获取方式和环境搭建教程都写进了文档里。适合已经跑通英文 TTS、想转中文方向的人也适合刚接触端到端语音合成、想从代码层面理解注意力机制怎么处理中文语流的学习者。如果你只是想要一个能直接部署的成品音色这个包给不了现成模型它给你的是从零训练出一套中文声学模型的能力以及训练路上每一个坑的绕行路线。2. 中文改造点与代码结构从拼音输入到注意力机制的落地改动2.1 中文和英文的差异决定了Tacotron必须改三处Tacotron 最早是为英文设计的英文还能按单词边界切分中文是连续语流字与字之间没有天然分隔再加上声调直接决定语义没有一个明确的“字→拼音声调”映射模型很难把输入文本映射到正确的韵律上。这个包在改造时主要动了三块输入表示用拼音序列加声调数字nǐ hǎo 写成 ni3 hao3替代英文音素序列注意力机制改成更适应连续语流的位置敏感注意力location-sensitive attention缓解英文版常见的注意力塌缩声学特征上使用 80 维梅尔谱作为预测目标而不是直接把文字映射到波形。这样改造之后模型学习的不是“汉字→音频”的黑匣子映射而是“音节声调→声学特征”的可解释中间表示。实际训练时你会发现loss 的下降曲线和英文版差别不大但最终合成音色的自然度提升非常明显。2.2 资源包代码结构train.py入口、hparams.py配置、synthesis.py推理先把压缩包解开目录组织是这个样子的以解压后的实际层级为准根目录有 train.py、synthesis.py、hparams.py、setup.pylib 目录下是一个 tacotron 子包tacotron_pytorch 目录里是注意力实现和编码器/解码器核心代码tests 和 notebooks 分别放测试脚本和一个叫 Test Tacotron 的 notebook。首次拿到这个包我的建议是先不要看训练代码先看 hparams.py里面是全局超参数再看 synthesis.py搞清楚推理时怎么把文本转成 mel 谱最后回到 train.py 看数据加载和训练循环。这个顺序能让你在二十多分钟内建立“数据从哪来、模型怎么算、结果怎么出”的完整链条而不是被一堆互调的模块绕晕。setup.py 是用来做包安装的我习惯在 conda 环境里执行 pip install -e . 把它安装成可导入模块具体命令到第 3 章再展开。这里只需要知道库的根目录是 lib/tacotron写代码时 import 路径别写错。tests 目录里的 test_attention.py 和 test_tacotron.py 是单元测试用来验证模块能不能正常前向传播跑训练前先把这两个文件执行一遍能排除不少环境层面的低级问题。2.3 先跑通 Test Tacotron.ipynb用注意力可视化验证模型能对齐notebooks 里的 Test Tacotron.ipynb 是一个独立的验证脚本不是训练脚本。它做的事情是加载一个预训练 checkpoint或者随机初始化的模型输入一段拼音序列逐帧输出注意力权重矩阵。注意力矩阵是所有 TTS 调试里最该看的东西训练收敛的模型注意力矩阵应该是一条接近对角线的带状分布如果注意力发散或者塌缩成一团后面合成出的音频必然乱。加载检查点并打印注意力权重的过程大概是import torch from tacotron_pytorch.tacotron import Tacotron # 加载模型n_symbols 要和你训练时的字典大小一致 model Tacotron(n_symbols120, n_frames80, reduction_factor2) checkpoint torch.load(checkpoint_epoch_50.pth, map_locationcpu) model.load_state_dict(checkpoint[model]) model.eval() # 模拟输入拼音序列转成ID结尾补上结束符 text_ids torch.tensor([ [23, 45, 67, 89, 0], # 数字对应拼音字典索引0是结束符 [21, 43, 65, 87, 0] ]) with torch.no_grad(): mel_out, attn_map model(text_ids) print(attn_map.shape) # (batch, decoder_steps, encoder_steps)这段代码里n_frames 是梅尔谱维度由 hparams.py 里的 num_mels 决定reduction_factor 如果设成 2解码器每一步会输出 2 帧谱注意力图的行数会相应变短。attn_map 是三维张量把第二维和第三维用 matplotlib 画成 heatmap就能看到每个解码步对输入音节的关注分布。我一般会在训练每五千步后手动跑一次这个 notebook把热力图存下来对比比起只看 loss 曲线要直观得多。很多“以为在训练、其实在瞎学”的问题都是在这一步发现的。3. 环境搭建与中文数据集准备从裸机到第一条训练命令3.1 环境版本这是 PyTorch 系别被 README 里的 TensorFlow 说法带偏这里有一个非常容易踩的坑这个包的摘要和部分历史文档里会写明“Python 3.x TensorFlow 2.x”但实际压缩包里的代码主力是 tacotron_pytorch 目录整个实现是基于 PyTorch 的。以代码目录为准训练入口 train.py、模型定义都在 PyTorch 框架下你在装环境时应该装 PyTorch 而不是 TensorFlow。版本选择上PyTorch 1.8 到 2.x 都能跑Python 建议 3.83.10太新的 Python 3.12 在部分依赖的编译环节容易翻车。我通常这样建环境conda create -n tts_chinese python3.9 -y conda activate tts_chinese pip install torch1.13.1cu117 torchaudio0.13.1 --extra-index-url https://download.pytorch.org/whl/cu117 pip install numpy scipy librosa matplotlib pypinyin jupyter pip install -e . # 以可编辑模式安装当前包参数说明torch 和 torchaudio 的版本要配对cu117 是 CUDA 11.7 的预编译版本如果你的 GPU 驱动只支持 CUDA 11.x 以下可以用 cu113 或 cu116 替换librosa 负责音频读取和梅尔谱提取0.9 或 0.10 版本都兼容这个包的接口pypinyin 用于把汉字转成带声调拼音是中文 TTS 预处理的核心依赖后面处理数据时一定会用到。逻辑说明pip install -e . 把库以开发模式装进当前环境之后你在任何目录 import 这个包都会指向当前源码改代码不用重装。注意本资源以 PyTorch 版本为准。README 或摘要里如果出现“TensorFlow 2.x”字样是早期英文版遗留的说明不要据此装 TensorFlow否则后续代码一行都跑不起来。3.2 中文数据集选型AISHELL-3 和 BZNSYP 怎么选数据集决定了你这个模型的声音底子。就这个包的需求来说数据要包含两个要素一是清洗过的音频wav 格式采样率一般 22050Hz二是与音频逐句对应的文本标注。中文开源 TTS 里最常见的两个选择是 AISHELL-3 和 BZNSYP。AISHELL-3 是一个多说话人数据集包含大约 85 小时的中文录音文本带拼音标注BZNSYP 是标贝女声音库约 12 小时单人录音音色一致性高、噪声低。如果你只想快速验证训练流程我建议先用 BZNSYP单说话人、音频干净、预处理脚本好写如果要做多说话人音色控制再切到 AISHELL-3。数据集时长说话人标注适用场景BZNSYP约12小时单女声文本拼音首次跑通训练全流程AISHELL-3约85小时多说话人文本拼音多音色/说话人控制TTS-CORPUS数小时混合文本补充语料/跨域测试选好数据集后把音频统一处理成 22050Hz 单声道 wav文件名按句号映射到文本文件格式类似000001.wav 这是一段测试文本。这个配对文件后面会被 train.py 读入。公开渠道可以直接下载这些数据集注意看授权协议个人学习和研究用途基本没问题商用需要逐条确认。3.3 数据预处理把汉字转成拼音序列并提取梅尔谱训练前必须做两步转换文本层把汉字转成拼音加声调音频层切帧并提取梅尔谱。我一般写一个独立的 preprocess.py 来处理不直接改 train.py。from pypinyin import pinyin, Style import librosa import numpy as np # 汉字转带声调拼音 def text_to_pinyin_ids(text): py pinyin(text, styleStyle.TONE3) # TONE3 输出 ni3 hao3 这种带数字声调 id_list [vocab[item[0]] for item in py] # vocab 是拼音-索引字典 id_list.append(vocab[end]) return np.array(id_list) # 音频转梅尔谱 def audio_to_mel(path, sr22050, n_mels80): wav, _ librosa.load(path, srsr, monoTrue) mel librosa.feature.melspectrogram( ywav, srsr, n_melsn_mels, n_fft1024, hop_length256, win_length1024 ) mel librosa.power_to_db(mel) return mel.T # (frames, n_mels)这里有几个值得注意的参数Style.TONE3 会把“你好”输出成ni3 hao3声调数字跟在后边比用 TONE2 的声调符号ní hǎo更好切分n_fft1024、hop_length256 是 Tacotron 常用的 STFT 配置换算下来一个 1 秒的音频会得到约 86 帧梅尔谱这个帧率直接决定解码步数文本末尾的end结束符必须加否则解码器不知道在哪里停训练时长会因为对齐边界不明确而拉长。标点是另一套逻辑我习惯把逗号和句号也做成单独的符号放进 vocab而不是直接删掉不然模型学不到停顿位置合成长句时会出现一口气憋到底的听感。4. 训练实战与调参hparams.py里的关键参数和训练日志怎么看4.1 第一次训练前的超参数设置hparams.py 是全局超参数配置文件涉及到中文训练最关键的参数主要有这几个num_mels、reduction_factor、batch_size、learning_rate以及注意力相关的维度。我建议第一次跑的时候把注意力维度保持默认只动三个参数batch_size 以显存能塞下为准、但不要超过 32reduction_factor 设 2 或 3合成时解码器每次输出 2 或 3 帧能有效减少解码步数训练更快但过大的 reduction_factor 会让音质发糊learning_rate 用 1e-3配合预热和衰减。给一份推荐的首次训练参数表参数推荐值说明num_mels80梅尔谱维度别低于 80否则高频细节丢失reduction_factor2大于 3 音质糊等于 1 训练慢batch_size1632取决于 GPU 显存AISHELL-3 用 32 可能 OOMlearning_rate1e-3配 Adam注意力发散就降到 5e-4max_iters200k中文语料收敛往往比英文慢4.2 train.py 的训练循环与日志train.py 支持命令行参数覆盖 hparams.py 里的默认值。启动训练我一般这么跑python train.py --data_root./data/bznsyp \ --checkpoint_dir./checkpoints/ \ --batch_size16 \ --reduction_factor2 \ --max_iters200000 \ --log_dir./logs/这个命令里的 data_root 指向预处理好的数据目录目录里要有一个 metadata 文本文件每行是“音频文件名加拼音/文本 ID 序列”这是整个训练的数据入口。checkpoint_dir 用来存断点我习惯每五千步手动把 checkpoints 目录打包一次防止训练中断后没有后悔药。log_dir 是 TensorBoard 日志目录loss 曲线和注意力图都能在里面看。训练过程中每 100 步会打印当前的 step、loss、学习率。真正需要盯的不只是 loss还有一个细节reduction_factor2 时解码器每一步输出 2 帧如果 loss 在 10k 步后仍然在 1.0 以上且不下降基本可以断定注意力没对齐先停掉回第 2.3 节把注意力图打出来看。另外训练日志里如果出现nan优先检查是不是学习率太高或者输入序列里混了空文本这是两个最常见的原因。因为 train.py 在历史版本里有细微改动动手前先 grep 一遍 train.py把 checkpoint 保存和加载的字段名确认好能避免启动时踩到 KeyError。4.3 GPU 训练的中断恢复与 checkpoint 管理训练中段经常遇到断电、显存溢出被杀进程的情况所以断点保存很重要。常见做法是给 train.py 加一个 --resume 参数指定从哪个 checkpoint 恢复。如果你翻源码发现它没实现这个参数可以直接在外层包一层脚本每次启动前检查 checkpoint_dir 里最新的文件if ls checkpoints/*.pth /dev/null 21; then LATEST$(ls -t checkpoints/*.pth | head -1) python train.py --data_root./data/bznsyp \ --checkpoint_dir./checkpoints/ \ --resume$LATEST else python train.py --data_root./data/bznsyp \ --checkpoint_dir./checkpoints/ fi逻辑很简单有 checkpoint 就从最新断点继续没有才从头训练。注意恢复训练时batch_size 和 reduction_factor 必须和保存 checkpoint 时保持一致不然模型结构对不上加载会直接报错。我个人的习惯是每次恢复前先看一眼 checkpoint 文件名里的 step 数确认它比日志里最后打印的 step 新否则你会把模型练回几天前的状态白跑一大段。5. 避坑指南中文TTS训练最常见的五个翻车现场5.1 注意力不收敛loss 不降、合成的语音像卡带现象训练到 20k 步loss 仍在 1.2 上下抖动合出来的音频每个字间隔不均匀像卡带。原因这是中文 TTS 训练最典型的注意力不收敛。中文标注里如果混入了过长句子比如超过 30 个字解码器的注意力窗口容易跨行塌缩另外学习率设置过大也会导致注意力矩阵的梯度震荡。解决两个办法一起用。一是把数据里超过 25 个字的句子过滤掉或截断保持序列长度分布均匀二是把学习率从 1e-3 降到 5e-4并配合预热头两千步用小学习率让注意力稳定。改完这两个再看注意力图一般 15k 步内能看到明显的对角线。5.2 合成音色“电音感”重听起来像机器唱出来的现象模型 loss 已经收敛但合成音频的每个字都带着明显的金属味和数码味。原因这个包默认的声码器大概率是 Griffin-Lim它在相位重建上先天不足相位误差会直接体现在频谱细节上听起来就是“电音感”。这不是模型没训好而是声码器把 mel 谱还原成波形时损失了太多信息。解决先确认输出 mel 谱的幅值归一化是否与训练时一致不少翻车其实是推理时把 DB 值直接当线性谱喂给了 Griffin-Lim。如果归一化没问题再考虑换更先进的声码器比如 WaveGlow 或 HiFi-GAN把训练好的 Tacotron 当作声学模型mel 谱输出到声码器生成波形。这个替换不改变 Tacotron 本身但对最终音质的提升是决定性的。5.3 多音字读错模型把“重庆”读成“时钟的钟”现象训练完测试固定句子“重庆”被读成 zhong4 qing4“音乐”被读成 yin1 le4。原因拼音序列只保留了音节加声调多音字在所有语境里只能对应一个读音训练语料里出现频率高的那个读音会把模型绑死。模型没有上下文语义通道它只是记忆了当前字词组合的统计规律。解决如果语料量足够在词级别建立词典把多音字放到词组里再按词组选读音或者在预处理阶段用词法分析器比如 jieba 分词加多音字表对文本做一轮消歧把“重庆”固定映射到 chong2。一个取巧的办法是给常用多音字加人工纠错表在预处理脚本里直接覆盖测试集里出现的高频词。5.4 长句 OOMbatch_size 调小也救不回来现象训练 20 字以内的句子没问题一旦 batch 里出现一个 40 字的长句显存直接溢出报 CUDA out of memory。原因Tacotron 的显存消耗与序列长度近似平方级关系因为注意力矩阵的尺寸是 decoder_steps × encoder_steps长句子会瞬间把 batch 的显存抬高。你以为 batch_size16 很保守但其中一条长句的显存占用可能顶得上其他样本的总和。解决最稳的方案是动态 batch按句长分组把接近长度的句子放在同一个 batch 里做 padding或者干脆在数据过滤时把超长句单独抽出来训练主数据只用 525 字的样本长句留给推理阶段处理。再有就是把 reduction_factor 从 2 提到 3解码步数减少三分之一显存占用会明显下降。5.5 训练时 loss 出现 nan整个模型权重被污染现象训练到某个 step 后loss 突然变成 nan再训练多少步都拉不回来checkpoint 全部报废。原因绝大多数是浮点溢出。要么是输入音频里有一段静音或爆音mel 谱做 log 之后出现无穷大要么是学习率在衰减过程中被除零。解决先从数据侧排查对每个音频算一次 mel 谱的最大值把 DB 值超过 100 的样本挑出来删除或重新导出再在训练循环里加一个保护loss 是 nan 时跳过当前 batch 并临时把学习率降一半。另外养成习惯训练五千步时手动存一个 checkpoint这样即使 nan 污染了最近的断点也有后悔药可以回退。6. 合成推理与效果验证synthesis.py实操和MOS评测的完整流程6.1 synthesis.py 的用法与参数synthesis.py 是独立的推理脚本输入一段中文文本调用训练好的模型输出 mel 谱再用默认声码器恢复成 wav。基本用法python synthesis.py --checkpointcheckpoints/checkpoint_200000.pth \ --text今天天气不错适合出去走一走。 \ --output./output/test1.wav \ --hparamsreduction_factor2checkpoint 参数指向训练得到的模型断点text 直接传汉字文本脚本内部会先走一遍汉字转拼音的流程所以要确保 synthesis.py 里调用的转换函数和训练预处理用的是同一套output 指定保存路径hparams 用来覆盖配置这里的 reduction_factor 必须和训练时一致否则模型输入输出的帧尺寸对不上推理会直接报错。如果脚本里没有内置汉字转拼音我一般会改造一下把第 3.3 节的 text_to_pinyin_ids() 直接搬进来让分析文本进、拼音 ID 出保持链路一致。合成短文本通常问题不大如果文本很长注意先手动加标点否则模型会莫名憋出停顿感很怪的声音。输出的音频建议用 spectrogram 图快速扫一遍有没有断帧再上耳朵听。6.2 不用只靠耳朵用 MOS 和 A/B 对比做量化筛选音质好不好耳朵听是主观的作为工程判断必须有量化依据。常用的是 MOS请 510 人听同一批合成语音按 15 打分5 分是真人水平。我的评测流程是每组 20 条文本覆盖短句、长句、数字、多音字和标点符号评测者盲听打分最后取平均。评分描述5听不出是合成与真人无异4有轻微合成痕迹但不影响理解3能听懂有明显机械感2部分字词听不清需要看文本辅助1完全无法理解像噪声我自己的标准是平均 3.5 分以上才值得继续优化4 分以上才是可交付的水平。如果同时有两个 checkpoint 拿不准A/B 盲选可以快速筛掉音色更差那版比一个人反复听有效得多。从那以后我每次训练完都不会直接下结论“效果还行”而是强制走一遍固定流程用 20 条测试文本统一合成请至少三个人盲听打分再把分数记到训练日志里。这个习惯救了我很多次有些模型听起来顺耳但评分只有 3.0有些听起来一般评分反而到 4.2耳朵很容易被局部音色骗过去。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?