论文标题:使用 GAN 进行语音模仿
原标题:Voice Impersonation Using Generative Adversarial Networks
关键词:语音模仿,生成对抗网络,工作,风格转型,风格转换
概述
提出的模型:VoiceGAN
基于:GAN、DiscoGAN、CNN
模型实现:Github:Yolanda-Gao/VoiceGANmodel (opens new window)
使用中文模版的全文翻译在最后
背景介绍
在论文中,我们试图通过将语音的风格内容从目标说话者转移到模仿者的语言来解决自动生成模仿的问题。目标是仅转换语音的特定方面,而不修改其他内容。
现有模型局限
- 它们通常不足以捕捉到更一般意义上的不可测量、不可量化的风格。
- 在大多数情况下,为了有效地学习语音转换,这些录音还必须是完全时间对齐的。
- 当目标不是学习语音的批量转换,而只是转换风格时,实现学习语音转换所需要的硬目标不仅是不现实的
样式转换的GAN
特别是在图片风格转换的应用背景下,已经有许多文献从很多方面对基本的 GAN 做了扩展。
就是把标准GAN的Loss稍稍变了一下名称而已。
DiscoGAN
DiscoGAN是一个对称模型它试图将两类数据
整体生成器损失是
判别器损失
用于语音模仿的 GAN
由 DisocoGAN 修改而来
难点:
- 问题一:最初的 DiscoGAN 设计用于固定尺寸的图像,而声音信号长度可变。
- 问题二:如何确保语音信号中的语言信息不会丢失
- 问题三:如何修改语音的特定方面, 例如风格。
DisocoGAN 的修改
- 解决一:使用完全的卷积结构与自适应的池化层。
在这里,一个自适应的 池化层被添加在 CNN 层之后和完全连接层之前, 它包括逐通道池化(channel-wise pooling),其中每个通道的特征图图(feature map)汇集到一个元素中。这会将任何可变大小的特征图转换为固定维数的向量,其中包含了与通道数一样多的单元。
解决二:修改重建损失(Reconstruction Loss)。
这里,
表示即使在转换为 之后也试图保留 的结构。仔细选择
和 可在转换为 后确保准确的再转换(复原)和保留语言信息。解决三:添加第二种判别器 ——风格嵌入模型。
加入了一个判别器
,确定原始数信号和转换得到的信号有着相同的预期的风格。
模型整体 Loss
模型整体图示
实验与结果
使用 TIDIGITS 数据集。该数据集总共326位发言者:
- 111名男性,114名女性、50名男生和51名女生。
- 每位说话者读77位数的句子。音频的采样率是16000赫兹。
模型实现
模型中的生成器网络包括 6 层卷积编码器和 6 层反卷积解码器。
判别器网络包括具有自适应池的 7 层 CNN。
生成结果的质量评估
风格分类测试
我们使用独立训练的基于CNN的分类器来预测生成的数据的样式。分类器接受了来自两个性别的发言者的800个话语的训练。结果显示100% 生成的数据被分类为目标说话者的风格 表示我们的VoiceGAN网络实现了良好的风格转换性能。
语音信噪比 (SNR) 测试
我们从测试数据集中随机选择 40 个样本(每个发言者 20 个)并计算生成结果的均值和方差。
WADA 测试结果因为我们生成的噪声没有很好地通过高斯噪声建模,所以大约都是 100 dB 。
STNR 测试结果显示我们生成的数据质量很好。
实现过程中数据的读取与处理:
data_style_*
为读入的数据,
经过切分为batch大小之后使用read_spect_matrix()
获取特征图谱矩阵,之后输入模型中训练
for i in range(n_batches):
pbar.update(i)
generator_A.zero_grad()
generator_B.zero_grad()
discriminator_A.zero_grad()
discriminator_B.zero_grad()
discriminator_S.zero_grad()
A_path = data_style_A[ i * batch_size: (i+1) * batch_size ]
B_path = data_style_B[ i * batch_size: (i+1) * batch_size ]
if args.task_name =='spectrogram':
A = read_spect_matrix( A_path )
B = read_spect_matrix( B_path )
A = Variable( torch.FloatTensor( A ) )
B = Variable( torch.FloatTensor( B ) )
if cuda:
A = A.cuda()
B = B.cuda()
# A -> AB -> ABA
# pdb.set_trace()
A = A.unsqueeze(1)
AB, AL_feats, LAB_feats = generator_B(A)
ABA, ABL_feats, ABLA_feats = generator_A(AB)
# B -> BA -> BAB
B = B.unsqueeze(1)
BA, BL_feats, LBA_feats = generator_A(B)
BAB, BAL_feats, BALB_feats = generator_B(BA)