读VITS代码

总之就是读一下 vits(原论文标题:Conditional Variational Autoencoder with Adversarial Learning for End-to-End Text-to-Speech)。vits, “Variational Inference with adversarial learning for end-to-end Text-to-Speech”,一个变分推理、对抗学习、端到端的 TTS 方法。论文团队给出了他们的示例代码。

概览

工程包含的文件:

  • attentions.py:关于自注意力机制的部分,定义了 transformer 的结构。

    • Encoder 类
      • 定义了参数和 forward 方法
    • Decoder 类
      • 定义了参数和 forward 方法
    • MultiHeadAttention 类
      • 定义了参数,forward 方法,attention 方法,_matmul_with_relative_values 方法,_matmul_with_relative_keys 方法,_get_relative_embeddings 方法,_relative_position_to_absolute_position 方法,_absolute_position_to_relative_position 方法,_attention_bias_proximal 方法
    • FFN(前馈神经网络) 类
      • 定义了参数,forward 方法,_causal_padding 方法,_same_padding 方法。
  • commons.py:公用的一些常用方法

    • 定义了 init_weights 方法
    • 定义了 get_padding 方法
    • 定义了 convert_pad_shape 方法
    • 定义了 intersperse 方法
    • kl_divergence 方法
    • rand_gumbel 方法
    • rand_gumbel_like 方法
    • slice_segments 方法
    • rand_slice_segments 方法
    • get_timing_signal_1d 方法
    • add_timing_signal_1d 方法
    • cat_timing_signal_1d 方法
    • subsequent_mask 方法
    • fused_add_tanh_sigmoid_multiply 方法:使用 @torch.jit.script 装饰
    • convert_pad_shape 方法
    • shift_1d 方法
    • sequence_mask 方法
    • generate_path 方法
    • clipgrad_value 方法
  • data_utils.py

    • 定义了 TextAudioLoader 类
      • 切分训练的时候使用的音频、文本
    • 定义了 TextAudioCollate 类
      • 批量整理文本、音频
    • 定义了多说话者版本 TextAudioSpeakerLoader 类
      • 类似 TextAudioLoader 但是是多说话者版本
    • 定义了 TextAudioSpeakerCollate 类
      • 类似 TextAudioCollate,但是是多说话者版本
    • 定义了 DistributedBucketSampler 类
      • 储存桶相关
  • losses.py:损失函数相关

    • feature_loss 方法
      • 计算特征图(fmap)之间的差异
    • discriminator_loss 方法
      • 计算判别器(Discriminator)的损失
    • generator_loss 方法
      • 计算生成器(Generator)的损失
    • kl_loss 方法
      • 计算KL散度损失,用于变分自编码器(VAE)
  • mel_processing.py:梅尔频谱处理

    • dynamic_range_compression_torch 方法
      • 动态范围压缩,乘 C、裁剪、然后取对数
    • dynamic_range_decompression_torch 方法
      • 动态范围解压缩,和动态范围压缩互补
    • spectral_normalize_torch 方法
      • 对幅度谱进行动态范围压缩,就是调用 dynamic_range_compression_torch 方法
    • spectral_de_normalize_torch 方法
      • 同上,只是调用了一下 dynamic_range_decompression_torch 方法
    • spectrogram_torch 方法
      • 作用:
        • 计算音频信号 y 的短时傅立叶变换(STFT)谱。
      • 参数:
        • FFT点数 n_fft
        • 采样率 sampling_rate
        • 帧移 hop_size
        • 窗长度 win_size
        • 是否在时间轴上居中 center
      • 先对输入信号做反射填充,然后应用 Hann 窗函数进行窗口化处理,最后计算 STFT 并取模平方根得到幅度谱。
    • spec_to_mel_torch 方法
      • 作用:
        • 将频域谱 spec 转换为 Mel 频谱。
      • 使用全局变量 mel_basis 存储预先计算好的梅尔滤波器组矩阵,如果矩阵不存在,则会基于给定的参数计算并转换为当前设备和数据类型。
      • 将幅度谱与梅尔滤波器组矩阵进行矩阵乘法,然后对结果进行动态范围压缩。
    • mel_spectrogram_torch 方法
      • 完整流程的 Mel 频谱计算函数,包含了从原始音频信号到 Mel 频谱的全部步骤。
      • 包括了对输入信号的检查(确保其在 [-1, 1] 范围内),计算 STFT 幅度谱,转换为 Mel 频谱并进行动态范围压缩。
  • models.py

    • 模型主体结构。
    • 每一个类是模型的一个(或者一种)组件,核心是 forward 方法。
    • StochasticDurationPredictor 类
      • 随机时长预测器,论文里提到的主要创新点,使用了正态流。接收 gin_channels 参数进行调整。
      • forward 方法
    • DurationPredictor 类
      • 时长预测器,简化版本的 StochasticDurationPredictor,不支持条件输入,没有正态流。
    • TextEncoder 类
      • 继承自 torch.nn.Module,并且使用了 attentions 的 Encoder 制作成员(上面的随机时长预测器和下面几个类也用到了 torch.nn.Module,但是没有使用 attentions)
      • forward 方法:
        1. 将文本输入通过嵌入层来获得初始的嵌入表示
        2. 将这些嵌入表示转换为适合自注意力机制的格式(三维张量,维度为批次大小、隐藏通道数和序列长度)
        3. 使用自注意力机制和卷积层来处理这些嵌入表示
        4. 通过投影层输出最终的编码表示
      • ResidualCouplingBlock 类
        • 残差耦合块
        • 其中的流变换根据 reverse 参数决定是正向还是反向,执行正向流变换的时候,输入数据会依次通过每个流变换层。每个流变换层都会更新数据,并通过残差连接将变换后的数据与原始数据相结合,这有助于避免梯度消失问题
        • 流部分和 StochasticDurationPredictor 是一样的
      • PosteriorEncoder 类
        • 后验分布编码器
        • 在给定观测数据(如语音信号)的条件下,对潜在变量(如声谱特征)的分布进行建模
        • 换言之就是,将观测到的语音信号编码为潜在空间的分布,使得模型能够捕捉到语音信号的复杂性和多样性,并在生成过程中保持这些特性
        • forward 方法:
        1. 将输入数据通过预处理卷积层来获得初始的编码表示
        2. 将这些编码表示通过扩张卷积层进行进一步的处理,扩张卷积层通过扩张率来增加感受野,捕捉更长范围内的依赖关系
        3. 通过投影卷积层输出均值和对数方差、输出可以用于后续的生成过程,如重参数化技巧,以生成新的样本。
      • Generator 类
        • 从潜在空间生成频谱
        • forward 方法:
        1. 输入数据首先通过 conv_pre 进行处理(是一个一维的卷积核,Conv1d)
        2. 数据依次通过 ups 中的上采样层和 resblocks 中的残差块。在每一步上采样过程中,数据的分辨率逐渐增加,同时通过残差连接保持了信息的流动
        3. 通过 conv_post 生成最终的输出,这可以是语音波形或其他类型的音频特征
        • remove_weight_norm 方法:
          • 移除所有卷积层中的权重归一化(weight normalization)
          • 可能是测试使用的?
      • DiscriminatorP 类
        • GANs 的一部分,辨别器。
        • 适合周期信号,P 指的是 Periodic,周期的(我猜的)
        • 此处可以参考 HiFi-GAN 模型
        • DiscriminatorS 类
          • 同上, GANs 的一部分,辨别器。
          • 适合非周期信号(文本,图像),S 指的是 Sequential,序列的(我猜的)
        • MultiPeriodDiscriminator 类
          • 多周期辨别器,使用了上面两种辨别器:
            1
            2
            discs = [DiscriminatorS(use_spectral_norm=use_spectral_norm)]
            discs = discs + [DiscriminatorP(i, use_spectral_norm=use_spectral_norm) for i in periods]
        • 综合二者的能力
      • SynthesizerTrn 类
        • 用于训练的合成器,较为复杂,使用了上面定义的一系列模块:
          • enc_p: PosteriorEncoder,文本编码器,用于将文本转换为嵌入表示。它使用一个自注意力编码器来处理文本输入。
          • dec: Generator,生成器,用于从嵌入表示生成语音信号。包含一系列上采样层和残差块,用于生成高质量的语音波形。
          • enc_q: TextEncoder,后验编码器,用于编码声谱特征。使用卷积层和扩张卷积来处理声谱数据。
          • flow: ResidualCouplingBlock,残差耦合块,用于数据的流动和变换。它包含一系列残差耦合层,用于学习声谱数据的复杂分布。
          • dp: StochasticDurationPredictor,持续时间预测器,用于预测语音信号的持续时间
            • 根据 use_sdp 参数的值,它会初始化为 StochasticDurationPredictor 或 DurationPredictor。
          • emb_g: 一个 nn.Embedding,说话者嵌入层,用于条件生成不同说话者的语音
            • 如果 n_speakers 大于0,这个层会被初始化。
        • forward 方法:
          1. 文本编码: 使用 enc_p(文本编码器)对文本输入 x 进行编码,得到文本的嵌入表示。同时,使用 x_lengths 来创建一个掩码,以便在后续步骤中忽略填充的部分。
          2. 声谱编码: 使用 enc_q(后验编码器)对目标声谱特征 y 进行编码,得到声谱的嵌入表示。同时,如果提供了说话者ID sid,则使用 emb_g(说话者嵌入层)来获取说话者嵌入,并将其加入到声谱编码中。
          3. 持续时间预测: 如果模型使用随机持续时间预测器(use_sdp 为 True),则调用 dp(持续时间预测器)来预测语音信号的持续时间。否则,使用 DurationPredictor 来预测(节约成本)。
          4. 生成路径计算: 计算生成语音的路径。这通常涉及到计算负对数似然(negative log-likelihood)和注意力机制,以便在生成过程中对齐文本和声谱特征。
          5. 生成语音波形: 使用 dec(生成器)来生成语音波形。生成器接收文本嵌入表示和声谱嵌入表示作为输入,并输出最终的语音波形。
          6. 输出: forward 方法的输出包括生成的语音波形、持续时间预测损失、注意力权重、生成的声谱片段的ID、输入文本和声谱的掩码,以及原始的嵌入表示。
          • 输出内容:
            • o: 生成的语音波形。
            • l_length: 持续时间预测损失。
            • attn: 注意力权重,用于显示文本和声谱特征之间的对齐情况。
            • ids_slice: 生成的声谱片段的ID。
            • x_mask, y_mask: 输入文本和声谱的掩码,用于在后续处理中忽略填充的部分。
            • (z, z_p, m_p, logs_p, m_q, logs_q): 原始的嵌入表示和相关的统计信息,这些信息可以用于进一步的分析和调试。
        • infer 方法
          • 训练结束之后,用于推理的方法
          1. 文本编码: 与 forward 方法类似,infer 方法首先使用文本编码器 enc_p 对输入文本 x 进行编码,得到文本的嵌入表示。
          2. 声谱编码: 如果提供了说话者ID sid,则使用说话者嵌入层 emb_g 来获取说话者嵌入,并将其加入到声谱编码中。
          3. 持续时间预测: 根据模型是否使用随机持续时间预测器(use_sdp 参数),调用相应的持续时间预测器 dp 来预测语音信号的持续时间。
          4. 生成语音波形: 使用生成器 dec 来生成语音波形。在推理模式下,生成器会根据文本嵌入表示和预测的持续时间生成连续的语音波形。
          5. 注意力权重计算: 通过计算注意力权重,可以了解模型是如何在生成语音时关注输入文本的不同部分的。
          6. 输出: infer 方法的输出通常包括生成的语音波形、注意力权重和其他可能的中间结果,如声谱特征的嵌入表示。
        • voice_conversion 方法
          • 用于执行说话者转换
          • 接收源说话者的语音信号和目标说话者的标识符作为输入,然后生成具有目标说话者特征的语音信号
          • 参数:
            • y: 源说话者的语音信号,通常是一个三维张量,形状为 [batch_size, spec_length, spec_channels]。
            • y_lengths: 源语音信号的长度,用于指示每个样本中的非填充声谱的长度。
            • sid_src: 源说话者的标识符。
            • sid_tgt: 目标说话者的标识符。、
          • 过程:
            • 说话者嵌入获取: 使用 emb_g(说话者嵌入层)根据源说话者和目标说话者的标识符获取相应的说话者嵌入。这些嵌入将用于调整生成的语音,使其具有目标说话者的特征。
            • 源语音编码: 使用 enc_q(后验编码器)对源说话者的语音信号 y 进行编码,得到声谱的嵌入表示。
            • 说话者转换: 通过 flow(残差耦合块)和可能的其他变换层,将源说话者的嵌入表示转换为目标说话者的嵌入表示。这个过程可能涉及到使用说话者嵌入来调整声谱特征,以便模仿目标说话者的声音。
            • 生成目标语音: 使用 dec(生成器)根据转换后的嵌入表示生成目标说话者的语音信号。生成器会根据调整后的声谱特征生成连续的语音波形。
            • 输出: voice_conversion 方法的输出是转换后的语音信号,它应该具有目标说话者的声音特征,同时保持原始文本的内容不变。
          • 输出:
            • o_hat: 转换后的目标说话者的语音信号。
            • y_mask: 目标语音信号的掩码,用于在后续处理中忽略填充的部分。
            • (z, z_p, z_hat): 原始的嵌入表示和相关的统计信息,这些信息可以用于进一步的分析和调试。
  • modules.py
    • LayerNorm 类
      • 归一化层
    • ConvReluNorm 类
      • attention 里的一个结构
    • DDSConv 类
      • 带空洞卷积(Dilated Convolution)和深度可分离卷积(Depth-Separable Convolution)的模块
    • WN 类
      • 权重归一化(Weight Normalization)
    • ResBlock1 类 和 ResBlock2 类
      • 残差块(Residual Block),用于构建更深的网络结构
    • Log 类
      • 对输入数据 x 取对数的操作
      • 用于变分自编码器(VAE)中的变分下界
    • Flip 类
      • 翻转输入/取消翻转(取决于 reverse 是 True 还是 False)
    • ElementwiseAffine 类
      • 逐元素的仿射变换
    • ResidualCouplingLayer 类
      • 残差耦合层
      • 用于流模型(Flow Model)中的变分推断
    • ConvFlow 类
      • 基于卷积的流模型
      • 用于生成模型或者变分推断
  • preprocess.py
    • 作为主函数时:
      • 向命令行添加参数
      • 从指定的文件列表中读取文件路径和文本
      • 使用指定的文本清理器清理文本
      • 将清理后的文本写入新的文件列表
    • 总之就是字面意思的预处理
  • train.py
    • main 函数
      • 断言检查:
        • 使用 assert torch.cuda.is_available() 确保当前环境可以使用CUDA,因为脚本设计为仅在GPU上运行。
      • 获取GPU数量:
        • 通过 torch.cuda.device_count() 获取系统中可用的GPU数量,并将其存储在变量 n_gpus 中。
      • 设置环境变量:
        • 设置环境变量 MASTER_ADDR 和 MASTER_PORT,这些变量用于分布式训练中的进程间通信
        • MASTER_ADDR 被设置为 ‘localhost’,MASTER_PORT 被设置为 ‘80000’。
      • 获取超参数:
        • 调用 utils.get_hparams() 函数获取超参数设置
        • 超参数通常包括:
          • 学习率
          • 批次大小
          • 训练周期数
      • 启动分布式训练:
        • 使用 mp.spawn(mp 是 torch 的 multiprocessing) 函数启动多个训练进程,调用 run 函数
          • nprocs:要启动的进程数
    • run 函数
      • 在 main 中被 mp.spawn 调用
      • 函数参数:
        • rank:当前进程的排名(ID),在分布式训练中用于区分不同的进程。
        • n_gpus:系统中可用的GPU数量。
        • hps:包含超参数的对象,这些超参数定义了模型的训练和数据配置。
      • 判断当前进程(rank)的排名是否是0(是主进程),如果是的话:
        • 初始化日志记录器:
          • 调用 utils.get_logger 方法初始化日志记录器,并记录超参数 hps 的信息。这有助于监控训练过程并记录重要信息。
        • 检查Git哈希:
          • 调用 utils.check_git_hash 方法来验证代码的一致性,确保训练过程中使用的代码版本与预期的版本相符
          • 一般我使用的时候需要注释掉
        • 创建TensorBoard SummaryWriter:
          • 创建两个 SummaryWriter 对象,一个用于记录训练过程中的信息(writer),另一个用于记录评估过程中的信息(writer_eval)。
      • 初始化分布式训练环境:
        • 调用 dist.init_process_group 方法初始化分布式训练环境。设置后端为 ‘nccl’,初始化方法为 ‘env://‘,世界大小(world size)为 n_gpus,当前进程的排名为 rank。
      • 设置随机种子:
        • 使用 torch.manual_seed 方法设置随机种子,以确保训练的可重复性。随机种子使用超参数中的 hps.train.seed 值。
      • 设置当前使用的GPU
      • 加载训练数据:
        • 使用 TextAudioLoader 类加载训练数据
        • 创建 DistributedBucketSampler 对象,用于在分布式训练中对数据进行采样
        • 创建 DataLoader 对象,用于加载训练数据。
      • 判断当前进程(rank)的排名是否是0(是主进程),如果是的话:
        • 加载评估数据
        • 创建评估数据的 DataLoader 对象。
      • 模型:
        • 使用 SynthesizerTrn 类构建生成器模型 net_g
        • 使用 MultiPeriodDiscriminator 类构建鉴别器模型 net_d
      • 优化器:
        • 使用 torch.optim.AdamW 创建生成器优化器 optim_g、鉴别器优化器 optim_d
        • 使用 DDP(Distributed Data Parallel)包装生成器和鉴别器模型,以支持分布式训练。
      • 加载检查点(checkpoint):
        • 从检查点文件中加载生成器和鉴别器的状态
          • 如果加载失败,则将全局步数 global_step 和当前周期 epoch_str 设置为初始值。
      • 设置学习率调度器:
        • 使用 torch.optim.lr_scheduler.ExponentialLR 创建学习率调度器
        • 设置学习率衰减率。
      • 训练和评估循环:(for epoch in …)
        • 遍历超参数定义的训练周期 hps.train.epochs
          • 在每个周期中,执行训练和评估过程
            • 如果当前进程的排名为0,则调用 train_and_evaluate 函数进行训练和评估,并记录日志和TensorBoard信息
            • 否则,只执行训练过程。
        • 更新学习率调度器:
          • 在每个周期结束时,更新生成器和鉴别器的学习率调度器。
            • 即 调用 scheduler_g.step() 和 scheduler_d.step()
    • train_and_evaluate 函数
      • 函数参数:
        • rank: 当前进程的排名(ID),用于分布式训练中的标识。
        • epoch: 当前的训练周期。
        • hps: 超参数设置,包含了模型训练和数据配置的所有重要参数。
        • nets: 一个包含生成器(net_g)和鉴别器(net_d)的列表。
        • optims: 一个包含生成器和鉴别器优化器的列表。
        • schedulers: 一个包含生成器和鉴别器学习率调度器的列表。
        • scaler: 梯度缩放器,用于混合精度训练中梯度的缩放。
        • loaders: 一个包含训练和评估数据加载器的列表。
        • logger: 日志记录器,用于记录训练过程中的信息。
        • writers: 一个包含TensorBoard的SummaryWriter对象的列表,用于记录训练和评估过程中的信息。
      • 函数内部:
        • 将nets、optims、schedulers中的生成器和鉴别器分别赋值给net_g、optim_g、scheduler_g和net_d、optim_d、scheduler_d。
        • 如果writers不为None,则将写入器赋值给writer和writer_eval。
        • 调用train_loader.batch_sampler.set_epoch(epoch)设置当前的训练周期,这对于某些采样器(如DistributedBucketSampler)来说是必要的,因为它们可能需要根据周期进行调整。
        • 定义全局变量global_step,用于跟踪训练步数。
        • 将生成器和鉴别器设置为训练模式(net_g.train()和net_d.train())。
        • 遍历训练数据加载器train_loader中的每个批次。
          • 将批次数据移动到对应的GPU上。
          • 使用autocast上下文管理器进行混合精度训练,如果启用了混合精度训练。
          • 调用生成器net_g生成预测值y_hat,并计算损失。
          • 将真实音频的频谱转换为Mel频谱y_mel。
          • 计算鉴别器的损失loss_disc,并执行反向传播和优化器步骤。
          • 计算生成器的损失loss_gen_all,并执行反向传播和优化器步骤。
          • 如果当前进程是主进程(rank == 0),则记录日志和TensorBoard信息。
        • 如果当前进程是主进程,检查是否到达了评估间隔,如果是,则调用evaluate函数进行评估,并保存检查点。
        • 向控制台打印全局训练步数global_step。
    • evaluate 函数
      • 函数参数:
        • hps:包含超参数的对象,这些超参数定义了模型的训练和数据配置。
        • generator:训练好的生成器模型,用于生成语音。
        • eval_loader:评估数据的加载器,它提供了评估数据集的批量数据。
        • writer_eval:用于评估数据的TensorBoard SummaryWriter对象,用于记录评估过程中的信息。
      • 设置模型为评估模式:
        • 调用 generator.eval() 将模型设置为评估模式。这是必要的,因为在评估模式下,模型会关闭dropout和批量归一化等只在训练时使用的层。
      • 禁用梯度计算:
        • 使用 torch.no_grad() 上下文管理器来禁用梯度计算。这有助于减少内存消耗,并加速评估过程,因为在评估时不需要计算梯度。
      • 遍历评估数据加载器:
        • 通过 for 循环遍历 eval_loader 中的每个批次的数据。
        • 将批次数据移动到GPU上(这里假设使用的是第一个GPU,即 cuda(0))。
      • 处理单个样本:
        • 从评估批次中提取单个样本数据(这通常是通过切片操作完成的),因为评估函数可能设计为只处理单个样本。
        • 调用 generator.module.infer 方法(注意这里使用了 .module,这可能是因为模型被包装在 nn.DataParallel 或 nn.parallel.DistributedDataParallel 中)来生成语音。
        • 计算生成的语音的长度。
      • 转换频谱为Mel频谱:
        • 使用 spec_to_mel_torch 函数将真实和生成的频谱图转换为Mel频谱图。Mel频谱图是一种表示音频数据的常用方式,它更接近于人类对频率的感知。
      • 记录评估结果:
        • 如果当前进程的排名为0(主进程),则使用 utils.summarize 函数记录评估结果。这包括生成的Mel频谱图、音频样本以及其他可能的统计信息。
        • 将生成的Mel频谱图和音频样本添加到 image_dict 和 audio_dict 字典中,这些字典随后被传递给 utils.summarize 函数。
        • 如果是训练的第一步(global_step == 0),则还包括真实样本的Mel频谱图和音频样本以供参考。
      • 恢复模型训练模式:
        • 在评估结束后,调用 generator.train() 将模型恢复到训练模式。
  • train_ms.py
    • 多说话者训练,ms 意思是 multi-speaker
    • 总之就是多了一些多说话人相关的设置,对应的类也被换成了多说话人需要的类
    • 除此之外,还使用了 GradScaler 来进行梯度缩放
    • 因为我是懒狗所以直接交给 chatgpt 帮我对比了
    • 数据加载器和处理:
      • 第一段代码中使用了TextAudioLoader和DistributedBucketSampler来加载和处理文本和音频数据。
      • 第二段代码中使用了TextAudioSpeakerLoader,这表明它可能在处理与说话者相关的数据,这可能是一个多说话者的TTS模型。
    • 模型结构:
      • 第一段代码中的模型定义较为简洁,没有提及说话者的数量。
      • 第二段代码中的SynthesizerTrn模型在初始化时考虑了n_speakers参数,这意味着模型可能包含说话者嵌入或有说话者适应的功能。
    • 评估函数:
      • 第一段代码中的evaluate函数没有提供详细的实现,而第二段代码中的evaluate函数实现了对单个样本的评估,并生成了Mel频谱图和音频样本的可视化。
      • 第二段代码中的评估还考虑了说话者(speakers参数),并且在评估时使用了generator.module.infer方法,这表明评估过程中可能使用了说话者特定的信息。
    • 训练循环:
      • 第一段代码中的train_and_evaluate函数实现了训练和评估的循环,但在代码片段中没有提供完整的实现。
      • 第二段代码中的train_and_evaluate函数提供了完整的实现,包括前向传播、损失计算、反向传播和优化器步骤,以及日志记录和评估过程。
    • 梯度缩放:
      • 第二段代码中使用了GradScaler来进行梯度缩放,这在混合精度训练中非常有用,可以防止梯度值过大导致的数值不稳定问题。
      • 第一段代码中没有明确提及梯度缩放。
    • 学习率调度器:
      • 两段代码都使用了指数衰减的学习率调度器,但是第二段代码中提供了更详细的实现。
    • 保存检查点:
      • 第二段代码中在每个epoch结束时保存了生成器和鉴别器的检查点,而第一段代码中没有明确提及检查点的保存。
    • 代码结构:
      • 两段代码都遵循了类似的结构,包括初始化、模型定义、训练循环和评估过程。
      • 第二段代码提供了更多的细节,包括模型的具体参数、评估过程和训练循环中的日志记录。
  • transforms.py
  • utils.py

