下面這支影片我們可以看到那時候的聲碼器是一台大型機器,在影片 39 秒的地方你可以聽聽那段慘不忍賭的合成人聲,但在那個時候卻是奇蹟。
如果你很有興趣想知道那台老古董是怎麼發出聲音的話,你可以看一下這篇文章的介紹
然而我們不會用 WaveNet_Vocoder 來做,因為它實在是太慢了。
想像一下不管是圖片或是聲音,下一個元素的產生或多或少都與前一個元素有關,在圖片上的話最直觀的理解就是漸層。
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
如上圖所示, 輸入經過因果捲積之後分別進入由膨脹捲積跟 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 的架構之後,我們現在來談談怎麼用 WaveNet 做 vocoder
輸入的條件還可以有兩種形式分別是:
全局條件:
整個 model 都照條件去做, 加入之後 Gated Activation Unit 變成
局部條件:
限定 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