这篇继续聊 TTS 架构。前两篇我们把语音合成的整体路线图和主流方案做了梳理,这一篇直奔主题,聊最核心的那条主线——从文本到波形,一个句子输入进来,到最终变成可以播放的语音文件,中间到底经历了什么,每一步又各自解决什么问题。
先说结论:所谓“从文本到波形的一气呵成”,并不是说只有一个黑盒模型把文字直接吐成 wav,而是把传统架构里明显割裂的“文本分析—声学预测—波形生成”三个阶段,逐步收拢到一个目标统一的建模体系里。它要解决的核心问题是误差累积、中间特征不一致,以及多模型协同带来的工程部署成本。这篇我会按真实系统落地的顺序,从文本处理链路、声学建模、声码器合成,再到端到端一体化实战,一步步拆开讲,中间穿插我实际跑实验、调模型时踩过的坑。适合正在做 TTS 项目、或者刚接触语音合成想建立完整认知的开发者阅读。
1. 整体架构设计:为什么要“一气呵成”
1.1 传统级联架构的得与失
早期的 TTS 系统是明确的三段式流水线。第一段文本前端,负责把字符转换成语言学特征,比如分词、注音、韵律标记;第二段声学模型,输入语言学特征,输出声学特征,最典型的是梅尔谱;第三段声码器,把梅尔谱还原成可播放的波形。这种划分的好处是每个环节独立可控,出问题了能精准定位是哪一个模块的问题。
但坏处同样明显。三个模块的目标函数是各管各的:声学模型只保证生成的梅尔谱像自然语音的梅尔谱,声码器只保证从频谱恢复出的波形失真小,没有任何一个模块知道最终听感是什么。更麻烦的是,中间环节的误差会沿着流水线一路放大。文本前端把韵律边界标错了,声学模型生成的谱就会走样;声学模型的某个高频毛刺,可能让神经声码器直接合成出“电音”。我实际项目里最头疼的就是这种“上游小错、下游爆炸”的连锁反应,一个 badcase 查到最后,根因常常在某个数字正则化规则没覆盖到。
1.2 端到端到底指什么
现在市面上的“端到端”说法,其实有好几种程度,别被技术文章的标题党带偏。
最保守的做法是“分段端到端”:文本前端还是独立存在,但声学模型和声码器通过联合训练被打通,梯度可以从波形一路传到频谱生成。更进一步的做法,把文本前端的核心模块也用可微的神经网络整合进来,字直接进模型,音素对齐交由模型内部完成。最彻底的做法,是直接让模型输出波形的采样点,中间连梅尔谱都不要了,真正实现文本到波形的一体化建模。
选择哪种程度,取决于你的数据量、算力和对可解释性的需求。我个人更推崇“渐进式端到端”:数据量不足时保留文本前端做规则兜底,同时把声学模型和声码器合并训练;等数据积累到位了,再逐步把文本前端模型化。一上来就追求最彻底的端到端,碰到问题时可能连故障入口都找不到。
2. 文本处理链路:让机器先“看懂”这句话
很多从模型入手做 TTS 的人会低估文本前端这步,觉得反正模型越来越强,塞进去就行。但真实数据里,文本前端的质量直接决定合成结果的上限。一个文本解析错误,后面的模型再强也救不回来。
2.1 文本正则化:数字符号都在搞事情
正则化说白了,就是把人类书写习惯里的数字、符号、缩写、日期等非标准写法,转成“读出来是什么”的确定文本。比如“2024-01-01”要读成“二零二四年一月一日”,“88.5%”要读成“百分之八十八点五”,“$129”要读成“一百二十九元”。这些规则多到可以单独维护一个知识库。
实际操作中最大的坑是上下文歧义。一个“1”在电话号码里有时读“幺”、有时读“一”,在年月日里读“一”还是“零”取决于前后环境;一个“×”在“3×5”里读“乘”,在“5×8 尺寸”里又有别的读法。我建议正则化采用“规则分层 + 异常兜底 + 人工复查”的方式:先做通用规则,再叠一层领域词典,最后抽检未覆盖样例回填规则。
文本正则化还要注意处理顺序。比如“3.14”如果先做数字归一化,可能会把句点吞掉;“第3条”要整体识别为序数后再发音。我踩过最大的坑是日期与小数规则冲突:像“2024.3.14”这种带小数的日期写法,分别走了小数和日期两套规则,最后读成了“二零二四三一四”。排查了很久,最终统一了识别优先级才解决。
2.2 多音字消歧:中文特有的难题
中文文本前端最有挑战的部分就是多音字。光是“行”就有 háng、xíng 两种读法,“地”有 de、dì,“得”有 de、dé、děi。读错一个字,合成句子立刻就出戏,而且比音色瑕疵还明显。
传统解决方案是维护一个大规模多音字词典,配合上下文规则:前后字、词性、领域。比如“银行”里的“行”读 háng,“行走”里的“行”读 xíng,这种可以用词典覆盖。但遇到“还”这种活跃词,“还书”读 huán,“还有”读 hái,规则很容易打架。近两年的主流做法是把多音字消歧建模成序列标注任务,用上下文语言模型直接做预测,准确率可以做到很高。
工程上的经验是:先上规则,再上模型,但永远保留一条“词典覆盖优先”的强制路径。因为模型再准也是概率输出,对于人名、地名、企业名这种低频高成本场景,词典的确定性是模型替代不了的。
2.3 从字到音素:一串理想的发音符号
文本归一和消歧之后,需要把汉字转成模型更好学的音素序列。常见的选择有两类:一类是直接采用拼音带声调,比如“简体中文”转成 jian3 ti3 zhong1 wen2;另一类是进一步拆成更细粒度的音素符号。拼音的好处是可读性好、和中文声韵结构自然对应,调试起来直观;细粒度符号的好处是做多语言和代码切换,也就是中英混说时更通用。
做跨语言 TTS 的同学通常会采用“共享音素 + 语言专属音素”的形式。比如中英混合句子“你好,I love you”,中文音素和英文音素共用一套符号系统映射,模型不用区分输入语言。这个设计在数据量普惠的声学模型时代尤其实用。
实际项目里,还要注意轻声音节、儿化音、停顿符号的处理。“妈妈”的第二个“妈”是轻声,和单字字调完全不同;“这儿”容易读成 zhè er 而不是 zhèr。这些细节需要在字音转换阶段专门处理,否则模型学习阶段会收到不一致的输入,练出来的模型说话总有一股“照本宣科”的僵硬感。
3. 声学建模:把音素序列变成声音的轮廓
文本处理完,接下来是重头戏。声学模型的目标很简单:给定音素序列以及可能的韵律标签,生成一个可以交给声码器的声学表征。但就是这么个目标,演化出一大片研究方向。
3.1 声学特征选什么
先要决定“输出什么”。最常见的表征是梅尔谱,把线性频率按人耳感知特性压到梅尔刻度,通常取 80 到 128 个频带,帧移 10ms 至 12.5ms。梅尔谱的好处是维数低、训练稳定,缺点是一次生成的不是最终听感,还要靠声码器补相位。
也有人直接生成线性谱,信息更全但维数高、训练更难;还有人额外预测基频 F0、能量等副特征,帮助模型表达音调起伏。我的经验是:普通说话场景用 80 到 100 维梅尔谱足够;如果合成内容偏歌唱或情感变化丰富,就必须加显式 F0 控制,否则音高曲线会非常“平”。
特征参数设定上,帧长、帧移、谱维数需要和声码器匹配。我在项目里同时用两个声码器做对照,一个要求 100Hz 帧率,一个要求 80Hz,前端声学模型的输出层维度、上采样倍数就完全不同。这种兼容性问题要在选定声码器之后第一时间锁死,不然返工成本很高。
3.2 自回归和非自回归:各让一步
声学模型的建模方式,大体分两派。自回归派按时间顺序逐帧生成梅尔谱,每一帧依赖前面已经生成的帧。代表思想如经典的 attention 生成机制,质量上限高、语气连贯,但速度慢,而且有逐帧错位的风险——一步跑偏,后面全歪。非自回归派直接并行生成整段谱,代表思想如基于时长预测的并行生成方案,它通过显式时长预测先把每个音素的帧数算出来,然后并行展开,速度提升以数量级计。
选哪派要看场景。离线合成、音质优先、不着急出结果的场景,自回归仍然是效果天花板比较高的选择;实时合成、批量合成、对延迟敏感的场景,非自回归明显更合适。
我自己的一个深刻感受是:非自回归模型的短板不在谱生成本身,而在于时长预测。因为模型假设每个音素的时长是独立确定的,但真实语音里,上下文、语义、语速都会影响一个字的实际时长。解决思路是引入随机时长预测器,允许时长从分布中采样,而不是输出唯一值,这样合成语音的节奏会自然很多,这也是当前很多端到端系统采用的设计。
3.3 对齐:最难啃的骨头
不管是自回归还是非自回归,模型都要知道“哪个音素对应哪些帧”,这个对应关系叫对齐。自回归可以靠注意力机制软对齐隐式学会,但非自回归需要显式监督信号。
主流做法是先跑一个外部强制对齐工具,用预训练的语音识别模型把文本音素和时间戳对应起来,再换算成每音素帧数,作为时长预测器的训练目标。强制对齐的效果直接决定非自回归模型的上限,如果对齐数据本身错位,时长预测器学到的就是带噪声的映射,最终合成语音会出现严重的节奏问题。
我在一个中文数据集上折腾了很久,发现强制对齐对静音片段的处理很关键。句首、句尾、逗号位置的静音该不该建模?不建模的话,合成句子会“抢读”;建模的话,时长预测器又要额外学会预测停顿时长,对短句很容易过拟合到“全部停顿 0.1 秒”的死胡同。后来我在数据预处理里对静音做了显式标记,并把静音帧数限制到一个合理区间,训练才稳定下来。
4. 声码器合成:从频谱到可听波形的最后一跃
声学模型把“声音的轮廓”画完了,最后一步是声码器,负责把梅尔谱或线性谱还原成波形。这一步看似简单,实际上是听感质量的分水岭。
4.1 传统声码器到神经声码器
早年最常用的声码器是 Griffin-Lim 算法。它根据幅度谱,通过反复迭代估计相位,能从梅尔谱恢复时域波形。原理不复杂,速度也快,但音质天花板很低——说白了是“强行补相位”,出来的声音有明显的金属感和“嗡嗡”的箱音,只能用来快速验证声学模型,不适合面向用户。
神经声码器的出现把音质拉高了一个档次。早期的神经声码器用自回归逐采样点生成,音质真实但生成速度慢到没法实时。后面出现了各种基于流、基于生成对抗网络的声码器,其中生成对抗类声码器在生成质量和推理速度上取得了很好的平衡。典型实现只有几十兆参数,配合判别器对抗训练,单个 GPU 上可以轻松跑到几十上百倍实时率,听感也接近原始录音。
4.2 神经声码器怎么选
选择声码器要权衡三点:音质、速度、稳定性。音质方面,生成对抗类声码器在今天的主流场景里已经够用;速度方面,要看能否满足流式或批量合成需求;稳定性则要看训练是否容易崩、推理时是否容易出爆音。
实际落地中,声码器与声学模型的关系有两种。一种是最常见的“预训练声码器 + 冻结使用”:声码器在大量通用音频上预训练,声学模型产出梅尔谱后直接喂给它。优点是声学模型和声码器解耦,迭代互不干扰;另一种是“声码器与声学模型联合微调”,把声码器的梯度回传到声学模型,让声学模型学着输出这个声码器最喜欢的谱,通常能提升几个点的听感分数,但对训练稳定性要求更高。我在小数据量实验里联合微调时常遇到生成谱发怪声的问题,后来只在数据量足够时才启用联合微调。
5. 端到端一体化实战:直接让模型吐波形
前面拆完三个环节,现在讲最核心的主题:怎么把它们收拢成一个真正的端到端系统。这里我不贴具体代码,因为不同框架差异很大,更重要的是把思路讲透。
5.1 一体化模型的核心设计
近年来很多端到端方案走的是“变分 + 对抗 + 随机时长”的路线。流程大致是:文本经过编码器变成隐向量序列;隐向量和来自参考语音的真实后验特征一起注入变分后验编码器,得到一个带噪声的隐变量;时长预测器为每个音素采样一个时长,把隐变量扩展成帧级序列;最后解码器直接把扩展后的隐变量转成波形。训练时全程用一个判别器对波形做真假判别,反向支撑解码器部分学得更真实。
这套设计里最精妙的一点是:它把“声学特征生成”和“波形生成”合并进了同一个后验编码器与解码器框架中,不再单独生成中间梅尔谱,而是让波形直接从潜在表征里生成。这样做的好处是,所有参数在同一个损失函数下一齐更新,误差不再被中间表示切断,从文本到波形的路径变成一次完整的前向传播。
5.2 损失函数怎么搭
一体化模型通常不只有一个损失。大致分四块:
- 重建损失:生成的波形和真实波形在频域上的差异,常用梅尔谱重建损失;
- KL 损失:让隐变量分布贴近先验分布,保证推理时即使没有参考语音也能采样;
- 时长损失:让随机时长预测器预测的分布贴近对齐得到的实际时长;
- 对抗损失:让判别器分不清生成波形和真实波形,以及对应的特征匹配损失。
是不是四个都得上?我的答案是:缺一个都会出毛病。去掉对抗损失,声音变“糊”;去掉时长损失,语速乱跳;去掉 KL 损失,训练初期容易不收敛;去掉重建损失,最后听感跟目标声音差太远。我在项目里一般先从“重建 + 时长 + KL”三件套做起,模型能正常学到文本到波形的对应关系后,再加入对抗损失做音质微调,训练曲线会稳很多。
5.3 推理与训练的差异
一体化模型的训练和解码行为区别很大。训练时后验编码器能看到参考语音的特征,模型学习的是“从文本加真实语音条件”重建波形;推理时没有参考语音,只能靠先验采样。这中间的 gap 是很多复现失败的原因。
我的解法是:每次训练产生新的 checkpoint,验证时强制走一遍纯推理模式,完全不用参考语音。不要只看训练损失下降就开心,很可能训练损失很好但推理效果一塌糊涂。另外推理时长预测部分要设置温度参数,温度太高语速会抽搐,太低读起来又机械,我经常把温度控制在 0.6 到 0.8 之间。
5.4 踩过的坑和针对性手段
端到端模型看起来美好,但调试起来确实“无处下手”。我把踩过的几个典型坑列一下:
- 波形爆音:大概率是解码器上采样得到的波形幅值过大,需要在输出层加裁剪或平滑处理,同时检查数据是否做过峰值归一化。
- 长句崩盘:上下文信息在长句子里丢失,造成后半段莫名杂音或低音。建议先切句,超过一定长度的句子按标点切成短句,分段合成再拼接,拼接处做短时间窗的淡入淡出融合。
- 发音和文本不一致:这个多半是文本前端或音素映射问题,别急着怀疑模型。
- 相同文本多次推理结果差异大:因为引入了随机时长和采样,这是正常现象,只要每次听感都在可接受范围内即可;如果方差大到音色都变了,需要检查后验编码器和先验分布是否偏差太大。
6. 常见问题排查与调试思路
端到端链路一长,问题往往不是单一模块坏了,而是多个因素叠加。我习惯把问题拆成三类:文本解析类、时长对齐类、波形生成类。
6.1 文本解析类问题
表现:数字读错、多音字读错、英文单词发音不对。
排查路径:先把文本前端单独跑一遍,把分词、注音、音素序列直接打印出来看。大部分情况到这里就能定位。注意别只测几个测试样例,建议准备一个覆盖数字、日期、时间、货币、单位、符号、多音字、人名地名的回归测试集,每改一次规则全量跑一遍。
6.2 时长对齐类问题
表现:语速过快、停顿怪异、某些字被“吞掉”。
排查路径:看时长预测分布,把每个音素的时长可视化出来。吞字多半是预测时长落在零附近,这时候检查对齐标注是不是对短音素不友好。停顿怪异则是静音建模的问题,可以像前面说的给静音单独加标签处理。
6.3 波形生成类问题
表现:杂音、电音、爆音、金属音。
排查路径:先用真实音频喂声码器。如果声码器把真实梅尔谱还原成波形时没杂音,说明问题出在声学模型生成的谱上;如果真实谱进来都有杂音,声码器本身需要重训或换权重。这一步能快速隔离故障面,百试百灵。
6.4 训练不稳定的通用对策
端到端一体化模型训练不稳定几乎是常态。通用对策依次是:降低学习率、缩小 batch size、增大梯度裁剪阈值、调整损失权重。对抗损失尤其敏感,如果判别器损失掉到接近零,说明判别器碾压了生成器,这时候减小判别器学习率,或者给生成器的对抗损失加权。另外,检查数据里是否有低音量或峰值截断的样本,数据问题远比模型问题常见。
下面把典型问题汇总成速查表,方便大家对照排查:
| 现象 | 可能原因 | 排查顺序 |
|---|---|---|
| 数字、多音字读错 | 文本前端规则缺失或冲突 | 先看分词注音输出,再查词典覆盖 |
| 语速忽快忽慢 | 时长预测不准、温度参数过高 | 可视化时长分布,调整采样温度 |
| 有杂音、金属音 | 声学谱异常或声码器失配 | 真实谱喂声码器,隔离故障面 |
| 句子后半段崩坏 | 上下文丢失、序列过长 | 按标点切分,分段合成再拼接 |
| 某些字被吞掉 | 对齐标注或时长预测偏向零 | 检查静音建模,修正对齐数据 |
7. 实操经验沉淀
这一篇的内容比较密,最后把我个人做 TTS 项目沉淀的一些经验做个梳理,不算总结,就是一些能在下一个项目里直接用起来的东西。
7.1 先把非端到端基线跑通
别把端到端当成银弹。端到端模型收敛快、效果好,但对数据质量和数据量的要求都更高。我的习惯是先跑通非端到端基线,把文本处理、对齐的坑全部踩一遍,再上端到端。基线模型本身就是最好的 debug 工具,遇到问题可以随时回到基线验证是“新架构的问题”还是“老问题在新架构里的表现”。
7.2 数据质量投入远比想象中重要
这是老生常谈,但 TTS 数据环节特别容易踩坑,比如录音文本和实际发音不一致、有混响、有噪声、音量不统一。文本对齐、音质筛选这些离线环节值得投入主要精力,模型选型只是后面一部分的事。数据决定上限,模型只是逼近这个上限。
7.3 中间特征可视化是 debug 的抓手
做端到端模型不代表不用看中间结果。把注意力矩阵、时长分布、频谱图都存下来,偶尔翻一翻。很多疑难杂症,看一眼特征图就知道病根在哪。比如时长分布出现大面积尖峰,基本可以断定对齐信号有问题;频谱图出现横向条纹,多半是上采样层导致的伪影。
7.4 评测要比 loss 曲线更贴近真实
离线测试集是 oracle,真实场景里的长文本、口头语、网络用语才是检验标准。我在项目里固定每周跑一轮盲听评测,找不同人来打分,比只看 loss 曲线可靠得多。主观评测的重点不是“打几分”,而是记录每个样本的具体问题,归类后回填到数据清洗和文本前端规则里。
7.5 一个排查链路问题的万能思路
如果合成结果明显不对,我一般按照“文本前端 → 声学特征 → 声码器”的顺序逐步 dump 中间产物:先听文本前端单独转出的音素对不对,再看声学模型生成的频谱轮廓是否自然,最后才怀疑声码器。每一步都有独立判断依据,问题很快就能锁定在某个环节。这个习惯帮我省下了大量摸黑调试的时间,强烈建议一试。
这篇文章从文本到波形,把 TTS 架构拆到了实际操作层。如果你正卡在某个具体环节,可以对照问题排查部分逐条试,大概率能少走一些弯路。后面我计划再单独写一篇训练数据清洗和主观评测体系搭建的内容,到时候见。