音频模型_HiFiGAN源码解析2

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. 初始片段:
    *020472. 第二个片段:
    *51225593. 第三个片段:
    *10243071
  • 函数参数

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

  • 16
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值