一行文本清洗如何拖累俄语语音合成
作者发现语音预处理删除了标注俄语重音的组合字符,语料中的 е 与 ё 混用也增加了发音错误。恢复重音、修正 ё 并补充语音指令后,21 个可检查样本的正确数从 14 个提升到 18 个。
完整中文译文
我运营着一个面向语言学习者的阅读网站。每篇故事的每一行都配有音频,由 OpenAI 的 gpt-4o-mini-tts 生成。俄语是最难处理的语言:重音位置不固定,日常书写中又不标重音,而且重音会改变词义(за́мок 是城堡,замо́к 是锁)。一位俄语母语教师告诉我,音频中的重音错误已经频繁到影响使用的程度。
其实,我的文本里已经标出了重音。为了方便学习者,我会在重读元音后面加一个尖音符(U+0301)。但有个“语音生成前清理文本”的函数,在把文本发送给语音模型之前,删掉了所有组合附加符号。除此之外,语料还沿用了俄语日常印刷的习惯,把本该写成 ё 的地方写成了 е。于是,模型只能逐个词猜测重音,而遇到 ё 时,它经常猜错。
我用几种不同的方式为 25 个测试项生成了音频,再用音素级识别器(wav2vec2 xlsr-53 espeak)检查:
线上原有音频:21 个可检查项中,14 个正确。
同一个音色,恢复尖音符和 ё,再在语音指令里加一句话,说明这些符号的含义:21 个中,18 个正确。
Azure 的一个 neural voice,无论是否标注重音:正确项都在 11 到 14 个之间。重音标记几乎没带来变化。
加上标记后,含 ё 的词在 4 次生成中,4 次都读对了。对于真正需要依靠上下文判断的同形异音词(до́ма 和 дома́),即便加了标记,每次生成的正确率仍然只有 50% 左右。
每个词的重音依次来自三个来源:我自己设置的覆盖规则、网站已经展示给学习者的词汇表,以及 RuAccent(一个开源重音模型)。如果这些来源的判断不一致,或者工具拿不准,就让一个 LLM 审核器结合词所在的句子进行判断。这一步总共处理了大约 12,000 个 token,工具给出的标注中,约有 8% 是错的,主要是 окна́ / о́кна 这类词形变化。
模型采用采样生成,所以同样的输入也可能得到不同的结果。每段音频都要通过时长检查;如果音频只有一个词,还要通过音素识别器的重音检查:先看元音音质,难分高下时再看时长。检查不通过的音频,最多重新生成三次。对于怎么都读不对的词,最后一次尝试会在发送给语音模型的文本副本中,把非重读的 о 改写成 а。这就是 akanye,也就是俄语实际发音中的“а 化”现象。这个办法修好了 за́мок 的发音。
有个坑让我整轮试运行都白做了:ffprobe 对通过管道读取的 MP3,会把时长报告为 "N/A",于是我的时长检查把所有音频都判成了不合格。要先解码音频再测量时长,而且在批量处理前,先拿一段音频测试。
最终重新录制了大约 36,000 段音频,API 费用约为 35 美元。在我的检查集上,ё 的发音错误率从约 40% 降到了约 2%,其他重音错误率从 13% 降到了不到 1%。82% 的音频第一次生成就通过了检查。还有 142 个单词在三次尝试后仍未通过,这些会交给人耳检查。
机器仍然无法可靠判定的,是一段只有一个词、时长 3 秒的音频。如果你也在做俄语 TTS,而且有更好的评分器,我很想听听你的办法。
网站:https://languagethroughliterature.com
如需采取进一步措施,可以考虑屏蔽此人和/或举报滥用行为。