VITS 模型详解与公式推导:基于条件变分自编码器和对抗学习的端到端语音合成模型

参考文献:

[1] Kim J, Kong J, Son J. Conditional variational autoencoder with adversarial learning for end-to-end text-to-speech[C]//International Conference on Machine Learning. PMLR, 2021: 5530-5540.

[2] Su J, Wu G. f-VAEs: Improve VAEs with conditional flows[J]. arXiv preprint arXiv:1809.05861, 2018.

[3] Kim, J., Kim, S., Kong, J., and Y oon, S. Glow-TTS: A Generative Flow for Text-to-Speech via Monotonic Alignment Search. Advances in Neural Information Processing Systems, 33, 2020.

[4] Theis L, Oord A, Bethge M. A note on the evaluation of generative models[J]. arXiv preprint arXiv:1511.01844, 2015.

[5] Ho J, Chen X, Srinivas A, et al. Flow++: Improving flow-based generative models with variational dequantization and architecture design[C]//International Conference on Machine Learning. PMLR, 2019: 2722-2730.

[6] Kong J, Kim J, Bae J. Hifi-gan: Generative adversarial networks for efficient and high fidelity speech synthesis[J]. Advances in Neural Information Processing Systems, 2020, 33: 17022-17033.

[7] 变分自编码器(一):原来是这么一回事 - 科学空间|Scientific Spaces

[8] 细水长flow之f-VAEs:Glow与VAEs的联姻 - 科学空间|Scientific Spaces

[9] 细读经典:VITS,用于语音合成带有对抗学习的条件变分自编码器 - 知乎 (zhihu.com)

[10] 论文分享 | VITS:基于条件变分自编码器和对抗学习的端到端语音合成模型 (careerengine.us)

[11] VITS 语音合成完全端到端TTS的里程碑-CSDN博客

文章目录

  • 导语
  • 变分推断部分(Variational Inference)
    • 条件变分自编码器(cVAE)
    • 重构损失(Reconstruction Loss)
    • KL 散度(KL-Divergence)
    • 流模型 Flow 的介入
  • 对齐估计部分(Alignment Estimation)
    • 单调对齐搜索(Monotonic Alignment Search)
    • 从文本中预测时长(Duration Prediction from Text)
  • 对抗学习部分(Adversarial Training)
  • 损失函数总结
  • 模型架构介绍
    • 后验编码器(Posterior Encoder)【仅用于训练】
    • 先验编码器(Prior Encoder)
    • 解码器(Decoder)
    • 判别器(Discriminator)【仅用于训练】
    • 随机持续时间预测器(Stochastic Duration Predictor)
    • 推理阶段架构

导语

在学习 VITS 前,请首先去了解 VAE 和 Flow,本篇文章将基于这两篇我的博客,欢迎大家前往查看。

变分推断部分(Variational Inference)

条件变分自编码器(cVAE)

  • 首先,上来就是条件VAE(cVAE)的核心公式:
    log ⁡ p θ ( x ∣ c ) ≥ E z ∼ q ϕ ( z ∣ x ) [ log ⁡ p ( x ∣ z ) ] − D K L [ q ϕ ( z ∣ x ) ∣ ∣ p θ ( z ∣ c ) ] = E z ∼ q ϕ ( z ∣ x ) [ log ⁡ p ( x ∣ z ) − log ⁡ q ϕ ( z ∣ x ) p θ ( z ∣ c ) ] \begin{aligned} \log{p_\theta(x|c)} &\ge \mathbb{E}_{z\sim q_\phi(z|x)}[\log{p(x|z)}] - D_{KL}[q_\phi(z|x)||p_\theta(z|c)] \\ &= \mathbb{E}_{z\sim q_\phi(z|x)}[\log{p(x|z)} - \log\frac{q_\phi(z|x)}{p_\theta(z|c)}] \end{aligned} logpθ(xc)Ezqϕ(zx)[logp(xz)]DKL[qϕ(zx)pθ(zc)]=Ezqϕ(zx)[logp(xz)logpθ(zc)qϕ(zx)]

    • 右式就是所谓的变分下界(Variational Lower Bound,又称为 Evidence Lower Bound,ELBO)。相较于常规的 VAE,这里的条件 VAE 就是多加了个引导性质的条件 c,类似于标签信息,用于引导生成的样本,在 语音合成(Text To Speech,TTS) 任务中自然就相当于提供的文本信息(Text)。

    • 加入的条件为 c,那么自然的,z 的先验分布就变成了 p(z|c),边缘似然也加了条件 c,变成了 p(x|c)

    • q 就是我们熟知的近似后验分布(Approximate Posterior Distribution)

    • 而等式右边,我们之前也介绍过,可以分为重构项(第一项)和正则项(第二项)。

    • 简单的条件 VAE 的结构图如下,看不太懂也没关系,后面继续讲,图源自变分自编码器(一):原来是这么一回事 - 科学空间|Scientific Spaces

  • 和 VAE 的目标一样,我们也需要将这个 ELBO 最大化,因此这个公式就是我们的目标函数的一部分。由此我们就可以得到部分的训练损失函数,也就是 ELBO 取反。一为重构损失:
    L r e c o n = − E z ∼ q ϕ ( z ∣ x ) [ log ⁡ p ( x ∣ z ) ] \mathcal{L}_{recon} = -\mathbb{E}_{z\sim q_\phi(z|x)}[\log{p(x|z)}] Lrecon=Ezqϕ(zx)[logp(xz)]
    二为正则损失(KL 散度,KL-Divergence)
    L k l = D K L [ q ϕ ( z ∣ x ) ∣ ∣ p θ ( z ∣ c ) ] \mathcal{L}_{kl} = D_{KL}[q_\phi(z|x)||p_\theta(z|c)] Lkl=DKL[qϕ(zx)pθ(zc)]

