最近读了一下 Revisiting Over-Smoothness in Text to Speech ,感觉还是有所启发。同时随手记录一些最近思考得 tts/vc 中表现力相关的问题点,无论对错,留个痕迹未来再回顾。
NAR (non autoregressive) 的缺点
现在来看,NAR 几乎是 tts/vc 模型的标配了。今年在 icassp 上读过了好几篇论文的贡献点都在做 NAR 模型。 虽然 NAR 模型不一定更好,但更快是一定的。更少的计算量和天然可并行的架构,在工程性能上具备相当客观的优势。 一个例子是 vocoder,从 wavernn 到 melgan/hifigan。一夜之间,后者几乎就成为了声码器的标配。NAR + GAN 的结构, 效果好且快,而且常见的mode collapse 和over smooth 的问题却并没有发生。
NAR 和 AR 最大的区别是后者用前一时刻/上一像素点的输出结果作为输入,来完成生成任务。 对于绝大多数生成任务来讲,都是一对多的映射关系。
举个例子,有一个等长音频的映射 x -> y。 对于训练数据1,有 x[0:5] -> y3,对于训练数据2,有 x[1:6] -> y4。这两条训练数据中,输入是同一条数据,但有不同的标签。 训练出来的模型,推理可能会存在 x -> y/y` 两种情况,y=[… y3, y4 …] / [… y’3, y’4] 都是符合预期的结果。 但是,由于训练不稳定或其他原因,可能生成 [… y3, y’4 …] 这样的 badcase, 或者生成 [… (y3 + y’3) / 2, y4 …] 导致效果的下降。 后者就是典型的 over-smooth 问题(blurry)。
类似的情况,也出现在 asr 领域。例如 rnnt Vs wenet,后者为了解决类似的问题,引入了 ctc loss后, 使用 attention-based 来进行二次打分和重排,都是为了尽量降低没有之前文本信息带来效果的损失。
过度平滑(OverSmooth)
过度平滑是语音合成中经常出现的问题。具体现象是生成音频风格表现力变弱,听感会更平淡, 从而会丧失一些发音人的特点,更严重的发音会糊掉。特别是在 emotional tts 这个细分领域,可能出现的概率会更高。
过度平滑出现的原因是什么?对于大多数 tts 系统,训练的核心损失函数都是用 MSE/MAE 来最小化重建损失(reconstruction loss), 期望获得自然度和音色相似度的最优。即对于训练数据 (x, y) (x-文本,y-音频),训练模型 y = f(x),最小化 MSE(y’, y)
这样做看似很自然,却存在一个很大的缺陷: 对文本数据而言,音频数据其实是更为稠密的数据表现形式。 相同的文本信息,可能会存在不同的音频,这些音频对应的波形 y可能数据分布差异很大。同时, 文本信息只是音频 content 的高度抽象的表达,除此之外,还有音色/duration/prosody等其他因素会影响整个音频。
因此如果观察数据分布,往往会存在同一个 x 对应多个 y 的情况(one-to-many)。 即使我们可以通过小心的筛选数据保证 short-utterance 的重复不存在, 但 text 里大量反复出现的音素标签使得 phoneme-level 层面依然难以避免。
假如对于某个 x,这里以 ang1 为例(汉语中张的韵母),会存在多个音频波形 y, 如果画出这些 y 的分布,假如满足正态分布。使用 mse 方法训练,大概率 x 会对应到这些 y 分布的平均值上。 由此便产生了过度平滑的 case。
对于传统的 merlin 方法,首先训练 duration model,然后将每个音素对应到帧上面。text enc 的感受野过窄, 导致过度平滑出现非常频繁。但是对于 Tacotron-based 方法,存在 encoder 将 x 转成 enc-embed。 encoder(cbhg/transformer) 一般是接收整个文本作为输入,通过全局 attention 和 双向 rnn, 这样即使对应相同音素的相邻帧,也会有不同的特征表示。再加上自回归的decoder,可能会更大程度上规避这样的问题。 但是,相似的文本依然有可能得到相似的 embed,问题并没有彻底解决。
之前提到的 Revisiting 论文,就是重点在分析和解决这个问题。整体而言两个思路: 使用更多 variance data 以及模型层面的改进。扩展数据是最有效的手段,通过增加 x 的维度,训练由 x -> y 变成了 x|(x_pre, x, x_post) -> y ,从而使得 one-to-many 中的 one 有不同的表征。例如, tacotron 和 merlin 相比,一个重大的进步就是,使用 nnet-encoder 提取 embedding,这个过程中, 每帧 embed 不仅和当前音素有关,还和之前/之后的音素有关,某种程度上变相的使用了更多信息。 一些更为先进的方案中,采用了 dur/pitch/energy 的特征,本质上都是 “细化”了 x, 从而达到类似的效果。 除此之外,还有一些模型层面的优化方案,例如改进损失函数和结构。前面我们提到过的自回归方案, 就是一个不错的选项。另外,最近也出现了 Flow/VAE/GAN 之类的方案,也收到了还不错的效果。
题外话: 我最开始接触 tts 这个领域是客服场景,开始拿到的数据中存在大量这种一对多的情况, 当时会发现相同模型在不同数据上效果差别很大。具体表现是bzn 训练效果和稳定性都非常好,但切换到自有数据上效果会下降。 下降的表现点是发错音的频率增加以及清晰度下降(通俗讲就是糊了)。 现在复盘来看,这两个gap 可能都和原始数据中存在过多 one-to-many 有关。
VAE
vae(variable encoder) 是目前生成问题中大火的模型。看了一些文章和介绍,粗浅的理解一下。
vae 的核心是认为 encoder 由很多个隐变量组成,每个隐变量分布虽然未知,但总是可以用多个高斯分布的和近似模拟。
例如从 数字 label 生成对应图片(minst经典识别的反问题),除了 content 以外, 图片中还包括了 style/position/灰度等其他的信息。这个问题就特别适用于使用 vae 建模,可以通过隐向量来获得那些 label 以外的信息。
和 flow 模型相比,两者的本质其实都是过enc+dec,但区别有二:vae 的隐层向量往往维度较低,以求获得更为”纯净”的表达。但是 flow 一般在固定维度向量的空间下完成。 很多时候,vae 比较诟病的一点就是隐层向量的设计会比较 tricky,当训练数据/训练参数发生变化后,往往不能很好的迁移。 第二点是 flow 模型需要 forward 操作是可逆的,这样才能实现推理,这可能限制了 flow 模型本身的表现力。
另外,有时 vae 的可解释性可能也并不理想,往往不能显式控制,例如 tts 中要给一句风格相似的音频(reference-audio),而不是参数化控制。
GAN
GAN 也是目前比较主流的生成方案,也是我自己在声码器/vc中实际使用比较多的方法。原理上将,使用 GAN 可以拟合任意分布,生成的清晰度一般比较好, 但也要承担 collapse 的风险。例如,GAN-based vocoder 已经成为了目前的主流声码器。
mode collapse
Mode collapse 问题:如果 x->y,是一个 one-to-many 问题,GAN 模型会随机的收敛到其中一个值上 (不是平均值),虽然会比过度平滑的情况好一点,但依然会丢掉很多其他可能的情况。
然而比较幸运的是,对于声码器来说,x(mel)本身也是稠密的,几乎是一个 one-to-one 问题, 这和图片生成/tts生成有很大的区别。因此 Mode collapse 现象非常不严重。 信号值虽然更稠密,但mel 几乎包括了所有但信息(丢掉了相位信息,但似乎影响很小)。
vc 的情况很类似,也可近似堪称一个 one-to-one 问题。
小结: 这里面,vae 和 flow 方法,因为没怎么太花精力来实现,可能理解得比较浅薄。后面会实现一版 vae-based vc,到时新的经验教训再来这篇文章后追加补充。