TTS 前端文本处理:多音字、数字、英文缩写怎么读对
合成出来的语音听着别扭,第一反应往往是”模型不行”。但相当一部分问题其实出在文本进模型之前的那一步——前端文本处理。
字读错了、数字读法不对、该停的地方没停,这些都不是声学模型或声码器的锅。它们拿到什么标注就发什么音,前端送进去的是错的,后面再好也救不回来。
前端在流水线的哪一段
TTS 通常分成三段:文本 → 前端 → 声学模型 → 声码器 → 波形。整条链路的分工见 TTS 的三段流水线。
前端负责的是文本分析:文本规范化、分词、注音(中文这里就是多音字消歧)、韵律预测。它的输出不是音频,而是一串带发音与韵律标注的序列。
这一段有个特点:它的错误是确定性的。同一句话送进去,每次都读错同一个字,不会时对时错。这反而是好消息——确定性的错误可以定位、可以修,而且往往一条注音就解决。
TN 与 ITN:一对方向相反的镜像
前端第一步叫 TN(Text Normalization,文本规范化),方向是书面 → 口语:
- 「2025 年」→「二零二五年」
- 「3.14」→「三点一四」
- 「30%」→「百分之三十」
语音识别的后处理里有个方向完全相反的兄弟叫 ITN,把「二零二五年」还原成「2025 年」,服务的是要读文字的人。详见 ITN 逆文本规范化。
| TN | ITN | |
|---|---|---|
| 用在哪 | TTS 前端 | ASR 后端 |
| 方向 | 书面 → 口语 | 口语 → 书面 |
| 服务谁 | 要发音的声学模型 | 要阅读的人和程序 |
**记法很简单:机器要念出来,就得先把字变成它会念的样子。**两者共享同一批歧义,只是从两侧各踩一次坑。
数字:同一串字符没有唯一读法
这是前端最典型的难点。同样是 2025:
- 年份读「二零二五」
- 数量读「两千零二十五」
- 编号(房间号、末位号)可能逐位读
三种读法都合法,选哪种完全取决于上下文。类似的还有一批:
| 写法 | 可能读法 A | 可能读法 B |
|---|---|---|
1-2 | 一到二(区间) | 一杠二(编号分隔) |
3/4 | 四分之三 | 三月四日 / 三比四 |
2:1 | 二比一 | 两点零一分 |
007 | 零零七 | 七(去掉前导零) |
规则无法脱离上下文——这句话是最需要记住的一句。你可以写一条”四位数字优先按年份读”的规则,它立刻会在价格、编号上出错。前端能做的是覆盖高频模式,剩下的靠人工干预。
多音字:只能靠上下文消歧
中文多音字没有捷径,必须看词、看句:
- 行:银行 / 行走
- 重:重复 / 重量
- 了:了解 / 算了
- 长:长度 / 长大
- 和:和平 / 和面
工业实现有两条路径:
词典 + 规则。查词表,词表里没有就按规则兜底。优点是可控——某个专名读错了,往词典里加一条就永久修好,不用重训任何东西。缺点是覆盖靠人力堆。
神经网络 G2P(字形到音素)。把注音当成序列标注任务来学,覆盖面广,见过的上下文变体多。代价是会出意外错误:它可能在一个从没出问题的常见词上突然读错,而且不好预判下次在哪犯病。
实际系统常是二者结合:模型兜底覆盖长尾,同时保留一个人工干预入口——词典优先级高于模型,让人能一票否决。这个入口是刚需,不是可选项。人名、地名、品牌名这类专名,是最典型的需要手工加词典的场景。
英文缩写与中英混排
英文缩写在中文里的读法没有通法:
API逐字母读(A-P-I)NASA当一个单词读iOS有约定俗成的固定读法
同样是三四个大写字母,读法完全不同,靠的只能是词典。没有词典的缩写,系统只能猜,猜错概率不低。
中英混排还有第二层问题:语言切换处的音色与韵律连贯。「用 Python 写个脚本」这句话里,中文和英文如果由不同的发音处理路径生成,切换点可能出现音色跳变、语速突变,听起来像是两个人在接力说话。
韵律:断句错了比读错字更伤
前端还要预测停顿位置、重音、语调。这部分决定的是”听不听得懂”,而不只是”读得对不对”。
经典例子是「下雨天留客天留我不留」。断句方式不同,意思完全相反。合成语音里也一样:每个字都读对了,但停顿位置不对,听者需要在脑子里重新断一遍句才能理解。
停顿之外还有重音和语调。全句一个调子、没有重音起伏,就是常说的”机器味”的主要来源之一。这部分展开见 为什么合成音听起来像机器和 语速与停顿怎么调。
排查表:读错了先看哪里
听到一句不对劲的合成音,按下表定位:
| 读错类型 | 典型表现 | 成因 | 怎么修 |
|---|---|---|---|
| 多音字 | 「银行」读成「银航」 | 词典没覆盖,或 G2P 判错上下文 | 给出注音标注强制指定读音;专名往词典里加条目 |
| 数字读法 | 「2025 年」读成「两千零二十五年」 | TN 未识别出年份语境 | 把数字直接写成中文(「二零二五年」),绕开 TN |
| 数字边界 | 「1-2 号」读成「一杠二号」 | 连字符语义判错 | 改写成「1 到 2 号」或直接写中文 |
| 英文缩写 | API 被当单词硬拼 | 缩写不在词典里 | 加词典;或在文本里写成「A P I」拉开间隔 |
| 专名 | 人名、地名、品牌名读错 | 未登录词,被切词切碎 | 加入词典 + 注音;保持专名不被拆开 |
| 断句 | 该停不停、不该停乱停 | 韵律模型判错短语边界 | 插入停顿标记;把长句拆成短句 |
| 中英切换 | 切换处音色跳变 | 混排处理不连贯 | 减少混排;把能译的英文译成中文 |
一条经验:先判断错的是”内容”还是”形式”。字词全对、只有停顿和语调难听,那是韵律;某个字发音就是错的,那是注音。两者的修法完全不同,别混着试。
三个最实用的修法
一、注音标注。给出注音来强制指定读音,是修复读错最直接的手段。它不依赖模型改进,立刻生效,且只影响这一处。
**二、数字直接写成中文。**这是最实用的一招。与其调 TN 规则赌它判对,不如直接在文本里写「二零二五年」。你比系统更清楚这个数字该怎么读——把这个知识直接写进文本,比让系统去猜可靠得多。金额、编号、电话号码同理。
**三、插停顿标记。**很多系统支持 SSML 一类的标记语言来插入停顿、控制语速,但是否支持、支持到什么程度要看你用的具体实现,这个不能想当然,得查所用系统的文档。若不支持标记,退而求其次:把长句在该停的地方切成短句,也能起到类似效果。
这三招的共同点是把不确定性从系统手里拿回到自己手里。前端的很多问题本质是”系统不知道你想要什么”,直接告诉它就行。
常见问题
问:前端是不是可以省掉,让大模型式 TTS 直接吃原始文本?
有些做法确实弱化了显式前端。但只要输入里还有阿拉伯数字和英文缩写,“这串字符怎么读”的歧义就依然存在,只是从显式规则挪进了模型内部。挪进去的代价是不可控——读错了没有一条规则可以改。所以在需要确定性的场景(金额播报、编号确认),显式前端处理仍然有价值。
问:怎么判断问题出在前端还是声学模型?
看错误是否稳定复现。同一句话每次都读错同一个字,基本是前端;音质发闷、有金属味、忽好忽坏,更可能在后两段。另一个办法:把出错的词单独拿出来合成,如果单独合成是对的,那问题多半在分词或上下文判断。
问:多音字词典要维护到多大?
不追求大而全,追求盖住你的业务高频词。做法是收集实际会合成的文本,跑一遍,把读错的挑出来入库。业务文本的词汇分布通常很集中,几百条专名词典能解决绝大部分抱怨。
问:流式合成会不会影响前端的判断?
会。流式场景下前端可能拿不到完整句子就要开始处理,上下文变少,消歧难度上升。这也是首包延迟与质量之间的一处取舍,详见 流式 TTS 与首包延迟。
相关阅读
- ITN 逆文本规范化:把「二零二五年」变成「2025 年」
- 流式 TTS 与首包延迟:为什么它比总耗时更重要
- TTS 的三段流水线:前端、声学模型与声码器
- 语速与停顿怎么调:合成语音的精细控制
- 为什么合成音听起来像机器:五个常见原因
本文讲的是语音合成前端处理的通用原理,不绑定某一个具体实现。 各家系统的文本规范化覆盖范围、注音标注语法、是否支持 SSML 差异很大,请以你所用系统的官方文档为准。 我们没有对文中提到的任何方案做过实测,因此不给准确率、速度一类的数字。