重构损失(Reconstruction Loss)

  • 首先是重构损失。在 VAE 中我们介绍过,重构损失就是比较真实样本与生成样本的差距。而论文中并没有直接比较二者的原始波形,而是选取了真实样本与生成样本的梅尔频谱图(Mel-spectrogram)进行比较,因此重构损失的式子可以表示为:
    L r e c o n = ∥ x m e l − x ^ m e l ∥ 1 \mathcal{L}_{recon} = \parallel{x_{mel} - \hat{x}_{mel}}\parallel_1 Lrecon=xmelx^mel1
    注意,想要从原始波形得到其梅尔频谱图是不需要通过神经网络的,换言之这个变换过程不需要参与训练。并且,这个也只会出现在训练过程中,而不会出现在推理过程中。此外,从潜在空间采样出潜在变量 z 后,我们也不是将整个 z 都喂给 decoder,而是选
    取其中的部分序列
    作为 decoder 的输入,论文中这项技术被称为 Windowed Generator Trainning,可以翻译成窗口生成器训练。

    在训练时只对潜在变量 z 的一部分进行采样,而不是对整个序列进行采样。这个部分通常是一个固定大小的窗口,包含潜在变量序列的一部分。

    这样做的原因可能是为了提高训练效率,减少计算成本,并且更好地处理长序列。在文本到语音合成等任务中,输入序列可能非常长,完全采样整个序列可能会变得非常昂贵。通过使用部分序列,可以更有效地进行训练,并更好地处理长序列的上下文信息。

KL 散度(KL-Divergence)

  • 其次是 KL 散度。在这一项 KL 散度中,我们需要比较 z 的后验分布与它的先验分布之间的差距,防止过拟合等等,当然,这里的后验分布是近似后验分布(q)。我们分别讨论:

    • **z 的后验分布 q(z|x):在这里,我们并没有使用样本 x 的梅尔频谱图来计算近似后验分布,而是使用 x 的线性频谱图(linear-scale spectrogram)**来作为后验编码器的输入,用于计算 z 的后验分布,记为 x_lin,原因可以参考下方:

      1. 高分辨率信息提供:在语音合成任务中,高分辨率的频谱信息对于捕捉语音信号的细节和清晰度至关重要。梅尔频谱图在频域上对声音信息进行了压缩,因此可能会丢失一些高频细节。使用线性频谱图能够为后验编码器提供更多的高分辨率信息,有助于模型更准确地学习和生成语音。
      2. 后验编码器设计:后验编码器的任务是将输入语音的频谱信息映射到潜在变量 z 的分布。使用线性频谱图作为输入有助于后验编码器更好地建模输入语音的频谱特征。对于 VITS,这个设计选择是为了提供更多的信息以改善潜在变量的建模。
      3. 变分推断属性:虽然输入从梅尔频谱图变为线性频谱图,但这并不违反变分推断的属性。变分推断的目标是在给定观测数据的条件下,找到一个潜在变量的分布,使得该分布与真实的后验分布尽可能接近。选择不同的输入特征并不会违反这一属性,只要模型能够在学习过程中适应这些变化。

      因此,z 的后验分布我们记为:
      q ϕ ( z ∣ x l i n ) q_\phi(z|x_{lin}) qϕ(zxlin)

    • z 的先验分布 p(z|c):在 VAE 中,我们说过,z 的先验分布就是标准正态分布 N(0, I),也就是 p(z),而在条件 VAE 中,由于有了先验条件,因此 z 的先验分布也需要改变,即变为了 p(z|c),因此 z 的先验分布也需要进行计算。在 cVAE 中,我们额外使用一个编码器去计算 z 的先验分布,我们称其为先验编码器(Prior Encoder),记为 c。而在 VITS 中,先验编码器的输入主要有:

      • 从输入文本中提取出来的音素序列(phonemes sequence),记为c_text
      • 音素和潜在变量之间的对齐矩阵(Alignment between phonemes and latent variables),记为 A

      值得一提的是,对齐矩阵 A 是一个硬性单调的,|c_text|×|z| 维度的注意力矩阵(hard monotonic attention matrix ),目的在于去表示每个音素需要扩展为多长的时间来与目标语音时间进行对齐,相当于去寻找哪些文本对应语音的哪些部分,即语音和文本的对应关系。这样可以确保在合成的过程中,生成的声学特征与输入的文本之间的对齐关系是单调递增的,即随着时间的推移,声学特征的生成应该与文本中的语音单元相对应,而不会出现跳跃或错位。注意力机制在我讲解 Transformer 的博客中有详细介绍Transformer模型详解,可以前往了解一下,这里用处不大,仅仅需要知道大概概念即可。

      综合音素序列和对齐矩阵这两个先验编码器的输入,z 的先验分布我们也可以写出:
      p θ ( z ∣ c t e x t , A ) p_\theta(z|c_{text}, A) pθ(zctext,A)

  • 所以损失函数的第二项,也就是 KL散度项就是:
    L k l = log ⁡ q ϕ ( z ∣ x l i n ) − log ⁡ p θ ( z ∣ c t e x t , A ) \mathcal{L}_{kl} = \log q_\phi(z|x_{lin}) - \log p_\theta(z|c_{text}, A) Lkl=logqϕ(zxlin)logpθ(zctext,A)
    其中,z 的后验分布展开后为:
    z ∼ q ϕ ( z ∣ x l i n ) = N ( z ; μ ϕ ( x l i n ) , σ ϕ ( x l i n ) ) z \sim q_\phi(z|x_{lin}) = N(z; \mu_\phi(x_{lin}), \sigma_\phi(x_{lin})) zqϕ(zxlin)=N(z;μϕ(xlin),σϕ(xlin))

