網絡結構
首先輸入數據,進行一維的因果卷積,然後進入到殘差塊中,殘差塊是這樣的結構:將數據再進行一次空洞卷積,分兩路,一路是用tanh()做激活函數,一路是用sigmoid做激活函數,最後又將兩路合併,合併完成後,我們在經過一個一維的膨脹卷積,這裏得到的輸出,我們又會進行兩路處理,一路是進入下一次的殘差塊,一路是往右邊發展,經過一個relu激活,一個一維空洞卷積,一個relu,一個一維空洞卷積,再接着softmax ,最後得到輸出。
def _create_network(self, input_batch, global_condition_batch):
'''Construct the WaveNet network.'''
outputs = []
current_layer = input_batch
#殘差塊前面的因果卷積
# Pre-process the input with a regular convolution
current_layer = self._create_causal_layer(current_layer)
output_width = tf.shape(input_batch)[1] - self.receptive_field + 1
#構建dilated convolution
# Add all defined dilation layers.
with tf.name_scope('dilated_stack'):
for layer_index, dilation in enumerate(self.dilations):
with tf.name_scope('layer{}'.format(layer_index)):
output, current_layer = self._create_dilation_layer(
current_layer, layer_index, dilation,
global_condition_batch, output_width)
outputs.append(output)
with tf.name_scope('postprocessing'):
# postprocess the output.
w1 = self.variables['postprocessing']['postprocess1']
w2 = self.variables['postprocessing']['postprocess2']
if self.use_biases:
b1 = self.variables['postprocessing']['postprocess1_bias']
b2 = self.variables['postprocessing']['postprocess2_bias']
if self.histograms:
tf.histogram_summary('postprocess1_weights', w1)
tf.histogram_summary('postprocess2_weights', w2)
if self.use_biases:
tf.histogram_summary('postprocess1_biases', b1)
tf.histogram_summary('postprocess2_biases', b2)
total = sum(outputs) #跳躍連接
#(+) -> ReLU -> 1x1 conv -> ReLU -> 1x1 conv
transformed1 = tf.nn.relu(total)
conv1 = tf.nn.conv1d(transformed1, w1, stride=1, padding="SAME")
if self.use_biases:
conv1 = tf.add(conv1, b1)
transformed2 = tf.nn.relu(conv1)
conv2 = tf.nn.conv1d(transformed2, w2, stride=1, padding="SAME")
if self.use_biases:
conv2 = tf.add(conv2, b2)
return conv2
1.dilated casual convolution
因果卷積casual convolution:在t時刻的預測不會依賴於任何一個未來時刻的數據
空洞卷積dilated convolution:跳過部分輸入
使用因果卷積,需要考慮很久之前的輸入,需要增加捲積層,卷積層數的增加就帶來:梯度消失,訓練複雜,擬合效果不好。因此使用空洞卷積在相同的層數下獲得更大的感受野。
2.softmax distribution
音頻的使用16bit進行存儲,意味着沒個timestep需要輸出65536個值,使用進行壓縮量化到256個值。
3.門控激活單元
實驗表明音頻信號模型中,非線性激活函數比線性激活函數好。
4.殘差連接和跳躍連接
緩解因模型過深導致的梯度消失問題。
殘差連接可以形象理解爲怕忘記很早之前的事,然後寫個便條直接提醒自己那件事。
跳躍連接在網絡中表現爲將幾個殘差塊的輸出加權進行相同處理後得到輸出。
5.條件建模
通過條件分佈建模,接受額外的輸入h,我們可以指導WaveNet生成具有目標特點的音頻。
實驗中,WaveNet的條件建模有全局建模和局部建模兩種。
全局條件指可以影響整個timesteps的輸出分佈。局部條件指們有第二種時間序列,可以通過對原始數據的低採樣率獲得(比如TTS模型中的線性特徵)。我們首先通過transposed convolutional network(上採樣)將時間序列轉換成和語音序列一樣分辨率的新的時間序列。然後將其用於激活單元。
6.context stacks
也是爲了增加感受野,具體做法是使用一個獨立的更小的上下文堆棧來處理語音信號的長跨度信息。
實驗
WaveNet利用真實的人類聲音剪輯和相應的語言、語音特徵來訓練其網絡,讓其能夠辨別這兩方面(語言和語音)的音頻模式。
實驗1和實驗3類似,使用音頻進行訓練,不基於文本或者樂譜,生成的時候,網絡想生成生成什麼就生成什麼,可能會生成非人類的語言,但是在音樂生成中可能別有一番風味。不過實驗1會另外輸入說話人的id,在訓練的時候也是以id進行條件建模。
1.多說話人語音生成
數據集:VCTK(Yamagishi, 2012)數據集中的英文多人語料
輸入:說話人的id的one-hot編碼
輸出:音頻
2.TTS
數據集:Google’s North American English and Mandarin Chinese TTS systems的語音數據集
輸入:文本的linguistic features
輸出:音頻
TTS實驗中需要搭配一個前端處理器來獲取文本的語言學特徵(可以是語譜圖),Tacotron2的的思路就是如此,Tacotron2得到梅爾頻譜圖再送入WaveNet得到音頻。
3.音樂生成
數據集:the Magna TagATune dataset、the YouTube piano dataset
輸入:無(實驗中用的非條件建模)
輸出:音頻
4.語音識別
數據集:TIMIT
這個實驗室對網絡進行一定的改進,在dilated convolution層後面增加了mean-pooling層,讓輸出聚集成10毫秒的帶寬。pooling層後面接了幾層非因果卷積層。我們採用兩類loss來訓練wavenet。一類loss爲預測下一個樣本;另外一類loss爲對該frame進行分類。
這個實驗可以看看下文資料鏈接中的Blog,用thcs30數據進行訓練的,我跑了一下,驗證集的loss值最終維持在45-50之間,效果不是特別好,針對中文數據集,可能需要對WaveNet一定改動。
資料
原論文,官方代碼,官方Blog
筆記
源碼解讀
論文翻譯
基於WaveNet的自動語音識別系統
WaveNet介紹
合集