論文地址:《MelGAN: Generative Adversarial Networks for Conditional Waveform Synthesis》
官方源碼:github地址
簡介
常見的TTS系統不是直接生成音頻,而是中間先生成一種聲學特徵(多數爲Mel頻譜圖),再由聲學特徵生成音頻。MelGAN解決的就是聲學特徵(Mel譜圖)->音頻問題,也就是常說的聲碼器部分。
目前Mel頻譜–>音頻的方法主要可以分爲三類:
①信號處理方式:
常見有:Tacotron中使用的Griffin-Lim算法;Char2Wav中使用的WORLD聲碼器
缺點:需要引入明顯的僞像(準確度不夠)
②自迴歸網絡
常見有:WaveNet(Tacotron2中作爲聲碼器);SimpleRNN;WaveRNN
缺點:必須自下而上產生音頻,效率低(速度不夠)
③非自迴歸網絡
常見有:Parallel WaveNet;ClariNet;WaveGlow
缺點:訓練成本較大
MelGAN的優點:
- MelGAN是一種非自迴歸前饋卷積架構,是第一個由GAN去實現原始音頻的生成,在沒有額外的蒸餾和感知損失的引入下仍能產生高質量的語音合成模型。
- MelGAN解碼器可替代自迴歸模型,以生成原始音頻。
- MelGAN的速度明顯快於其他Mel譜圖轉換到音頻的方法,在保證音頻質量沒有明顯下降的情況下比迄今爲止最快的可用模型快10倍。
網絡細節
生成器
輸入:Mel譜圖 輸出:音頻
總體結構
其中殘差塊結構:
經過一層Conv層後送到上採樣網絡塊,上採樣網絡塊一共有4個,依次爲8x,8x,2x,2x,每個上採樣網絡塊中嵌套殘差塊,每個殘差塊有三層,依次dilation爲1,3,9,最後經過一層conv層得到音頻輸出,由於音頻的channel表示爲1,所以最後一層的channel設爲1。
源碼分析
#對一維卷積層進行Weight Norm
def WNConv1d(*args, **kwargs):
return weight_norm(nn.Conv1d(*args, **kwargs))
#對一維反捲積層進行Weight Norm
def WNConvTranspose1d(*args, **kwargs):
return weight_norm(nn.ConvTranspose1d(*args, **kwargs))
#殘差塊中一層的結構
class ResnetBlock(nn.Module):
def __init__(self, dim, dilation=1):
super().__init__()
#依次爲兩層卷積層
self.block = nn.Sequential(
nn.LeakyReLU(0.2),
nn.ReflectionPad1d(dilation),
WNConv1d(dim, dim, kernel_size=3, dilation=dilation),
nn.LeakyReLU(0.2),
WNConv1d(dim, dim, kernel_size=1),
)
self.shortcut = WNConv1d(dim, dim, kernel_size=1)
def forward(self, x):
return self.shortcut(x) + self.block(x) #殘差連接
#生成器
class Generator(nn.Module):
def __init__(self, input_size, ngf, n_residual_layers):
super().__init__()
ratios = [8, 8, 2, 2]
self.hop_length = np.prod(ratios)
mult = int(2 ** len(ratios))
#第一層卷積
model = [
nn.ReflectionPad1d(3),
WNConv1d(input_size, mult * ngf, kernel_size=7, padding=0),
]
# 上採樣階段,共4個,依次爲8x,8x,2x,2x的UpSampling layer
for i, r in enumerate(ratios):
model += [
nn.LeakyReLU(0.2),
WNConvTranspose1d(
mult * ngf,
mult * ngf // 2,
kernel_size=r * 2,
stride=r,
padding=r // 2 + r % 2,
output_padding=r % 2,
),
]
#加入殘差塊,每個殘差塊中有3層,dilation分別爲1,3,9
for j in range(n_residual_layers):
model += [ResnetBlock(mult * ngf // 2, dilation=3 ** j)]
mult //= 2
#最後一層卷積層
model += [
nn.LeakyReLU(0.2),
nn.ReflectionPad1d(3),
WNConv1d(ngf, 1, kernel_size=7, padding=0),
nn.Tanh(),
]
self.model = nn.Sequential(*model)
self.apply(weights_init)
def forward(self, x):
return self.model(x)
設計思路
1.Mel頻譜圖的時間分辨率比原始音頻低256倍,所以使用了堆疊的反捲積層進行unsample。
2.條件信息足夠的情況下,在輸入處增加噪聲是不必要的。所以與傳統GAN不同,笨笨並沒有增加noise input
3.使用殘差塊解決梯度消散的問題,空洞卷積層的感受野隨層數的增加而指數增加,能夠有效地增加每個輸出時間步長的感應野。
4.反捲積層的k-size和stride仔細選擇決定的,可以減少artifacts的出現。
5.歸一化選擇Weight Norm,因爲不會限制判別器的空間,也不會對激活進行歸一化
判別器
輸入:音頻 輸出:feature Map
總體結構
判別器,採用多尺度架構,除了對原始音頻做判別,還對原始音頻做降頻處理(採用Avg pooling方式)後再饋送到判別器下進行判別,共有3個Discriminator Block。Discriminator Block是有卷積層和下采樣層組成。
基於多尺度架構的判別器,是爲了保證捕獲音頻中的高頻結構,而且具有較少的參數,運行速度更快還可以應用於可變長度的音頻序列。
源碼分析
#對一維卷積層進行Weight Norm
def WNConv1d(*args, **kwargs):
return weight_norm(nn.Conv1d(*args, **kwargs))
#對一維反捲積層進行Weight Norm
def WNConvTranspose1d(*args, **kwargs):
return weight_norm(nn.ConvTranspose1d(*args, **kwargs))
#Discriminator Block結構
class NLayerDiscriminator(nn.Module):
def __init__(self, ndf, n_layers, downsampling_factor):
super().__init__()
model = nn.ModuleDict()
#第一層卷積
model["layer_0"] = nn.Sequential(
nn.ReflectionPad1d(7),
WNConv1d(1, ndf, kernel_size=15),
nn.LeakyReLU(0.2, True),
)
nf = ndf
stride = downsampling_factor
#4層4x Downsampling Layer
for n in range(1, n_layers + 1):
nf_prev = nf
nf = min(nf * stride, 1024)
model["layer_%d" % n] = nn.Sequential(
WNConv1d(
nf_prev,
nf,
kernel_size=stride * 10 + 1,
stride=stride,
padding=stride * 5,
groups=nf_prev // 4,
),
nn.LeakyReLU(0.2, True),
)
nf = min(nf * 2, 1024)
#第2層卷積層
model["layer_%d" % (n_layers + 1)] = nn.Sequential(
WNConv1d(nf_prev, nf, kernel_size=5, stride=1, padding=2),
nn.LeakyReLU(0.2, True),
)
#第3層卷積層
model["layer_%d" % (n_layers + 2)] = WNConv1d(
nf, 1, kernel_size=3, stride=1, padding=1
)
self.model = model
def forward(self, x):
results = [] #存放每層輸出的feature map
for key, layer in self.model.items():
x = layer(x)
results.append(x)
return results
#完整的判別器
class Discriminator(nn.Module):
def __init__(self, num_D, ndf, n_layers, downsampling_factor):
super().__init__()
self.model = nn.ModuleDict()
#3個Discriminator Block
for i in range(num_D):
self.model[f"disc_{i}"] = NLayerDiscriminator(
ndf, n_layers, downsampling_factor
)
#downsample函數
self.downsample = nn.AvgPool1d(4, stride=2, padding=1, count_include_pad=False)
self.apply(weights_init)
def forward(self, x):
results = []
for key, disc in self.model.items():
results.append(disc(x)) #每次降頻處理得到的結果依次存放到result
x = self.downsample(x) #對輸入x進行降頻處理
return results
損失函數
化簡爲
表示音頻,表示mel譜圖,表示高斯噪聲
加入Feature Matching損失,這一損失除了優化判別器,也優化了生成器,使真實和合成音頻的判別器特徵圖之間的距離最小。
KaTeX parse error: No such environment: equation at position 8:
\begin{̲e̲q̲u̲a̲t̲i̲o̲n̲}̲\mathcal{L}_{\m…
最終損失函數爲
實驗結果
1.TTS
將MelGAN作爲TTS系統的聲碼器部分,法案現MelGAN的效果跟WaveGlow差不多,但是MelGAN的速度會快很多。
2.音頻重構
MelGAN VQ-VAE:將VQ-VAE中的聲碼器替換爲MelGAN,在無條件下實現了VQ-VAE這種無條件的音頻重構。網絡框架圖如下。