流模型 Flow 的介入

  • 不过,到这边为止,似乎都是有关 cVAE 的内容,那么 VITS 的创新点在哪里呢?就在于先验分布和后验分布上!实际上,VITS 的设计针对的就是 VAE 身上的一些问题。人们发现,在使用 VAE 生成样本,比如图片的时候,生成出的样本往往都会很模糊,质量并不是很高。问题出在哪里?

    • 有人认为是 mse 误差的问题,也有人认为是KL散度的固有性质。但留意到一点是:即使去掉隐变量的 KL 散度那一项,变成普通的自编码器,重构出来的图像通常也是模糊的。这表明,VAEs图像模糊可能是低维重构原始图像的固有问题。
    • 既然是从低维重构原始图像不太行,那如果将隐变量维度取输入维度一样大小呢?似乎还不够,因为标准的 VAE 将后验分布也假设为高斯分布,这限制了模型的表达能力。事实上,人们猜测,由于高斯分布簇只是众多可能的后验分布中极小的一部分,如果后验分布的性质与高斯分布差很远,那么拟合效果就会很糟糕。
  • 因此,人们想到了另一个模型:Flow。流模型通过一系列耦合层,可以将复杂的输入分布转化为高斯分布,这样的过程可逆,雅可比矩阵行列式也容易计算,那么我们就可以使用流模型,将原本过于简单的高斯分布变为更加复杂的分布,从而提高其表达能力

  • 事实上,流模型诸如 Glow 也有一定的问题。我们在上一篇介绍流模型的文章流模型 Flow 超详解中曾经讲过,诸多对于变换过程的限制导致一层耦合层只能带来非常弱的拟合能力(非线性能力),所以一般还需要足够多的耦合层才能累积为强非线性变换,因此Glow模型通常比较庞大,训练时间较长。

  • 回归 VITS 模型本身。在 VITS 中,就是在先验分布上使用了标准化流(normalizing flow)的技术,通过对简单的先验分布进行可逆变换,将其转化为了更加复杂的分布,从而使得先验分布的表达能力提高,更能够捕捉真实样本的分布特征。我们使用 f_θ 表示标准化流,根据变量变换定理,VITS 的先验分布可以重写为:
    p θ ( z ∣ c ) = N ( f θ ( z ) ; μ θ ( c ) , σ θ ( c ) ) ∣ det ⁡ ∂ f θ ( z ) ∂ z ∣ p_\theta(z|c) = N(f_\theta(z);\mu_\theta(c), \sigma_\theta(c))\left| \det\frac{\partial f_\theta(z)}{\partial z} \right| pθ(zc)=N(fθ(z);μθ(c),σθ(c))detzfθ(z)
    而其中,这里的 c 就是先验编码器。根据 c 的输入,c 可以表示为:
    c = [ c t e x t , A ] c = [c_{text}, A] c=[ctext,A]

