一个单词,qara,意思是“黑色”。我把它喂给现成的语音合成模型,按下播放,听到的却是一个柔和、礼貌、非常土耳其语的kara。在克里米亚鞑靼语里,къ和к是两个不同的音,而къ就藏在我们每天说的词里:yoq、qız、vaqıt、qadar。读错它不是一个口音问题,是模型悄悄说着邻国的语言,还指望没人发现。
我发现了。就为这一个音节,我花了接下来几个月,用一块消费级显卡,在晚上下班后,从零数据集开始给克里米亚鞑靼语做语音模型。下面是我踩坑之后留下的工程笔记,写给准备为自己低资源语言做同样事情的独立开发者。大部分教训,每条花了我一周。
鸡和蛋都濒危
做语音数据集的标准流程是拿音频跑自动语音识别,得到文本。但克里米亚鞑靼语没有可用的自动语音识别,而没有语料库又做不出好的自动语音识别。典型的鸡生蛋问题,只不过这只鸡也濒危了。
我盯着这个死循环看了很久,才意识到自己把问题拿反了。有声书自带文本。文本是印好的,它就在那里,根本不需要识别。于是我停止尝试转写,转而尝试对齐:已知文本,已知音频,找出每个词落在哪里,按边界切分。用强制对齐代替识别。这个翻转是整个项目能存在的根本原因,因为它绕过了这门语言恰恰没有的东西。
如果你只从这篇文章带走一点,就带这一点:在训练识别器之前,先看看标准答案是不是已经躺在架子上。如果是,你面对的就是一个对齐问题,而对齐要便宜得多。
骗了我三个半小时的那次运行
第一次大规模对齐跑了七个半小时的音频。它跑完了,没有报错,产出了干净、自信、格式良好的输出。前两个小时是对的,剩下三个半小时全是垃圾。还不是那种一眼就能看穿的垃圾:时间戳合理,分段合理,看起来和好的部分一模一样。七个小时里我得到不到两小时可用材料,而且我是靠亲耳去听才发现的。
我现在把崩溃当成礼物。崩溃会指向出错的那一行。一个返回自信废话的进程,会慢慢耗掉你一周的命。修复它的不是代码改动,而是粒度改动。我没有把材料喂给对齐器三大块,而是拆成四十章,每一章独立定位。同样的音频,同样的文本,同样的工具,结果从不到两小时干净语音变成超过五小时。
这就是我保留的规则:如果一个阶段可能静默失败,绝不在大单元上跑它。小单元会显眼地失败,让你丢掉坏的那部分,而不是整批。
词对不齐,字母对得齐
章节匹配这一步也有自己的陷阱。在一本书里定位一个章节,听起来像字符串搜索问题,但实际做起来完全不是那么回事。音频里的朗读和纸面文本之间,隔着标点、停顿、重复、口误,还有朗读者偶尔跳过的段落。直接拿整段文本去匹配,错位会像雪崩一样越滚越大。
真正管用的是把对齐单位从词降到字母。不是拿整词去对,而是把文本和音频都切到更细的粒度,让匹配在字母层面发生。这样即使某个词被读错或者跳过,周围的字母仍然能锚定位置。对齐的容错率一下子高了很多,坏段也更容易被单独揪出来扔掉。
这个项目最后能跑通,靠的不是什么高级模型,而是三个很朴素的判断:先找现成的对齐材料,别硬做识别;把大任务拆小,让错误显形;把对齐粒度降到字母,让错位不再传染。低资源语言的语音技术,缺的往往不是算力,是愿意在晚上一块显卡前慢慢磨的耐心。
热门跟贴