流模型

在程序里,ResidualCouplingBlock(残差耦合块)类和 StochasticDurationPredictor(随机时长预测器)就是使用了流模型。

流变换(Flow Transformation)是流模型(Flow-based Model)中的一个核心概念,它指的是一系列可逆的变换过程,用于将一个复杂的概率分布转换为一个更简单(通常是高斯)的概率分布,或者反过来,将一个简单的概率分布转换为一个复杂的目标分布。这些变换过程是可逆的,意味着可以从一个分布直接转换到另一个分布,并且可以精确计算转换过程中的雅各比行列式(Jacobian determinant),这对于概率密度函数的计算至关重要。

简而言之就是概率界的傅立叶变换,把一个复杂的、通用的概率模型(对应到傅立叶变换里就是一个复杂的函数)分解为标准的简单分布,例如高斯分布(对应傅立叶变换的正余弦函数),过程可逆,变换之后更加容易处理(因为都是基本分布/函数了)。

流变换的特点:

  • 可逆性:
    • 流变换的一个显著特点是它的双向可逆性。这意味着变换不仅可以从分布A转换到分布B,还可以从分布B转换回分布A。这种可逆性使得流模型在理论上可以精确地计算概率密度,并且可以用于生成模型,通过逆变换生成新的样本。
  • 精确计算:
    • 在传统的生成模型中,如生成对抗网络(GANs)或变分自编码器(VAEs),通常需要通过近似方法来估计概率密度。而流模型通过流变换,可以直接计算概率密度,这在理论上提供了更精确的估计。
  • 雅各比行列式:
    • 流变换过程中的雅各比行列式是关键,因为它涉及到变换的尺度和方向的变化。在流模型中,通过设计特定的变换结构(如耦合层),可以简化雅各比行列式的计算,使其在实际应用中变得可行。

流变换的发展:

  1. NICE:
    最初的流变换模型NICE(Non-linear Independent Components Estimation)实现了从任意分布到高斯分布的可逆变换。
  2. RealNVP:
    后续的RealNVP(Real-Valued Non-Volume Preserving)模型进一步扩展了流变换的能力,实现了从任意分布到条件非高斯分布的可逆变换。
  3. GLOW:
    最新的GLOW(Generative Flow with Invertible 1x1 Convolutions)模型通过引入1x1卷积,实现了从任意分布到任意其他分布的可逆变换,这使得GLOW能够处理更复杂的分布,如图像数据。

流变换的应用:
流变换在生成模型中的应用非常广泛,特别是在需要精确概率估计的场景中。例如,在语音合成、图像生成、药物设计等领域,流模型可以通过流变换来生成高质量的样本,并且可以精确地计算样本的概率密度。

可以参考 https://gwylab.com/note-flow_based_model.htmlhttps://www.bilibili.com/video/BV1E441137wE


读VITS代码
http://petertan303.github.io/2024/03/29/读VITS代码/
作者
peter?
发布于
2024年3月29日
许可协议