注意,这里的先验分布 p_θ(z|c) 推导利用的变量变换定理在上一篇文章流模型 Flow 超详解的**变量变换定理(Change of Variables Theorem)**一节中进行了详细介绍,可供参考。这里利用结论作简单推导:

  • 我们的目的是让 z 的先验分布变得复杂,而不是简单的参数化高斯分布。因此我们首先假设 z 的先验分布空间非常复杂,在 z 通过一个标准化流 f_θ 后,可以变成一个简单的参数化的高斯分布,这个高斯分布为:
    p ( f θ ( z ) ∣ c ) = N ( f θ ( z ) ; μ θ ( c ) , σ θ ( c ) ) p(f_\theta(z)|c) = N(f_\theta(z); \mu_\theta(c), \sigma_\theta(c)) p(fθ(z)c)=N(fθ(z);μθ(c),σθ(c))
    其中,由于受到条件 c(即 c_text 和对齐矩阵 A)的影响,因此其均值和方差需要加上 c。

  • 我们需要求取的是复杂空间 z 的先验分布,而 z 和 f_θ(z) 的关系为:
    z = f θ − 1 ( f θ ( z ) ) z = f_\theta^{-1}(f_\theta(z)) z=fθ1(fθ(z))

  • 由上面两个式子,通过变量变换定理,我们可以知道:
    p ( z ∣ c ) = p ( f θ ( z ) ∣ c ) ∣ det ⁡ ( J f − 1 ) ∣ = p ( f θ ( z ) ∣ c ) ∣ det ⁡ ( J ( f θ − 1 ) − 1 ) ∣ = p ( f θ ( z ) ∣ c ) ∣ det ⁡ ( J f θ ) ∣ = N ( f θ ( z ) ; μ θ ( c ) , σ θ ( c ) ) ∣ det ⁡ ∂ f θ ( z ) ∂ z ∣ \begin{aligned} p(z|c) &= p(f_\theta(z)|c)\left| \det(J_{f^{-1}})\right| \\ &= p(f_\theta(z)|c)\left| \det(J_{(f_\theta^{-1})^{-1}})\right| \\ &= p(f_\theta(z)|c)\left| \det(J_{f_\theta})\right| \\ &= N(f_\theta(z); \mu_\theta(c), \sigma_\theta(c))\left| \det\frac{\partial f_\theta(z)}{\partial z} \right| \end{aligned} p(zc)=p(fθ(z)c)det(Jf1)=p(fθ(z)c)det(J(fθ1)1)=p(fθ(z)c)det(Jfθ)=N(fθ(z);μθ(c),σθ(c))detzfθ(z)

  • 结论得证。

对齐估计部分(Alignment Estimation)

我们曾经说过,对于先验编码器来说,它的输入有音素序列 c_text 和对齐矩阵 A。然而,音素序列有办法直接进行转化,但对齐矩阵 A 可不是凭空就来的。不过很可惜的是,我们并没有对齐矩阵的真实标签,因此我们必须在每次训练迭代的时候进行对齐估计。下面就来介绍这一部分。

