本文主要介紹3種模型,分別是前向注意力(Forward Attention,FA/FA+TA),逐步單向注意力(Stepwise Monotonic Attention,SMA)和FastSpeech2,前兩者都是要求注意力權重盡量保證單調向前。具體來說,假設某一解碼步上的注意力權重為:\([0,0.8,0.2,0]\),在求下一個解碼步的注意力權重時,對原始的query和key“比較”求得的注意力權重加個“系數”,這個系數是上一個注意力權重,加上上一個注意力權重右移一位,這個注意力權重的系數就是\([0,0.8,0.2,0]+[0,0,0.8,0.2]=[0,0.8,1,0.2]\),可以看到,這個注意力系數會讓上一個解碼步上“關注”的編碼狀態和下一個編碼狀態在本次解碼時更加受到關注,也就是本次解碼要不然停留在原地,要不然向前一步;FA+TA就是為注意力系數顯式加了一個向前向后的選擇,也就是計算這個注意力系數時,\(0.1\times [0,0.8,0.2,0]+0.9\times [0,0,0.8,0.2]=[0,0.08,0.74,0.18]\),這個多出來的\(0.1,0.9\)是通過一個額外的網絡學習得來,這個網絡稱之為“轉移代理”(Transition Agent,TA),轉移代理對向前、向后建模更為明確。注意到,轉移代理是對注意力權重的系數,再乘上一個系數,而SMA直接對注意力權重乘個向前或者向后的概率,就完事。FastSpeech1/2是另外一個思路,用一個單獨的網絡學習每個編碼狀態對應幾個解碼狀態。
Forward Attention in Sequence-to-Sequence Acoustic Modeling For Speech Synthesis
摘要
在語音合成中,音素序列和對應的聲學參數序列遵循着單調性原則,也就是說,在生成語音時音素和聲學參數都是同步向前的。該文提出了一種適用於該情形的注意力機制,在每一個解碼步上對注意力權重添加單調性條件。在每個時間步上,通過前向算法遞歸地計算注意力權重,於此同時,提出了一種轉移代理機制,在每個解碼步上幫助注意力機制決策是向前前進一個“音素”,還是停留在原地。
局部敏感注意力LSA
回顧Tacotron-2中的局部敏感注意力機制(Location Sensitive Attention,LSA)。帶有注意力機制的編解碼器將輸入序列映射到不同長度的輸出序列,編解碼器通常是由循環神經網絡(Recurrent Neural Networks,RNN)實現。編碼器通常將輸入序列\(t=[t_1,t_2,...,t_N]\)映射到隱狀態序列\(x=[x_1,x_2,...,x_N]\),解碼器利用編碼器隱狀態序列\(x\)生成輸出序列\(o=[o_1,o_2,...,o_N]\)。
在每一個解碼步上\(t\)上,注意力機制都對編碼器隱狀態進行“軟性地”選擇,將\(q_t\)記作第\(t\)個解碼步上的query,\(q_t\)通常是解碼器的隱狀態;\(\pi_t\in \{1,2,...,N\}\)可以看作一個類別隱變量,表示根據條件分布\(p(\pi|x,q_t)\)對編碼器隱狀態的選擇。那么上下文向量就可通過下式計算:
其中,\(y_t(n)=p(\pi_t=n|x,q_t)\)。LSA引入了卷積特征以穩定注意力的對齊,具體地,\(k\)個大小為\(l\)的卷積核卷積之前解碼步的注意力權重。將\(F\in R^{k\times l}\)記作卷積矩陣,則:
在實際的語音合成中,在編碼隱狀態和解碼隱狀態之間的對齊路徑\(\{\pi_1,\pi_2,...,\pi_T\}\)表示文本特征對應着多少個聲學特征。
前向注意力
將對齊路徑的概率分布記作:
將\(P\)記作對齊路徑,其保持着單調性和連續性,並且不跳過任何編碼器狀態。
前向變量\(\alpha_t(n)\)定義為對齊路徑\(\{\pi_0,\pi_1,...,\pi_t\}\in P\)上的總概率:
其中,\(\pi_0=1,\pi_t=n\)。也就是說,假設第0時間步必選中第1個編碼器隱狀態,第\(t\)時間步必選中第\(n\)個編碼器隱狀態。
注意,\(\alpha_t(n)\)由\(\alpha_{t-1}(n)\)和\(\alpha_{t-1}(n-1)\)計算而來:
並且,
保證\(\hat{\alpha}_t(n)\)歸一化,\(\hat{\alpha}_t(n)\)表示第\(n\)個編碼步上第\(t\)個解碼步的對齊權重,張量大小為[batch_size,encoder_times]
。並且利用\(\hat{\alpha}_t(n)\)代替注意力權重\(y_t(n)\)求取上下文向量:
前向注意力的算法流程如下:
對應的PyTorch實現:
class AttentionBase(torch.nn.Module):
"""Abstract attention class.
Arguments:
representation_dim -- size of the hidden representation
query_dim -- size of the attention query input (probably decoder hidden state)
memory_dim -- size of the attention memory input (probably encoder outputs)
"""
def __init__(self, representation_dim, query_dim, memory_dim):
super(AttentionBase, self).__init__()
self._bias = Parameter(torch.zeros(1, representation_dim))
self._energy = Linear(representation_dim, 1, bias=False)
self._query = Linear(query_dim, representation_dim, bias=False)
self._memory = Linear(memory_dim, representation_dim, bias=False)
self._memory_dim = memory_dim
def reset(self, encoded_input, batch_size, max_len, device):
"""Initialize previous attention weights & prepare attention memory."""
self._memory_transform = self._memory(encoded_input)
self._prev_weights = torch.zeros(batch_size, max_len, device=device)
self._prev_context = torch.zeros(batch_size, self._memory_dim, device=device)
return self._prev_context
def _attent(self, query, memory_transform, weights):
raise NotImplementedError
def _combine_weights(self, previsous_weights, weights):
raise NotImplementedError
def _normalize(self, energies, mask):
raise NotImplementedError
def forward(self, query, memory, mask, prev_decoder_output):
energies = self._attent(query, self._memory_transform, self._prev_weights)
attention_weights = self._normalize(energies, mask)
self._prev_weights = self._combine_weights(self._prev_weights, attention_weights)
attention_weights = attention_weights.unsqueeze(1)
self._prev_context = torch.bmm(attention_weights, memory).squeeze(1)
return self._prev_context, attention_weights.squeeze(1)
class ForwardAttention(AttentionBase):
"""
Forward Attention:
Forward Attention in Sequence-to-sequence Acoustic Modelling for Speech Synthesis
without the transition agent: https://arxiv.org/abs/1807.06736.
However, the attention with convolutional features should have a negative effect
on the naturalness of synthetic speech.
"""
def __init__(self, *args, **kwargs):
super(ForwardAttention, self).__init__(*args, **kwargs)
def reset(self, encoded_input, batch_size, max_len, device):
super(ForwardAttention, self).reset(encoded_input, batch_size, max_len, device)
self._prev_weights[:,0] = 1
return self._prev_context
def _prepare_transition(self, query, memory_transform, weights):
query = self._query(query.unsqueeze(1))
# W*q_t+V*x_n
energy = query + memory_transform
# energy: [batch_size,encoder_times]
energy = self._energy(torch.tanh(energy + self._bias)).squeeze(-1)
energy = F.softmax(energy, dim=1)
# shifted_weights: [batch_size,encoder_times]
# shifted_weights: [0;encoder_times[:-1]]
shifted_weights = F.pad(weights, (1, 0))[:, :-1]
return energy, shifted_weights
def _attent(self, query, memory_transform, cum_weights):
energy, shifted_weights = self._prepare_transition(query, memory_transform, self._prev_weights)
# \alpha_t(n)=(\alpha_{t-1}(n)+\alpha_{t-1}(n-1))y_t(n)
self._prev_weights = (self._prev_weights + shifted_weights) * energy
return self._prev_weights
def _normalize(self, energies, mask):
energies[~mask] = float(0)
return F.normalize(torch.clamp(energies, 1e-6), p=1)
def _combine_weights(self, previous_weights, weights):
return weights
https://github.com/Tomiinek/Multilingual_Text_to_Speech/blob/master/modules/attention.py
帶有對齊代理的前向注意力
所謂的對齊代理,其實就是引入了一個指示器,指示在第\(t\)個解碼步上移動到下一個“音素”上的概率,這個指示器由一個全連接層和Sigmoid層組成,輸入為上下文向量\(c_t\)、解碼器輸出\(o_{t-1}\)和query。
可以看到,相比於前向注意力,算法中就是多了一個概率值\(u\),用於控制“注意力”的移動,每一步都根據本時刻的上下文向量、上一時刻的解碼器輸出以及query計算下一個時刻的概率值。
這可以看作是一個專家產品(Product-of-Experts,PoE)模型,專家產品模型由若干個獨立的部分組成,結果由各個組件共同決定。在該文提出的方法中,一個組件\((1-\mu_{t-1})\hat{\alpha}_{t-1}(n)+\mu_{t-1}\hat{\alpha}_{t-1}(n-1)\)描述了單調性對齊的限制,另一個組件\(y_t(n)\)描述了原始的對齊方式,最終的注意力權重\(\hat{\alpha}_t(n)\)的計算由兩者共同決定。
與此同時,該文的注意力對齊機制還可以控制音頻速度。在生成時向轉移代理DNN的Sigmoid輸出中添加正或負的偏置值,轉移概率\(u_t\)就會增加或者減小,這就會導致注意力移動的速度變快或者變慢,從而導致生成語音變快或變慢。
class ForwardAttentionWithTransition(ForwardAttention):
"""
Forward Attention:
Forward Attention in Sequence-to-sequence Acoustic Modelling for Speech Synthesis
with the transition agent: https://arxiv.org/abs/1807.06736.
Arguments:
decoder_output_dim -- size of the decoder output (from previous step)
"""
def __init__(self, decoder_output_dim, representation_dim, query_dim, memory_dim):
super(ForwardAttentionWithTransition, self).__init__(representation_dim, query_dim, memory_dim)
self._transition_agent = Linear(memory_dim + query_dim + decoder_output_dim, 1)
def reset(self, encoded_input, batch_size, max_len):
super(ForwardAttentionWithTransition, self).reset(encoded_input, batch_size, max_len)
self._t_prob = 0.5
return self._prev_context
def _attent(self, query, memory_transform, cum_weights):
energy, shifted_weights = self._prepare_transition(query, memory_transform, self._prev_weights)
# use last time u_{t-1}
self._prev_weights = ((1 - self._t_prob) * self._prev_weights + self._t_prob * shifted_weights) * energy
return self._prev_weights
def forward(self, query, memory, mask, prev_decoder_output):
# `self._prev_context` should be equal to `context` in `forward()` of `AttentionBase`
context, weights = super(ForwardAttentionWithTransition, self).forward(query, memory, mask, None)
transtition_input = torch.cat([self._prev_context, query, prev_decoder_output], dim=1)
t_prob = self._transition_agent(transtition_input)
self._t_prob = torch.sigmoid(t_prob)
return context, weights
https://github.com/Tomiinek/Multilingual_Text_to_Speech/blob/master/modules/attention.py
實驗
由上圖可以看到,生成失敗的情形顯著減少。
Robust Sequence-to-Sequence Acoustic Modeling with Stepwise Monotonic Attention for Neural TTS
摘要
該文同樣是利用語音合成的單調特性,強制輸入輸出之間不僅僅單調,而且不能跳過輸入的每一個時間步。軟性注意力可以用來消除訓練和推斷之間的不一致性。
相關工作
普通注意力
首先還是回顧一下最通用的注意力機制:
其中,\(h_j\)為第\(j\)個編碼步的輸出,\(s_{i-1}\)為上一時刻\(i-1\)解碼器隱狀態,\(e_{i,j}\)用於衡量\(s_{i-1}\)和\(h_j\)之間的“相似度”,\(c_i\)是對編碼向量進行加權求和,作為第\(i\)步注意力的輸出。
單調注意力(Monotonic attention)可以同時保持單調性和局部性,可以應用到包括語音合成領域中:在每個解碼步\(i\)上,該機制都會檢查上一個解碼步選擇的memory索引\(t_{i-1}\),從“音素位置”\(j=t_{i-1}\)開始,采樣伯努利分布\(z_{i,j}\sim Bernoulli(p_{i,j})\)決定需要保持\(j\)不動,還是移動到下一個位置\(j\leftarrow j+1\)上。“音素位置”\(j\)會一直向前移動,直到到達輸入末尾或者收到采樣值\(z_{i,j}=1\)。當音素位置\(j\)停止時,該位置上對應的memory,即\(h_j\)將會被直接被作為上下文向量\(c_i\)。可以用下式遞歸計算\(\alpha_{i,j}\):
或者用下式並行計算:
之后就可以像正常的注意力機制一樣,利用注意力權重\(\alpha_{i,j}\)或者\(\alpha_i\)求得上下文向量\(c_i\)了。
注1:
- 伯努利分布Bernoulli
cumprod
和cumsum
運算符
以向量\(A=[1,2,3,4]\)為例,
cumprod
累積乘積
向量\(\mathop{cumprod}(A)\)第一位元素為1:
向量\(\mathop{cumprod}(A)\)第二位元素為2:
向量\(\mathop{cumprod}(A)\)第三位元素為6:
向量\(\mathop{cumprod}(A)\)第四位元素為24:
cumprod
累加求和
硬性單調注意力
在最通用的注意力機制中,利用\(\alpha_{i,j}\)計算\(c_i\)的時候,沒有使用采樣,而是加權求期望,這樣能使得訓練過程可以反向傳播。
在軟性注意力中,對同一時刻\(i\),即使\(h_j\)(\(j=1,2,...,T\))是天然有序的,但是在計算\(\alpha_{i,j}\)的時候,\(h_j\)是無序的。即使打亂\(h_j\)的順序,每個\(h_j\)對應的注意力權重值\(\alpha_{i,j}\)保持不變。並且對於不同時刻對齊到的\(h_j\)並不是單調的,本時刻對齊到\(h_j\),並不影響注意力下次可能就對齊到\(h_{j-1}\),或者\(h_{j-3}\) 😦,這並不符合語音合成的注意力特性。
硬性單調注意力能夠保證在計算\(\alpha_{i,j}\)時在編碼步\(j\)上是有序的,且讓\(\alpha_{i,j}\)在解碼步上是單調的。主要思路是,對任意一個時刻\(i-1\),關注且僅關注一個編碼隱狀態(attend and only attend to one hidden state),記作\(h_j\)。在每一個解碼步,計算概率\(p_{i,j}\),決定當前解碼步是繼續關注原先的編碼隱狀態\(h_j\),還是跳到下一個\(h_{j+1}\)。
\(e_{i,j}\)的計算和普通的軟性注意力(Soft Attention)是一樣的,都是求query和每個編碼隱狀態之間的“相似度”,但是和普通的軟性注意力不一樣的是,得到\(e_{i,j}\)之后並沒有對解碼步\(i\)上的所有\(j\)進行Softmax,而是獨立進行Sigmoid,得到一個概率值\(p_{i,j}\)。當概率\(p_{i,j}\)的伯努利采樣值\(z_{i,j}=1\),則關注原先的編碼隱狀態\(h_j\),否則\(z_{i,j}=0\)則前進一個編碼隱狀態\(h_{j+1}\)。如果前進一步(\(z_{i,j}=0\)),在前進之后再次計算\(z_{i,j+1}\),直到\(z_{i,j'}=1\),停止前進,此時得到上下文向量\(c_i=h_{j'}\)。有了上下文向量\(c_i\)之后就可以按照普通的軟性注意力里的方法,計算\(s_i\)和\(y_i\)。接着計算下一個上下文向量\(c_{i+1}\),此時需要從\(h_{j'}\)開始,重復上述過程。
訓練
公式1介紹了硬性單調注意力的方法,但是這里有一個從伯努利分布的采樣操作,采樣操作是沒辦法反向傳播的。解決方法和普通的軟性注意力一樣,使用\(c_i=\sum_{j=1}^T\alpha_{i,j}h_j\)代替原來直接的\(h_j\),但是硬性單調注意力中的\(h_j\)時通過一步一步前進獲得的,而求期望則是一個加權求和的過程。那么求每一解碼步上聯合概率\(\alpha_{i,j}\)的方法如下:
-
當\(i=1\)時,如果是關注\(h_j\),此時對應的概率為\(p_{i,j}\),且\(k=1,2,...,j-1\)都被跳過了,對應的概率為\(\prod_{k=1}^{j-1}(1-p_{1,k})\),則該事件的聯合概率為\(\alpha_{1,j}=p_{i,j}\prod_{k=1}^{j-1}(1-p_{1,k})\)
-
當\(i>=2\)時,假設\(i-1\)解碼步選中了\(h_k\),那么從時刻\(1\)到時刻\(i-1\)選中\(k\)這總共\(i-1\)個事件的聯合概率即為\(\alpha_{i-1,k}\);又假設\(i\)時刻選中了\(h_j\),概率為\(p_{i,j}\),那么說明\(h_k,h_{k+1}...,h_{j-1}\)都應該被跳過,概率為\(\prod_{l=k}^{j-1}(1-p_{i,l})\)
因此整個聯合概率模型為:
\[\alpha_{i,j}=p_{i,j}\sum_{k=1}^{j}(\alpha_{i-1,k}\prod_{l=k}^{j-1}(1-p_{i,l}))\tag{2} \]上式中的\(\alpha_{i,j}\)中,外層的\(\sum\)上界中的\(k\)最多可以到\(j\),此時內層的\(\prod\)變為\(\prod_{j}^{j-1}\);定義這種起始點大於終點的累乘結果為1。在這種情形下是正確的,因為當\(k=j\)時,說明\(i\)時刻和\(i-1\)時刻都選中了\(h_j\),概率為\(p_{i,j}(\alpha_{i-1,j}\prod_{l=j}^{j-1}(1-p_{i,l}))=p_{i,j}\alpha_{i-1,j}\)。利用該式對公式2的\(\alpha_{i,j}\)進行化簡。
化簡方法1
首先對公式2從外層的求和中分離出\(k=j\),則
從連乘符號中分離出\(l=j-1\),並且利用\(\prod_{l=j}^{j-1}(1-p_{i,l})=1\),那么,
把上式中的\((1-p_{i,j-1})\)從內部的連乘和求和符號中拿出來,畢竟已經不和\(l,k\)有關了。變為:
對比一下上面的那個聯合概率公式,也就是公式2,把公式2等號右側的\(p_{i,j}\)除到左邊,那么,
和公式4相比,上面公式3的連乘和求和符號部分,就是上標由\(j\)變為了\(j-1\)。把上式3等號右側的\(p_{i,j}\)除到左邊,公式3就變成了:
結合公式4,將求和符號包圍的部分替換掉,公式5變為:
令\(q_{i,j}=\frac{\alpha_{i,j}}{p_{i,j}}\),則上式6可以簡寫為:
到此,將\(\alpha_{i,j}\)的遞推公式化簡完畢。
化簡方法2
現在利用另一種方法化簡\(\alpha_{i,j}\)。將公式2內側的連乘\(\prod_{l=k}^{j-1}(1-p_{i,l})\)變換為\(\frac{\prod_{l=0}^{j-1}(1-p_{i,l})}{\prod_{l=0}^{k-1}(1-p_{i,l})}\),使得分子與\(k\)無關,那么分子就可以從外層的求和中分離出來,上式2變為
令\(q_i=\frac{\alpha_{i,j}}{p_{i,j}}\),將\(p_{i,j}\)除到等號左側,則
則,
令\(q_{i,j}=\frac{\alpha_{i,j}}{p_{i,j}}\),上式中的\(p_{i,j}\)除到左側,簡化之后的上式:
可以看到,和上面的化簡方法1得到的結果一致。
整理一下上述結果:
逐步單調注意力(Stepwise monotonic attention)
逐步單調注意力(Stepwise monotonic attention)在單調注意力(Monotonic attention)的基礎上添加額外的限制:在每一個解碼步,硬對齊位置應該最多移動一步。
在每一個解碼步\(i\)上,該機制探測上一步使用的memory條目\(j=t_{i-1}\),僅需要決定是向前或者停止不動。因此可以直接建立\(p_{i,j}\)分布的遞推關系:
同樣地,可以更為高效地並行計算:
其中,\([0;]\)表示左側填充0。
在訓練階段,上下文向量和普通的注意力機制類似。但是可以看到,無論是單調注意力,或是逐步單調注意力,在訓練和推斷階段的上下文向量存在不匹配的現象,在訓練階段,送入解碼器中的輸入是一個“軟性”上下文向量而非單一的memory,也就是說在訓練階段,解碼器可以利用將來要注意的memory而不是基於當前的memory來預測聲學特征。當然,該文建議在推斷階段仍然使用軟性注意力,只不過結合上式中求\(\alpha_{i,j}\)的方法,然后對memory進行加權求和。不過如果這樣做的話,就沒辦法流式合成語音了。
實驗
實驗中比較了5種基於Tacotron的語音合成模型,分別是:
-
基線Tacotron,采用原始的位置敏感注意力機制(Location Sensitive Attention),記作
Baseline
; -
混合高斯注意力機制(GMM Attention),20個高斯成分,記作
GMM
; -
單調注意力(Monotonic Attention),在推斷時采用硬性或軟性注意力,記作
MA hard
和MA soft
; -
前向注意力(Forward Attention),使用或者不使用轉移代理,記作
FA+TA
和FA w/o TA
; -
逐步單調注意力(Stepwise Monotonic Attention),推斷時使用硬性或軟性注意力,記作
SMA hard
和SMA soft
。
與基線Tacotron的偏向性測試
可以看到,SMA
優於Baseline
,具體來說SMA soft
又優於SMA hard
。
與軟性逐步單調注意力(Soft Stepwise Monotonic Attention)推斷的偏向性測試
可以看到,SMA soft
顯著優於GMM
,但是比FA+TA
還差是什么鬼。
FastSpeech 2: Fast and High-quality End-to-End Text to Speech
摘要
相比於FastSpeech,
-
通過真實目標訓練模型,而不是教師模型生成的簡化版目標;
-
顯式建模語音中的時長、音調和能量,在訓練時直接將真實語音中提取的這些特征作為條件輸入,在推斷時將預測特征作為條件輸入;
-
提出了FastSpeech 2s,直接從文本映射為語音,不適用頻譜作為中間媒介,提升生成速度,完全端到端並行生成。
相關工作
FastSpeech是一種非自回歸的語音合成方法,利用自回歸的教師模型提供:1)音素時長以訓練時長預測模型;2)生成頻譜進行知識蒸餾。但是FastSpeech存在一些問題,比如教師-學生知識蒸餾流程耗費過多時間,從教師模型中提取的注意力權重不夠精確。此外,將教師模型的生成目標作為目標損失了訓練數據中音調、能量、韻律等方面的多樣性信息,生成目標要比真實錄音簡單且“單調”。
語音合成是一種典型的一對多問題,因為語音中比如音調、時長、音量和韻律等變化,同一段文本可以對應任意多的語音。
如何去解決這種一對多映射問題呢?一個方法就是FastSpeech那樣,引入一個教師模型,利用教師模型生成目標和注意力權重,去除變動的部分,簡化原始語料中存在的數據偏差;第二個就是FastSpeech 2中采用的方法,將這些易於變動的部分直接剝離開,利用一個單獨的預測器生成變動,產生方差(variation)。
為了解決上述問題,FastSpeech 2直接利用原始語音進行訓練,使用從原始的目標音頻中抽取的時長、音調和能量作為額外輸入,並且利用這些額外輸入訓練對應的預測器。在推斷時,直接使用預測的特征作為額外輸入,生成語音。考慮到音調對語音比較重要,並且過於多變難以建模,因此利用連續小波變換(Continuous Wavelet Transform,CWT)將音調包絡轉換為音調譜(Pitch Spectrogram)。總結下來,FastSpeech 2的優勢有:
-
移除了教師-學生知識蒸餾機制,簡化訓練流程;
-
使用真實目標而非生成目標,減少信息損失;
-
顯式建模語音中易於變動的特征比如音調、時長、能量等,減輕一對多映射的難題,並且直接從原始語音中提取這些特征,訓練時更為精確。
FastSpeech 2/2s
整個FastSpeech 2/2s結構如下圖所示,編碼器將音素序列編碼到音素隱狀態序列,利用方差適配器(Variance Adaptor)向隱狀態序列中添加額外的方差信息,比如時長、音調、能量等,之后FastSpeech 2/2s利用解碼器將多信息混合的隱狀態序列轉換為梅爾頻譜或語音。
將語音中易於變動的信息總結如下:
-
音素時長,影響語音長度;
-
音調,是影響語音情感和韻律的重要特征;
-
能量,梅爾頻譜的幅度,直接影響語音的音量。
實際上可以將語音中更多易於變動的部分分離開來,單獨建模,比如情感、風格和說話人等等。在該文中僅單獨建模上述三個特征,模型中對應着三個預測器。在訓練時,使用從錄音文本中提取的真實時長,聲調和能量值作為目標去訓練時長、音調和能量預測器。
時長預測器
時長預測器輸入音素隱狀態,輸出每一個音素的時長,表示有多少梅爾頻譜幀對應着這個音素。使用平均方差(Mean Square Error,MSE)作為損失函數,抽取MFA(Montreal Force Alignment)抽取的時長作為目標值。
音調預測器
由於真實音調中的高方差,預測的音調值和真實音調值分布有比較大的不同,為了更好地對音調包絡的變化進行建模,使用連續小波變換(Continuous Wavelet Transform,CWT)將連續的音調序列分解為音調譜(Pitch Spectrogram),並且將音調譜作為音調預測器的目標值,同樣使用平均方差MSE作為損失函數。在該文中,將每一幀的音調\(F_0\)量化為對數域的256個值,之后做嵌入,將其轉換為對數嵌入向量\(p\),將其加入到擴展隱向量序列。
能量預測器
計算每一個短時傅里葉變換幀能量的L2范數作為能量,之后將每一幀的能量量化到256個值,同樣做嵌入,將其轉化為能量嵌入向量\(e\),像音調一樣加入到擴展隱向量序列中。注意,使用能量預測器直接預測原始的能量值,而非量化之后的值,並且使用平均方差MSE作為損失函數。而不是像音調一樣,對能量包絡進行連續小波變換的主要原因是能量不像音調一樣,有那么大的變化,對能量進行連續小波變換之后,也觀察不到任何的提升。
FastSpeech 2s
FastSpeech 2s使用中間隱狀態而非頻譜直接生成語音,使得整個語音合成模型更加緊湊簡潔。之前的工作不直接對語音樣本點進行建模的主要原因是,相比於頻譜,語音波形更加富有變化;並且大量語音樣本點的建模,對有限的GPU顯存而言,是一個比較大的挑戰。
在FastSpeech 2s中,語音解碼器采用對抗訓練的方式,使用類似於Parallel WaveGAN(PWG)中的判別器,而生成器輸入一小段隱狀態序列,然后上采樣。
實驗
語音質量
可以看到,合成語音的平均意見得分(Mean Opinion Score,MOS)還是相當高的,但是FastSpeech 2s直接建模語音波形,語音質量稍差。
生成速度
FastSpeech 2的訓練速度都比較快,推斷速度FastSpeech 2/2s都挺快,超過了實時。