【Day7】Vocoder Model 以及 WaveNet 介绍

声码器 (Vocoder)

  • 合成语音的概念最早是由贝尔实验室的工程师 - 荷马·达德利在 1928 年所提出,并在 1939 年 (二战开打那年) 的纽约世界博览会上首次展示了他取名为声码器的语音合成器

下面这支影片我们可以看到那时候的声码器是一台大型机器,在影片 39 秒的地方你可以听听那段惨不忍赌的合成人声,但在那个时候却是奇蹟。

如果你很有兴趣想知道那台老古董是怎麽发出声音的话,你可以看一下这篇文章的介绍

  • 看完最古老的 Vocoder 之後,我们广义上的定义一下 "现在的" Vocoder 其实只要是进行声音的分析与合成的系统不管软硬体,我们都可以称呼他为 Vocoder,所以用 AI 来合成人声的 Model 才会被称做 Vocoder。

WaveNet - 直接生成声音讯号的神经网路

  • WaveNet 是由 DeepMind 的研究人员於 2016年 9 月的一篇论文中提出的,早在 WaveNet 出现之前也有人使用神经网路合成语音,像是 Tacotron可以生成声音的时频谱以及一些特徵参数,但最後从频域回到时域还是用了 Griffin-Lim 演算法 (看来这演算还可以再战 10 年 ),而 wavenet 不用。除此之外它除了可以用在语音合成之外,也可以用在音乐生成 (GAN Music) 以及语音辨识其他领域,其衍伸出来的 WaveNet_Vocoder,就是 Auto_VC 原本使用的 Vocoder

然而我们不会用 WaveNet_Vocoder 来做,因为它实在是太慢了。

WaveNet 的想法

  • WaveNet 是源自於 PixelCNN 的想法,如果一张图片 P(X) 是由各像素 {x1 ~ xN} 所组合而成的,那一张图片就可以说它是各个像素的联合分布

想像一下不管是图片或是声音,下一个元素的产生或多或少都与前一个元素有关,在图片上的话最直观的理解就是渐层。

  • 因此如果我们用条件分布的乘积来表示的话就可以写成这样。

  • 在生成图片时就把 前面的像素当作条件来逐点生成 (这个就是慢的主要原因),我们把像素换成声音讯号就是 WaveNet 在做的事情,所以 WaveNet 要预测的目标其实是一下一个声音样本点的机率分布

WaveNet 的资料前处理

  • 声音讯号通常是以 16 位元的整数储在电脑里的,直接用的话模型在每个时间点都要输出 65536 个机率来表示所有可能样本点的机率分布,为了降低建模难度 WaveNet 用 µ low 演算法,将输入资料样本点量化成 8 位元,变成只有 256 种可能。

WaveNet 的核心

  • WaveNet 里使用了膨胀因果卷积(Dilated Causal Convolution) 其中膨胀卷积增加了 Respective field,而因果卷积则确保了前後输出的因果关系

WaveNet 的 Activation Function

WaveNet 中使用了和 PixelCNN 相同的 Gated Activation Unit,这个东西就类似激励函数,只是它是非线性的,在这里使用会比单纯做 ReLU 好 (因为 ReLU 会把小於 0 的都丢掉)。

# 把输入分别丢进 sigmoid 跟 tanh, 然後把结果乘起来
def gate(x): 
  res_sigmoid = sigmoid(x)
  res_tanh = tanh(x)
  res = Multiply()([res_sigmoid, res_tanh]) 
  return res

WaveNet 的架构

如上图所示, 输入经过因果卷积之後分别进入由膨胀卷积跟 Gated Activation Unit 所组成的 Residual Block, 以及 skip-connections,最後把所有结果相加起来再过 softmax 就是输出结果。

 def residual_block(x,filters,kernel_size,rate): 
      x = dilated_conv(filters,kernel_size,rate)(x) 
      x = gate(x)        
      x = conv_1D(32,1)(x) # 最後那个 [1*1]
      return x

WaveNet 做 Vocoder

稍微了解 WaveNet 的架构之後,我们现在来谈谈怎麽用 WaveNet 做 vocoder

  • 前面我们提到了用条件分布的乘积来表示一段声音,那既然是条件分布就代表说我们还可以给他额外的条件,现在我给原本的公式加上额外的条件 h 重写之後如下

  • 输入的条件还可以有两种形式分别是:

    1. 全局条件:
      整个 model 都照条件去做, 加入之後 Gated Activation Unit 变成

    2. 局部条件:
      限定 model 在什麽时候才照条件去做,加入之後 Gated Activation Unit 变成

  • 在 WaveNet 局部条件的输入原本是一个 Transpose Conv 它在文字转语音 (TTS)的做法是先训练一个 CNN 的语音转文字模型 (h = F(y)) 然後再把它 Transpose 回来 (y = f(h)), 通过这个将文字转换成语音,但这样生出来的语音品质非常糟糕,所以不会直接使用,而是拿来当作局部条件的输入,而输入的 ground truth 就是真正的语音。
    而这边的 Vf,k*y 就是 1X1 Conv 它是拿来取代 Transpose 的,经过实验证明效果比较好。

  • 同 TTS 的原理,你只要把局部条件换成语音特徵 (mel) 它就变成 Vocoder 了。

  • 如果有兴趣想做看看的话,你可以在这里找到我完整的 DEMO (没有加条件的部分),这个 repo 适用在 tf2.0+ 。

  • Deep Mind 并没有释出他们官方的 Code,不过坊间有不少 implement (目前我看到的大多只做到全局条件),你可以在这里找到

小结 & 碎念

今天我们谈了 Vocoder 它是进行声音分析与合成的系统,也对 WaveNet 做了简略的介绍,其实 WaveNet 後来进化出许多快速的版本,像是 Parallel Wavenet 跟 WaveRNN,但我没研究这些版本有没有拿来训练 Vocoder; 而 Vocoder 除了 WaveNet 的作法之外也有很多其他的做法,之後会在做介绍,感谢您的收看 ~ 我们明天再见。

结果 meeting 延期, 多了一个礼拜,赚烂了 XD

/images/emoticon/emoticon09.gif/images/emoticon/emoticon13.gif/images/emoticon/emoticon14.gif/images/emoticon/emoticon22.gif/images/emoticon/emoticon28.gif


<<:  D6(9/6)-永丰金(2890)

>>:  [Angular] Day7. Content projection

Batch Processing (3-1) - MapReduce Reduce-Side Joins and Grouping

Reduce-Side Joins and Grouping 当 MapReuce Job 执行时,...

AI ninja project [day 22] 变分自动编码器 Variational Autoencoder

如果讲了自动编码器(AE), 却不介绍变分自动编码器(VAE), 感觉有点太可惜了。 听到生成演算法...

【PHP Telegram Bot】Day03 - 向 @BotFather 申请一只机器人吧!

讲了两天的赣话,今天终於要来实作啦! The BotFather The Botfather 这个...

Day32 ATT&CK for ICS - Inhibit Response Function(4)

T0838 Modify Alarm Settings 攻击者修改设备的警告功能设定(如直接停止警告...

Progressive Web App Shortcuts: 程序快速启动选单 (9)

什麽是 App Shortcuts App Shortcuts 提供程序快速启动选单,透过这个捷径用...