单调对齐搜索(Monotonic Alignment Search)

  • 单调对齐搜索,顾名思义,就是去找一个单调对齐矩阵,而这个矩阵就是最好的单调对齐矩阵。怎么算是最好的单调对齐矩阵呢?那当然是能提高找到样本的概率,也就是提高目标函数的单调对齐矩阵最好啊!因此,很自然的,我们可以得到 MAS 的核心目标公式:
    A = arg ⁡ max ⁡ A ^ log ⁡ p ( x ∣ c t e x t , A ^ ) A = \arg \max_{\hat A} \log p(x|c_{text}, \hat{A}) A=argA^maxlogp(xctext,A^)
    由于我们在 VITS 中使用了标准化流 f,经过 f 后,分布将会变成一个简单的高斯分布,因此这个公式可以变化为:
    A = arg ⁡ max ⁡ A ^ log ⁡ p ( x ∣ c t e x t , A ^ ) = arg ⁡ max ⁡ A ^ log ⁡ [ N ( f ( x ) ; μ ( c t e x t , A ^ ) , σ ( c t e x t , A ^ ) ) ∣ det ⁡ ∂ f ( x ) ∂ x ∣ ] = arg ⁡ max ⁡ A ^ log ⁡ N ( f ( x ) ; μ ( c t e x t , A ^ ) , σ ( c t e x t , A ^ ) ) \begin{aligned} A &= \arg \max_{\hat A} \log p(x|c_{text}, {\hat A}) \\ &= \arg \max_{\hat A} \log [N(f(x); \mu(c_{text}, \hat{A}), \sigma(c_{text}, \hat{A}))\left| \det\frac{\partial f(x)}{\partial x}\right|] \\ &= \arg \max_{\hat A} \log N(f(x); \mu(c_{text}, \hat{A}), \sigma(c_{text}, \hat{A})) \end{aligned} A=argA^maxlogp(xctext,A^)=argA^maxlog[N(f(x);μ(ctext,A^),σ(ctext,A^))detxf(x)]=argA^maxlogN(f(x);μ(ctext,A^),σ(ctext,A^))

  • 然而,这对于我们来说,直接计算 log p 有点太麻烦了,毕竟我们前面都在讨论怎么最大化 ELBO,而不是最大化 log p。因此我们不妨改改这个 MAS 核心公式,将目标切换成让 ELBO 最大的对齐矩阵 A,即:
    A = arg ⁡ max ⁡ A ^ log ⁡ p ( x ∣ c t e x t , A ^ ) = arg ⁡ max ⁡ A ^ E z ∼ q ϕ ( z ∣ x ) [ log ⁡ p ( x ∣ z ) − log ⁡ q ϕ ( z ∣ x ) p θ ( z ∣ c t e x t , A ^ ) ] = arg ⁡ max ⁡ A ^ ( − log ⁡ q ϕ ( z ∣ x ) p θ ( z ∣ c t e x t , A ^ ) ) = arg ⁡ max ⁡ A ^ log ⁡ p θ ( z ∣ c t e x t , A ^ ) q ϕ ( z ∣ x ) = arg ⁡ max ⁡ A ^ log ⁡ p θ ( z ∣ c t e x t , A ^ ) \begin{aligned} A &= \arg \max_{\hat A} \log p(x|c_{text}, \hat{A}) \\ &= \arg \max_{\hat A} \mathbb{E}_{z\sim q_\phi(z|x)}[\log{p(x|z)} - \log\frac{q_\phi(z|x)}{p_\theta(z|c_{text}, \hat{A})}] \\ &= \arg \max_{\hat A} (- \log\frac{q_\phi(z|x)}{p_\theta(z|c_{text}, \hat{A})}) \\ &= \arg \max_{\hat A} \log\frac{p_\theta(z|c_{text}, \hat{A})}{q_\phi(z|x)} \\ &= \arg \max_{\hat A} \log p_\theta(z|c_{text}, \hat{A}) \\ \end{aligned} A=argA^maxlogp(xctext,A^)=argA^maxEzqϕ(zx)[logp(xz)logpθ(zctext,A^)qϕ(zx)]=argA^max(logpθ(zctext,A^)qϕ(zx))=argA^maxlogqϕ(zx)pθ(zctext,A^)=argA^maxlogpθ(zctext,A^)
    使用之前提供的公式,我们将 z 的先验分布进行变换:
    A = arg ⁡ max ⁡ A ^ log ⁡ p θ ( z ∣ c t e x t , A ^ ) = arg ⁡ max ⁡ A ^ log ⁡ N ( f θ ( z ) ; μ θ ( c t e x t , A ^ ) , σ θ ( c t e x t , A ^ ) ) \begin{aligned} A &= \arg \max_{\hat A} \log p_\theta(z|c_{text}, \hat{A}) \\ &= \arg \max_{\hat A}\log N(f_\theta(z); \mu_\theta(c_{text}, \hat{A}), \sigma_\theta(c_{text}, \hat{A})) \end{aligned} A=argA^maxlogpθ(zctext,A^)=argA^maxlogN(fθ(z);μθ(ctext,A^),σθ(ctext,A^))

  • 哦?这个公式是不是和之前我们提供的核心公式很相似?因此论文的作者偷了个懒,既然变换后的公式相似,我们就直接使用原始的 MAS 就行了,直接站在巨人的肩膀上。对于原始的 MAS,请参考论文Glow-TTS: A Generative Flow for Text-to-Speech via Monotonic Alignment Search

