3. 数据处理
3.1 数据集
一个单人,一个多人语音,详细介绍在:
单人-[LJSpeech-1.1](https://blog.csdn.net/disanda/article/details/139890868?spm=1001.2014.3001.5501)
多人-[VCTK](https://blog.csdn.net/disanda/article/details/139890912)
3.2 meldataset.py
- 归一化参数 (MAX_WAV_VALUE)
MAX_WAV_VALUE = 32768.0
# WAV 文件的最大幅度值。标准的 16 位 PCM 音频格式的范围是 -32768 到 32767,
# MAX_WAV_VALUE用来将音频归一化到 -1 到 1 之间。
- get_dataset_filelist文件操作函数
获取到是一个文件夹对象, 分别读取训练和测试集的音频文件路径,并返回这些路径的集合。
- 信号压缩
动态范围压缩函数,将输入的信号 x 进行压缩,避免信号范围过大。
通过log压缩信号值,通过exp还原
def dynamic_range_compression(x, C=1, clip_val=1e-5):
return np.log(np.clip(x, a_min=clip_val, a_max=None) * C)
# C是压缩系数,clip为裁剪函数,保证值在 [clip_val, +$\infty$]内
def dynamic_range_decompression(x, C=1):
return np.exp(x) / C
# 动态范围解压缩函数,用于恢复被压缩的信号。
3.3 mel_spectrogram函数
用torch.stft(), 将音频信号转换为mel谱。
- 短时傅里叶变换STFT(Short-Time Fourier Transform)
将信号分成多个小片段,对每个片段进行傅里叶变换。
n_fft是片段长度,hop_size 是片段之间的距离。
例如:
1. 初始片段:
* 从 0 到 2047。
2. 第二个片段:
* 从 512 到 2559。
3. 第三个片段:
* 从 1024 到 3071。
- 函数参数
def mel_spectrogram(y, n_fft, num_mels, sampling_rate, hop_size, win_size, fmin, fmax, center=False):
y: 输入的音频信号,一维的PyTorch向量。 # 原代码是 8192
n_fft: 用于计算短时傅里叶变换(STFT)的FFT窗口大小。
num_mels: 梅尔滤波器的数量。
sampling_rate: 音频信号的采样率。
hop_size: STFT的跳步大小(窗口移动的步长)。
win_size: 窗口大小。
fmin: 最低频率。
fmax: 最高频率。
center: 一个布尔值,表示STFT是否应对齐中心。
3.4 MelDataset类
处理音频数据,转为mel谱, 转为batch训练
继承自 torch.utils.data.Dataset,用于构建一个自定义的数据集类,适用于训练声音相关的机器学习模型。
- segment_size = 8192 & split = True
将不同长度的音频截取固定长度的连续采样点(起始点随机的8192个样本点)
如:
torch.Size([1, 212893])
torch.Size([1, 41885])
torch.Size([1, 213149])
torch.Size([1, 113309])
torch.Size([1, 178845])
# 截取后
torch.Size([1, 8192])
torch.Size([1, 8192])
torch.Size([1, 8192])
torch.Size([1, 8192])
torch.Size([1, 8192])
- 输出格式为:mel.squeeze(), audio.squeeze(0), filename, mel_loss.squeeze()
shape分别为:
1.mel.shape = torch.Size([80, 32]) # [mel_nums, N_frames]
2.audio.shape = torch.Size([8192]) # [segment_size]
3.filename = ./LJSpeech-1.1/wavs/LJ001-0001.wav
4.mel_loss.shape = torch.Size([80, 32])
其中 Frame = (segment_size - hop_size) / hop_size + 1 = 32 (取下整)
4. model.py 文件
models.py里面包含生成器、判别器、损失函数,其中判别器有两个:
-
Multi-Period Discriminator,多周期判别器
-
Multi-Scale Discriminator, 多时间尺度判别器
4.1 mpd vs. msd
mpd关注一段wav的不同周期,msd关注一段wav的不同时间分辨率
4.2 mpd
* mpd用到2维卷积,通过wav加1维(period),以处理同一个wav片段的不同周期
* mpd的卷积层是在时间维度上进行卷积操作,因此宽度c维度保持为1:
x.shape = (b, c, t) => (b, c, t // period, period)
4.3 msd
* msd用的下采样是平均池化,效果是减少音频信号的时间分辨率,同时保留其总体特征。
* msd 只用1维卷积,通过 AvgPool1d完成下采样,每次下采样后送进下一个块
* msd网络结构较深, 容易出现不稳定和过拟合的情况,添加谱归一化,提供额外的稳定性和正则化效果。
* 分组卷积将输入通道分组,每组独立应用卷积操作。这减少了卷积核的数量,从而减少了参数数量和计算量。
* 组卷积可以让每组卷积核独立学习输入信号的不同特征。这种多样性有助于捕捉复杂的音频信号特征,提高判别器的性能。
5. mpd判别器
mpd 设置5个周期period(2,3,5,7,11),将输入音频根据不同周期,由1d改为2d:
- 特征尺度:
x.shape = (b, c, t) => (b, c, t // period, period)
通过对输入wav设置不同周期,捕捉音频信号的周期性特征(如语音中的音高和振幅变化)。
不同周期通过不同的卷积神经网络块(block)进行判别,得到5个周期的判别结果,将结果相加送入损失函数。
5.1 mpd架构
每个周期用一个block,block长这样:
convs = nn.ModuleList([
norm_f(Conv2d(1, 32, (kernel_size, 1), (stride, 1), padding=(get_padding(5, 1), 0))),
norm_f(Conv2d(32, 128, (kernel_size, 1), (stride, 1), padding=(get_padding(5, 1), 0))),
norm_f(Conv2d(128, 512, (kernel_size, 1), (stride, 1), padding=(get_padding(5, 1), 0))),
norm_f(Conv2d(512, 1024, (kernel_size, 1), (stride, 1), padding=(get_padding(5, 1), 0))),
norm_f(Conv2d(1024, 1024, (kernel_size, 1), 1, padding=(2, 0))),
])
conv_post = norm_f(Conv2d(1024, 1, (3, 1), 1, padding=(1, 0)))
默认: kernel_size=5, stride=3,
不同period会设置不通的padding以将特征x(压缩)到特定尺寸
x.shape = torch.Size([16, 1, 8192]) 在不同period分别被池化为:
1.torch.Size([16, 1, 4096, 2]) #第一次未压缩
2.torch.Size([16, 1, 2731, 3])
3.torch.Size([16, 1, 1639, 5])
4.torch.Size([16, 1, 1171, 7])
5.torch.Size([16, 1, 745, 11])
5.2 输入输出
-
输入: 音频文件,shape = [batch_size, segment_size]
-
输出四个向量: y_d_rs, y_d_gs, fmap_rs, fmap_gs
- y_d_rs: 真实wav的判别(真),逐块输出5组block的顺序向量, 输出如下:
y_d_gs: 生成wav的判别(假),输出向量的shpae和y_d_rs相同
torch.Size([16, 102])
torch.Size([16, 102])
torch.Size([16, 105])
torch.Size([16, 105])
torch.Size([16, 110])
- fmap_rs: 真实wav的特征map序列,有5组block的顺序输出,且每个block有逐层的6层feature-map(fmap)输出, 因此fmap输出较多, , 输出如下:
fmap_gs: 生成wav的特征map序列,shape与真实fmap相同
1st block:
torch.Size([16, 32, 1366, 2]) # 1ts-layer
torch.Size([16, 128, 456, 2]) # 2nd-layer
torch.Size([16, 512, 152, 2]) # 3rd-layer
torch.Size([16, 1024, 51, 2]) # 4th-layer
torch.Size([16, 1024, 51, 2]) # 5th-layer
torch.Size([16, 1, 51, 2]) # 6th-layer
2nd:
torch.Size([16, 32, 911, 3])
torch.Size([16, 128, 304, 3])
torch.Size([16, 512, 102, 3])
torch.Size([16, 1024, 34, 3])
torch.Size([16, 1024, 34, 3])
torch.Size([16, 1, 34, 3])
3rd:
torch.Size([16, 32, 547, 5])
torch.Size([16, 128, 183, 5])
torch.Size([16, 512, 61, 5])
torch.Size([16, 1024, 21, 5])
torch.Size([16, 1024, 21, 5])
torch.Size([16, 1, 21, 5])
4th:
torch.Size([16, 32, 391, 7])
torch.Size([16, 128, 131, 7])
torch.Size([16, 512, 44, 7])
torch.Size([16, 1024, 15, 7])
torch.Size([16, 1024, 15, 7])
torch.Size([16, 1, 15, 7])
5th:
torch.Size([16, 32, 249, 11])
torch.Size([16, 128, 83, 11])
torch.Size([16, 512, 28, 11])
torch.Size([16, 1024, 10, 11])
torch.Size([16, 1024, 10, 11])
torch.Size([16, 1, 10, 11])
6. msd判别器
msd 会对音频信号wav进行3个尺度的下采样,并送入3个块进行判别。
判别长时间和短时间采样的音频纹理细节
6.1 架构
每个block长这样:
convs = nn.ModuleList([
norm_f(Conv1d(1, 128, 15, 1, padding=7)),
norm_f(Conv1d(128, 128, 41, 2, groups=4, padding=20)),
norm_f(Conv1d(128, 256, 41, 2, groups=16, padding=20)),
norm_f(Conv1d(256, 512, 41, 4, groups=16, padding=20)),
norm_f(Conv1d(512, 1024, 41, 4, groups=16, padding=20)),
norm_f(Conv1d(1024, 1024, 41, 1, groups=16, padding=20)),
norm_f(Conv1d(1024, 1024, 5, 1, padding=2)),])
conv_post = norm_f(Conv1d(1024, 1, 3, 1, padding=1))
正则化第1个块用spectral_norm, 2-3两个块用weight_norm
6.2 输入输出
msd有3个块,
输入输出的shape与mpd一致
-
输入: 音频文件,shape = [batch_size, segment_size]
-
输出四个向量: y_d_rs, y_d_gs, fmap_rs, fmap_gs
-
y_d_rs: 真实wav的判别(真),逐块输出3个特征向量, 输出如下:
y_d_gs: 生成wav的判别(假),逐块输出3个特征向量,输出shpae和y_d_rs相同
torch.Size([16, 128])
torch.Size([16, 65])
torch.Size([16, 33])
- fmap_rs: 真实wav的特征map序列,有3组block的顺序输出,且每个block有逐层的7层feature-map(fmap),输出shape如下:
fmap_gs: 生成wav的特征map序列,有3组block的顺序输出,输出shpae和fmap_rs相同
1st block:
torch.Size([16, 128, 8192])
torch.Size([16, 128, 4096])
torch.Size([16, 256, 2048])
torch.Size([16, 512, 512])
torch.Size([16, 1024, 128])
torch.Size([16, 1024, 128])
torch.Size([16, 1024, 128])
torch.Size([16, 1, 128])
2nd block:
torch.Size([16, 128, 4097])
torch.Size([16, 128, 2049])
torch.Size([16, 256, 1025])
torch.Size([16, 512, 257])
torch.Size([16, 1024, 65])
torch.Size([16, 1024, 65])
torch.Size([16, 1024, 65])
torch.Size([16, 1, 65])
3rd block:
torch.Size([16, 128, 2049])
torch.Size([16, 128, 1025])
torch.Size([16, 256, 513])
torch.Size([16, 512, 129])
torch.Size([16, 1024, 33])
torch.Size([16, 1024, 33])
torch.Size([16, 1024, 33])
torch.Size([16, 1, 33])
5 生成器
1个conv_pre,4个up_layers, 12个resblocks, 1个conv_post
5.1 各层介绍
-
conv_pre:1个Conv1d(80, 512, k=(7,), s=(1,), p=(3,))
-
4个ups:
(ups): ModuleList(
(0): ConvTranspose1d(512, 256, kernel_size=(16,), stride=(8,), padding=(4,))
(1): ConvTranspose1d(256, 128, kernel_size=(16,), stride=(8,), padding=(4,))
(2): ConvTranspose1d(128, 64, kernel_size=(4,), stride=(2,), padding=(1,))
(3): ConvTranspose1d(64, 32, kernel_size=(4,), stride=(2,), padding=(1,)))
- 12个resblocks
3组特征尺度,每组3个blocks:
(resblocks): ModuleList(
(n): ResBlock(
(convs1): ModuleList(
(0): Conv1d(256/n, 256/n, kernel_size=(3,), stride=(1,), padding=(1,)) # padding = (kernel_size*dilation - dilation)/2
(1): Conv1d(256/n, 256/n, kernel_size=(3,), stride=(1,), padding=(3,), dilation=(3,))
(2): Conv1d(256/n, 256/n, kernel_size=(3,), stride=(1,), padding=(5,), dilation=(5,))))
(convs2): ModuleList(
(0-2): 3 x Conv1d(256/n, 256/n, kernel_size=(3,), stride=(1,), padding=(1,))
256,256,256, 128,128,128, 64,64,64 32,32,32
- conv_post: 1个Conv1d(ch, 1, 7, 1, padding=3)
ch = 32
5.2 整体结构
conv_pre
ups_0
resblocks_(0-2)
ups_1
resblocks_(3-5)
ups_2
resblocks_(6-8)
ups_3
resblocks_(9-11)
conv_post
5.3 输入输出
-
输入 x.shape = [batch_sIze, mel_num, frames] = [16, 80, 32]
-
输出 y.shape = [batch_sIze, channel, segment_size] = ([16, 1, 8192])
6. 训练 & 推理
6.1 训练-Training
训练是通过train.py文件,G和D(mpd,msd)同时训练
6.2 推理 Fine-tuning
推理只针对生成器G, 通过inference.py文件
Reference
-
本人简化了原版,项目在这: https://github.com/disanda/HiFiGAN-Easy
-
https://github.com/descriptinc/melgan-neurips
-
https://github.com/r9y9/wavenet_vocoder
-
https://github.com/NVIDIA/waveglow