- Guangzhi Sun & 2020icassp
- Cambridge & Google brain
- demo page
abstract
- motivation:使用VAE生成fine-grained prosody embedding,合成的语音在token之间的韵律变化不自然。
- method:使用VQ将latent features离散化,对于结果再单独训练一个自回归模型。
introduction
使用VAE提取的fine-grained latent features是对每个phn单独建模的,彼此之间具有独立性,因此在音节之间的长时停顿和能量以及基频上的陡增都会产生合成语音的不连续和不自然。——解决方法在于生成过程中缩小不自然的样本中标准分布的方差(VAE输出的是【0,1】高斯分布),因为这样可以降低采样离群值的可能性,但是这样只是限制了分布来的多样性,并没有根本解决问题。
Tacotron+QFVAE(quantized fine-grained VAE) reference encoder,将连续的样本离散成指定类,改善了合成语音的自然度。
methods
ref 【13】Robust and fine-grained prosody control of end-to-end speech synthesis
- 根据ref[13],从梅尔谱中对齐phn,然后和encoder计算latent embedding
z
z
z(连续域)
- 使用VQVAE将连续域的 z z z离散化处理,同时限制【0,1】高斯分布。
- 其中, z n z_n zn是reference encoder编码的信息, e n e_n en是VQ量化之后的结果,VQVAE的ELBO如上公式。 s g sg sg是stop gradient operator
- ∣ ∣ z n − s g [ e n ] ∣ ∣ 2 || z_n - sg[e_n] ||^2 ∣∣zn−sg[en]∣∣2:固定quantized,让连续域的latent embedding接近量化数值,同时避免向量空间过度膨胀
- ∣ ∣ s g [ z n ] − e n ∣ ∣ 2 || sg[z_n] - e_n||^2 ∣∣sg[zn]−en∣∣2:固定连续域的latent embedding,更新code-book,最小化量化引入的误差
VQ部分的代码实现
class VectorQuantizer(nn.Module):
def __init__(self, num_embeddings, embedding_dim, commitment_cost):
super(VectorQuantizer, self).__init__()
self._embedding_dim = embedding_dim
self._num_embeddings = num_embeddings
self._embedding = nn.Embedding(self._num_embeddings, self._embedding_dim)
self._embedding.weight.data.uniform_(-1/self._num_embeddings, 1/self._num_embeddings)
self._commitment_cost = commitment_cost
def forward(self, inputs):
# convert inputs from BCHW -> BHWC
inputs = inputs.permute(0, 2, 3, 1).contiguous()
input_shape = inputs.shape
# Flatten input
flat_input = inputs.view(-1, self._embedding_dim)
# Calculate distances
distances = (torch.sum(flat_input**2, dim=1, keepdim=True)
+ torch.sum(self._embedding.weight**2, dim=1)
- 2 * torch.matmul(flat_input, self._embedding.weight.t()))
# Encoding,argmin求结果最小时的取值
encoding_indices = torch.argmin(distances, dim=1).unsqueeze(1)
encodings = torch.zeros(encoding_indices.shape[0], self._num_embeddings, device=inputs.device)
encodings.scatter_(1, encoding_indices, 1)
# Quantize and unflatten
# 重新去code-book找到对应的权重数值
quantized = torch.matmul(encodings, self._embedding.weight).view(input_shape)
# Loss
# e_latent_loss:固定quantized,让连续域的latent embedding接近量化数值,同时避免向量空间过度膨胀
# q_latent_loss:固定连续域的latent embedding,更新code-book,最小化量化引入的误差
e_latent_loss = F.mse_loss(quantized.detach(), inputs)
q_latent_loss = F.mse_loss(quantized, inputs.detach())
loss = q_latent_loss + self._commitment_cost * e_latent_loss
# quantized送给后续层,要实现梯度反传前层,就要和input结合起来
# perplexity 作为参考向量,看vq-codebook emb有多少被用到,数量设置是否合理
quantized = inputs + (quantized - inputs).detach()
avg_probs = torch.mean(encodings, dim=0)
perplexity = torch.exp(-torch.sum(avg_probs * torch.log(avg_probs + 1e-10)))
# convert quantized from BHWC -> BCHW
return loss, quantized.permute(0, 3, 1, 2).contiguous(), perplexity, encodings
AR prosody prior
训练时候,reference encoder基于mel-spec预测phn level prosody latent embedding,但是inference的时候没有target spec,额外训练一个AR prosody prior model,模型基于encoder embeddings预测phn level prosody latent embedding,是自回归的预测方式。
预测有两种:
- 离散型AR:直接预测量化后的 e n e_n en
- 连续型AR:预测连续向量 z n z_n zn
experiments
- datastes:LibriTTS ,585 hours