从文本中预测时长(Duration Prediction from Text)

  • 训练时可以通过样本语音、c_text,使用 MAS 来生成单调对齐矩阵,但在推理阶段我们没有语音,因此我们就需要额外再训练一个音素时长预测器(Phonemes Duration Predictor),去预测每个音素的发音时长,用于推理的对齐矩阵生成中。

  • 传统的时长预测可以通过将对齐矩阵 A 的每一行所有列求和来计算每个音素的持续时间,但是这样无法表现出韵律节奏变化多样性。为了反映真实的语音韵律多样性,论文设计了随机时长预测器(Stochastic Duration Predictor)。它是基于流模型的生成式模型,以最大化似然估计的方式进行训练。但是直接训练的话有两个问题:

    • 音素时长是一个离散的整数,需要使用连续标准流模型进行**去量化(Dequantization)**操作;
    • 音素时长是一个标量(scalar),难以实现高维变换。
  • 为了解决这两个问题,我们使用变分去量化(variational dequantization)和变分数据规整(variational data augmentation)两个策略,我们用 d 来表示持续时间序列(duration sequence):

    • 变分去量化:引入随机变量 u,和持续时间序列 d 有着相同的时间尺度(分辨率)和维度。由于音素长度是非零正整数,因此我们限定 u 的范围为 [0, 1),因此 d - u 就变成了一个正实数序列。
    • 变分数据规整:引入随机变量 v,和持续时间序列 d 有着相同的时间尺度(分辨率)和维度。我们将 v 的每一个通道都对应上 d 的每一个通道,将二者级联起来,共同组成一个更高维度的潜在表达。
  • 有了这两个变量,我们就可以着手去求我们的目标函数,也就是最大化持续时间序列的对数似然(log-likelihood)了。在这里,我们不去直接求取对数似然,而是去选取它的变分下界。在此之前,我们先根据刚刚的两个随机变量 u、v,引入一个后验分布:
    q ϕ ( u , v ∣ d , c t e x t ) q_\phi(u,v|d, c_{text}) qϕ(u,vd,ctext)

  • 因此,对数似然的变分下界就为:
    log ⁡ p θ ( d ∣ c t e x t ) ≥ E q ϕ ( u , v ∣ d , c t e x t ) [ log ⁡ p θ ( d − u , v ∣ c t e x t ) q ϕ ( u , v ∣ d , c t e x t ) ] \log p_\theta(d|c_{text}) \ge \mathbb{E}_{q_\phi(u,v|d, c_{text})}[\log\frac{p_\theta(d-u, v|c_{text})}{q_\phi(u,v|d,c_{text})}] logpθ(dctext)Eqϕ(u,vd,ctext)[logqϕ(u,vd,ctext)pθ(du,vctext)]
    简单的推导过程在后面。

  • 训练损失我们使用 L_dur 来表示,则训练损失就是变分下界的负项:
    L d u r = − E q ϕ ( u , v ∣ d , c t e x t ) [ log ⁡ p θ ( d − u , v ∣ c t e x t ) q ϕ ( u , v ∣ d , c t e x t ) ] \mathcal{L}_{dur} = -\mathbb{E}_{q_\phi(u,v|d, c_{text})}[\log\frac{p_\theta(d-u, v|c_{text})}{q_\phi(u,v|d,c_{text})}] Ldur=Eqϕ(u,vd,ctext)[logqϕ(u,vd,ctext)pθ(du,vctext)]

  • 与此同时,在训练时将会断开随机时长预测器的梯度反传,防止对该部分的训练时产生的梯度影响到其它模块。采样的过程就比较简单了,音素持续时间是通过随机持续时间预测器的逆变换从随机噪声中采样的,然后将其转换为整数即可,论文是采取了向上取整。

    下面简单说明一下持续时间序列的对数似然的变分下界的推导过程。过程参考论文:

    [1] Theis L, Oord A, Bethge M. A note on the evaluation of generative models[J]. arXiv preprint arXiv:1511.01844, 2015.

    [2] Ho J, Chen X, Srinivas A, et al. Flow++: Improving flow-based generative models with variational dequantization and architecture design[C]//International Conference on Machine Learning. PMLR, 2019: 2722-2730.

    • 我们遇到的持续时长 d 的各部分值是离散的正整数,因此持续时长预测是个离散概率模型,这个模型我们使用 P_model 来表示,持续时长序列 d 的维度我们用 D 来表示。直接训练这个离散模型很困难,不过论文[1]中指出,实际上,训练一个对 y=d-u, u∈[0,1)^D 建模的连续概率模型 p_θ,就相当于是在给 P_model 的对数似然下界进行训练。P_model 我们很容易可以通过这样的方式来表示:
      P m o d e l : = ∫ [ 0 , 1 ] D p θ ( d − u ) d u P_{model} := \int_{[0,1]^D} p_{\theta}(d-u){\rm d}u Pmodel:=[0,1]Dpθ(du)du

    • 而这里的 u 我们称之为去量化噪声(Dequantization Noise),在使用中是需要通过训练产生的,因此我们只能使用后验分布去表示 u 的分布。这里我们使用 q(u|d) 来表示 u 的近似后验分布。我们用 P_data 来表示真实离散数据 d 的分布,P_model 我们刚刚说过了就是我们期待训练的 d 的离散概率模型,因此我们的目标函数可以这样变化:
      E d ∼ P d a t a [ log ⁡ P m o d e l ( d ) ] = E d ∼ P d a t a [ log ⁡ ∫ [ 0 , 1 ] D p θ ( d − u ) d u ] = E d ∼ P d a t a [ log ⁡ ∫ [ 0 , 1 ] D q ( u ∣ d ) p θ ( d − u ) q ( u ∣ d ) d u ] \begin{aligned} \mathbb{E}_{d\sim P_{data}}[\log P_{model}(d)] &= \mathbb{E}_{d\sim P_{data}}[\log \int_{[0,1]^D} p_{\theta}(d-u){\rm d}u] \\ &= \mathbb{E}_{d\sim P_{data}}[\log \int_{[0,1]^D} q(u|d)\frac{p_{\theta}(d-u)}{q(u|d)}{\rm d}u] \end{aligned} EdPdata[logPmodel(d)]=EdPdata[log[0,1]Dpθ(du)du]=EdPdata[log[0,1]Dq(ud)q(ud)pθ(du)du]
      由 Jensen 不等式,可得:
      E d ∼ P d a t a [ log ⁡ P m o d e l ( d ) ] = E d ∼ P d a t a [ log ⁡ ∫ [ 0 , 1 ] D q ( u ∣ d ) p θ ( d − u ) q ( u ∣ d ) d u ] ≥ E d ∼ P d a t a [ ∫ [ 0 , 1 ] D q ( u ∣ d ) log ⁡ p θ ( d − u ) q ( u ∣ d ) d u ] = E d ∼ P d a t a E u ∼ q ( u ∣ d ) [ log ⁡ p θ ( d − u ) q ( u ∣ d ) ] \begin{aligned} \mathbb{E}_{d\sim P_{data}}[\log P_{model}(d)] &= \mathbb{E}_{d\sim P_{data}}[\log \int_{[0,1]^D} q(u|d)\frac{p_{\theta}(d-u)}{q(u|d)}{\rm d}u] \\ &\ge \mathbb{E}_{d\sim P_{data}}[\int_{[0,1]^D}q(u|d)\log \frac{p_{\theta}(d-u)}{q(u|d)}{\rm d}u] \\ &= \mathbb{E}_{d\sim P_{data}}\mathbb{E}_{u\sim q(u|d)}[\log \frac{p_{\theta}(d-u)}{q(u|d)}] \\ \end{aligned} EdPdata[logPmodel(d)]=EdPdata[log[0,1]Dq(ud)q(ud)pθ(du)du]EdPdata[[0,1]Dq(ud)logq(ud)pθ(du)du]=EdPdataEuq(ud)[logq(ud)pθ(du)]

    • 因此我们很容易可以得到,我们期望的目标函数的变分下界为:
      log ⁡ P m o d e l ( d ) ≥ E u ∼ q ( u ∣ d ) [ log ⁡ p θ ( d − u ) q ( u ∣ d ) ] \log P_{model}(d) \ge \mathbb{E}_{u\sim q(u|d)}[\log \frac{p_{\theta}(d-u)}{q(u|d)}] logPmodel(d)Euq(ud)[logq(ud)pθ(du)]

    • 再加上与 v 的联合概率分布,以及条件 c_text,我们不难推出:
      log ⁡ P m o d e l ( d ∣ c t e x t ) ≥ E q ϕ ( u , v ∣ d , c t e x t ) [ log ⁡ p θ ( d − u , v ∣ c t e x t ) q ϕ ( u , v ∣ d , c t e x t ) ] \log P_{model}(d|c_{text}) \ge \mathbb{E}_{q_\phi(u,v|d, c_{text})}[\log\frac{p_\theta(d-u, v|c_{text})}{q_\phi(u,v|d,c_{text})}] logPmodel(dctext)Eqϕ(u,vd,ctext)[logqϕ(u,vd,ctext)pθ(du,vctext)]
      结论得证。

对抗学习部分(Adversarial Training)

  • 这一部分就很简单了,使用对抗学习使得训练过程更加高效。主要就是引入一个判别器 D 来判断模型输出是真实波形 y 还是解码器 G 输出的波形。损失函数则是选用了两种曾经成功运用于 TTS 任务的损失函数:

    • 最小二乘损失函数(least-squares loss function)(Mao et al., 2017),用于对抗训练。
      L a d v ( D ) = E ( y , z ) [ ( D ( y ) − 1 ) 2 + ( D ( G ( z ) ) ) 2 ] L a d v ( G ) = E z [ ( D ( G ( z ) ) − 1 ) 2 ] \mathcal{L}_{adv}(D) = \mathbb{E}_{(y,z)}[(D(y)-1)^2+(D(G(z)))^2] \\ \mathcal{L}_{adv}(G) = \mathbb{E}_{z}[(D(G(z))-1)^2] Ladv(D)=E(y,z)[(D(y)1)2+(D(G(z)))2]Ladv(G)=Ez[(D(G(z))1)2]

    • 额外的特征匹配损失(feature-matching loss) (Larsen et al., 2016),用于训练生成器(generator)
      L f m ( G ) = E ( y , z ) [ ∑ l = 1 T 1 N l ∥ D l ( y ) − D l ( G ( z ) ) ∥ 1 ] \mathcal{L}_{fm}(G) = \mathbb{E}_{(y,z)}[\sum_{l=1}^{T}\frac{1}{N_l}\lVert D^l(y)-D^l(G(z))\rVert_1] Lfm(G)=E(y,z)[l=1TNl1Dl(y)Dl(G(z))1]

      • T 表示判别器中的总层数
      • D^l 输出具有 N_l 特征数的判别器的第 l 层的特征图
  • 值得一提的是,这两部分损失函数和 HiFiGAN 的生成器损失函数基本相同,如果想要深入了解可以参考相关论文,这里不作过多赘述。其中,特征匹配损失可以看作是在判别器的隐藏层中测量的重建损失,用于约束判别器中间层的输出。

损失函数总结

  • 综合上面所有讲述的内容,我们这个结合了 VAE 和 GAN 训练方式的模型,最终的训练损失函数如下:
    L v a e = L r e c o n + L k l + L d u r + L a d v ( G ) + L f m ( G ) \mathcal{L}_{vae} = \mathcal{L}_{recon} + \mathcal{L}_{kl} + \mathcal{L}_{dur} + \mathcal{L}_{adv}(G) + \mathcal{L}_{fm}(G) Lvae=Lrecon+Lkl+Ldur+Ladv(G)+Lfm(G)

模型架构介绍

  • 通过上面的介绍,现在我们也可以好好将整个模型进行呈现。以训练过程为例,模型组成如下:

后验编码器(Posterior Encoder)【仅用于训练】

  • 在训练时后验编码器以线性谱为输入,输出隐变量 z。推理阶段隐变量 z 则由流模型产生。VITS 的后验编码器采用 WaveGlow 和 Glow-TTS 中的非因果 WaveNet 残差模块。应用于多人模型时,可以将说话人向量添加到残差模块。

先验编码器(Prior Encoder)

  • 先验编码器包括文本编码器(Text Encoder)和提升先验分布多样性的标准化流。文本编码器为基于相对位置编码的 transformer 编码器。我们将**线性投影层(Linear Projection Layer)**添加到文本编码器的最后一层,这样就能生成构建先验分布的均值和方差。标准化流模块包含若干 WaveNet 的残差块。应用于多人模型时,可以向标准化流的残差模块添加说话人向量。

解码器(Decoder)

  • 解码器的结构与 HiFi-GAN V1 的生成器结构相同,应用于多人模型时,会将说话人向量线性变换后与隐变量 z 拼接在一起。

判别器(Discriminator)【仅用于训练】

  • 判别器的结构与 HiFI-GAN 中的多周期判别器结构相同。

随机持续时间预测器(Stochastic Duration Predictor)

  • 随机时长预测器用文本音素表示的隐状态 h_text 作为条件输入,用来估计音素的时长分布。应用于多人模型时,会将说话人向量线性变换后与隐状态 h_text 拼接在一起。

推理阶段架构

  • 最后再将推理时的模型架构给出:

  • 26
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
【资源介绍】 基于C++独立编译的中文和英文语音合成项目源码+项目说明+模型.zip 该项目是一个独立编译的语音合成程序(TTS)。可以本地运行不需要网络,而且没有额外的依赖,一键编译完成即可用于中文和英文的语音合成。 该项目的底层计算库使用Eigen,Eigen是一套模板定义的函数,大部分情况下,只需要包含头文件即可,所以本项目没有其他依赖,在C++环境下可以独立编译和运行。 本项目使用Eigen提供的矩阵库实现了神经网络的算子,不需要依赖例如pytorch,tensorflow, ncnn 等其他NN运行环境。 本项目在 Ubuntu 上编译运行通过,其他类Linux平台,如Android,树莓派等,也应该没啥大问题,在Window上没有测试过,可能需要少许改动。 本项目的模型基于语音合成算法 vits, 在其基础上进行了基于C++的工程化 将本项目下载到本地,最好是Ubuntu Linux 环境 从以下的百度网盘地址下载模型,放入本项目的model目录中: 链接: https://pan.baidu.com/s/1rYhtznOYQH7m8g-xZ_2VVQ?pwd=2d5h 提取码: 2d5h 模型文件放入后,models目录结构如下: models/ ├── multi_speakers.bin ├── single_speaker_mid.bin ├── single_speaker_english.bin ├── single_speaker_english_fast.bin └── single_speaker_fast.bin 进入Build 目录,执行以下命令: cmake .. make 编译完成后,会在Build 目录中生成 tts_test 执行程序 运行下列命令,测试中文语音合成TTS): ./tts_test ../test.txt ../models/single_speaker_fast.bin out.wav 运行下列命令,测试英文语音合成TTS): ./tts_test ../test_eng.txt ../models/single_speaker_english.bin out_eng.wav 【备注】 该项目是个人毕设/课设/大作业项目,代码都经过本地调试测试,功能ok才上传,高分作品,可快速上手运行!欢迎下载使用,可用于小白学习、进阶。 该资源主要针对计算机、通信、人工智能、自动化等相关专业的学生、老师或从业者下载使用,亦可作为期末课程设计、课程大作业、毕业设计等。 项目整体具有较高的学习借鉴价值!基础能力强的可以在此基础上修改调整,以实现不同的功能。 欢迎下载使用,也欢迎交流学习

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值