VITS源码解析1- 简介&数据预处理

VITS是基于深度学习的音频技术集大成者,在HiFiGAN的基础上把音频生成应用于两类典型应用:

  • 文本转语音 (Text-to-speech,TTS)

    将指定文本转为语音

  • 声色转化 (Voice Conversion)

    将一个人的声音变为另一个人的声音

注:基于神经网络的表征学习,可以用VITS同时实现以上两个任务

1. 模型理解

可以分为 HiFiGAN部分 与 TTS 部分

  • HiFiGAN

    有个编码器,输入mel谱,输出隐向量z

    有个解码器(生成器),输入隐向量z,输出对应音频

  • TTS部分

这个部分是将text的语义特征融入隐向量z, 通过z实现语音 <=> 文本。

问题在于,一个文本有无限种语音可能性(1对n),但tts是需要实现1对1,即可逆,关键技术包括:

1. 流模型 (Flows) 

	一种可逆的生成模型,文本特征嵌入语音特征的核心模型, 它的难度在于训练,需要后两个技术。

2. 单调对齐搜索(Monotonic Alignment Search,MAS)

	这个是一种动态规划算法,用于指导流模型训练, 但是它是单向的,即 语音 => 文本

3. 时序预测器

	文本(Text)转为音素(Phonemes)后, 需要一个预测器实现,保证在推理阶段代替MAS。

2. 环境和参数配置

2.1 Requirements.txt

	python 相关包,用pip命令安装即可,建议配置conda环境

2.2 Config文件夹

    - 是一个通用的Json文件, 用于训练VITS, 官方主要有两个数据集的配置,包括:

    - Lj-speech (单人)和 VCTK (108人),训练多人可以实现声色转换。

    - 包含3个参数群

        - Train参数,
        - Data参数
        - Model参数

2.3 monotonic_align文件夹

	用于构建文本到语音单调对齐搜索(Monotonic Alignment Search), 使用前需要用Cpython安装(Buiild)一下, 生成执行文件

3. 数据处理文件

数据集一般有两个文件夹,一个放音频,一个放text (Lj-speech 是一个csv表格)

预处理需要将下载的数据集处理为统一的格式,以方便模型训练。包括如下文件:

  • Data_utils.py

  • preprocess.py

  • text 文件夹

  • filelists文件夹

数据集VCTK测试代码:

import utils
import json
import scipy
from torch.utils.data import DataLoader
from data_utils import TextAudioLoader, TextAudioCollate, TextAudioSpeakerLoader, TextAudioSpeakerCollate

with open('./configs/vctk_base.json', "r") as f:
    data = f.read()
    config = json.loads(data)
hparams = utils.HParams(**config)

collate_fn = TextAudioSpeakerCollate() # TextAudioCollate()
train_dataset = TextAudioSpeakerLoader(hparams.data.training_files, hparams.data) # TextAudioLoader, TextAudioSpeakerLoader

train_loader = DataLoader(train_dataset, batch_size=10, collate_fn=collate_fn)

def volume_norm(x) :
    return x / abs(x).max()

for batch_idx, (x, x_lengths, spec, spec_lengths, y, y_lengths, speakers) in enumerate(train_loader):
    print(batch_idx)
    print(x.shape) # torch.Size([10, 97])
    # tensor([[  0, 131,   0,  51,   0, 158,   0,  16,   0,  46,   0, 156,   0,  43,
    #        0, 102,   0,  46,   0,  16,   0,  72,   0,  62,   0,  16,   0,  50,
    #        0,  87,   0, 158,   0,  16,   0,  50,   0, 156,   0,  57,   0, 135,
    #        0,  55,   0,   4,   0]])
    print(x_lengths) # tensor([97, 93, 75, 51, 59, 47, 43, 45, 49, 47])
    print(spec) # value = [-1, 1]
    print(spec.shape)   # torch.Size([10, 513, 223])
    print(spec_lengths) # tensor([223, 166, 154, 119, 118, 109,  93,  91,  90,  80])
    print(y) # value = [-1, 1]
    print(y.shape) # torch.Size([10, 1, 57330])
    print(y_lengths) # tensor([57330, 42557, 39470, 30650, 30429, 28004, 24035, 23373, 23153, 20507])
    print(speakers) # tensor([ 87,  28,  79,  22, 106,  39,   4,   2,  50,  47])
    #scipy.io.wavfile.write(data=volume_norm(y[0][0].cpu().numpy()), rate=hparams.data.sampling_rate, filename=f"{batch_idx}_train_dataset_audio.wav")
    break

这里我们去掉了源代码中分布式的类,可以看出

  • 音素x部分,音素左右插0填充。

  • 音素选择一个batch中最长的长度为标准,其他填充到该长度

  • spec也是选择一个batch中最长的长度为标准,其他填充到该长度

  • 波形y部分,没有填充

4. preprocess.py 文件

这个是脚本文件,将音频文本文件中的文本转换为音素格式文件。

  • 清理功能,是对文本字符格式的转换,即将不同语言、符合的文本标准化为ASCII字符格式。

  • 转音素功能,将文本转换为音素表示,音素为语音模型理解的序列。

4.1 主要功能

  • 文件读取与解析:

      1.该脚本通过命令行参数指定一个或多个文件列表(通常为 filelists),文件中的每一行包含了音频文件路径和对应的文本内容(例如用于语音合成的数据集)。
    
      2.通过 --text_index 参数确定文本在文件行中的位置。默认是第1列(索引1)。
    
      3.load_filepaths_and_text 函数负责从指定文件列表中加载音频文件路径和文本对。
    
  • 文本清理

    1. 脚本会对每一条文本应用一个或多个清理器进行处理,清理器由 --text_cleaners 参数指定,默认使用 english_cleaners2。

    2. 文本清理的主要目的是移除不需要的符号、空格或其他影响模型训练的字符,保证文本干净且适合输入到模型中。

    3. 文本清理由 text._clean_text 函数完成,该函数根据传入的清理器(例如 english_cleaners2)来处理英文文本转音素过程。

  • 生成新的文件

    1. 处理后的文本会被重新写入文件,新的文件名是原文件名加上一个后缀(通过 --out_extension 参数指定,默认是 .cleaned)。
    2.每一行文本与音频文件路径仍保持对应关系,确保不会因为清理过程导致文件与文本的配对出错。
    

4.2 参数解析

--out_extension:用于指定生成新文件的扩展名后缀,默认为 "cleaned",也就是生成的新文件名会以 .<out_extension> 作为后缀。

--text_index:用于指定文本在文件中的位置,默认为第1列(索引1),通常这是音频文件路径与文本配对数据集中的文本部分。

--filelists:输入的文件列表,包含音频文件路径和文本配对,默认处理两个文件:"filelists/ljs_audio_text_val_filelist.txt" 和 "filelists/ljs_audio_text_test_filelist.txt"。

--text_cleaners:指定要使用的文本清理器,默认为 ["english_cleaners2"],可以通过传入不同的清理器实现不同的文本清理规则。

4.3具体流程

1. 解析参数:通过 argparse 模块解析命令行传入的参数。
2. 加载文件列表:使用 load_filepaths_and_text 从文件中加载音频路径和文本对。
3. 清理文本:对每一条文本应用指定的清理器(例如 english_cleaners2)进行处理,生成清理后的音素文本。
4. 写入新文件:将处理后的内容按照原格式写入新文件,文件名会添加 .cleaned 后缀。

5. Text 文件夹

用于文本预处理,包含 symbols.py 和 cleaners.py 和两个文件,通过自带的__init__.py文件是整合两个文件功能。

  • symbols.py

    将文本映射为符号的索引数字表示。映射简化了模型的输入处理,使模型能够更容易地处理和生成语音。

  • cleaners.py

    将文本的每个部分音素化,帮助在文本和音频对齐。

  • 音素

    根据发音特性、应力、位置等因素会有不同的音素ID。即使是同一个字母,如果发音或应力不同,也会得到不同的ID。

  • 应力

    有多种形式(如重读、次重读、无重读等),这会导致同样的字母(由于其所处的位置和应力不同,而被映射到不同的音素。

5.1 Symbols.py

符号集(symbols)文件,这个符号集包括了文本预处理和表示所需的所有可能字符,并为每个字符分配了唯一的标识符。

symbos包括填充字符、标点符号、字母和国际音标字符等特殊符号, 即将文本中的各种特殊字符映射到符号集合中。

  • 符号定义:

    1._pad: 用于填充的符号,通常在处理不等长的序列时使用。
    2._punctuation: 标点符号的集合,包括常见的句号、逗号、问号等。
    3._letters: 大写和小写字母的集合,涵盖了英语字母表。
    4._letters_ipa: 国际音标(IPA)字符的集合,用于表示不同的发音符号。

  • 符号列表:

将所有的符号合并成一个列表

1._pad 被放在列表的开头,用于填充。
	2._punctuation 包含的所有标点符号。
	3._letters 包含的所有字母(大写和小写)。
	4._letters_ipa 包含的所有国际音标字符。

5.2 Cleaners.py

通过多个步骤将输入文本标准化,适合TTS模型处理。每个清理器函数对文本进行特定的转换,如缩写扩展、音素化、标点符号保留等。

  • 依赖库

    1. re:正则表达式库,用于匹配和替换文本中的模式。

    2. unidecode:一个将非ASCII字符转换为其ASCII等价物的库。适用于将文本标准化为仅包含基本字符。

    3. phonemize:用于将文本转换为音素表示的库(将文字转化为语音模型理解的音素序列)。

  • 正则表达式和缩写映射

    1. _whitespace_re:匹配一个或多个空白字符的正则表达式,用于压缩空白符号。

    2. _abbreviations:一组常见的英文缩写及其完整形式的映射列表。通过这些规则,像"Mr." 会被展开为 “mister”。

  • 主要函数

    1.expand_abbreviations(text)

    • 作用:展开缩写,将文本中的常见缩写替换为它们的完整形式。
    • 实现:通过遍历 _abbreviations 列表,使用 re.sub(正则表达式替换)将缩写替换为完整单词。

    2.expand_numbers(text):

    • 作用:将数字展开为文字形式,如 “5” 转换为 “five”。
    • 注意:这个函数实际调用的 normalize_numbers 未定义,因此在实际使用时需要实现这个功能。

    3.lowercase(text):

    • 作用:将文本转换为小写。

    4.collapse_whitespace(text):

    • 作用:通过正则表达式 _whitespace_re 将多个空白字符压缩为一个空格,确保文本的格式化一致。

    5.convert_to_ascii(text):

    • 作用:使用 unidecode 将文本中的非ASCII字符转换为其对应的ASCII字符。主要用于文本的转码,使其更容易处理和标准化。
  • 清理函数basic_cleaners

不转码和音素化的情况。主要用于快速标准化文本,不进行语言的复杂处理。

1. 将文本转换为小写。
2. 压缩空白字符。
3. 不转码和音素化的情况。主要用于快速标准化文本,不进行语言的复杂处理。
  • 清理函数transliteration_cleaners

适用于非英语文本,将文本标准化为ASCII字符,可以处理带有特殊字符的语言(如带有重音符号的法语)。

1.将文本转化为ASCII字符(通过 unidecode)。
2. 将文本转换为小写。
3.压缩空白字符。

  • 清理函数english_cleaners

    适用于处理英文文本,将其转换为音素表示。这种处理方式特别适合语音合成模型,因为音素表示能更好地指导模型生成正确的发音。

1.将文本转化为ASCII字符。
2.将文本转换为小写。
3.展开缩写。
4.使用 phonemize 将文本转换为音素(language='en-us’表示使用美式英语发音)。
5.压缩空白字符。

  • 清理函数 english_cleaners2

    和 english_cleaners 的处理步骤相同,适用于需要更细致的音素表示,特别是当标点和音调在生成语音时起重要作用的场景, 不同点如下。

      1. 保留标点符号。
      2. 包含重音标记(stress),使生成的音素包含音调信息。
    

5.3 init.py

整合文本到音素 (Text-to-Phoneme)的过程, 通过函数 text_to_sequence 实现,

  1. 先转换通常用于将文本数据转换为音素

  2. 再将转换为一个由整数(ID)组成的序列,不同ID对应于文本中的不同字符。

6. Data_utils.py

包括5个类,前2个处理Lj-Speech, 后2个处理VCTK, 最后1个分布式分割处理数据

6.1 TextAudioLoader

继承了torch.utils.data.Dataset类,除构造函数外,还需实现默认函数:

1. __getitem__ 函数定义了如何根据索引来获取单个样本(音频和文本对)。

2. __len__ 函数返回数据集的总长度。
  • 类功能:

      1. 加载音频和文本对:从给定的文件路径中加载音频文件和相应的文本。
    
      2.文本归一化:将文本标准化并转换为整数序列(通常是字符或音素的 ID)。
    
      3.计算音频的谱图:通过音频文件计算对应的 Mel 时频谱图,用于模型输入。
    
  • 使用步骤:

      1. 准备文件列表
    
        2.定义超参数(hparams):包含数据预处理和模型相关参数的对象,像音频采样率、滤波器长度、窗口长度等都
    
      3.创建 TextAudioLoader 
    

dataset = TextAudioLoader(audiopaths_and_text=‘path/to/audiopaths_and_text.txt’, hparams=hparams)

	4.使用 DataLoader 进行批处理

创建 dataset 实例对象后,可以将其传递给 torch.utils.data.DataLoader,以便在训练模型时自动加载和批处理数据。

dataloader = torch.utils.data.DataLoader(dataset, batch_size=32, shuffle=True, num_workers=4)

直接遍历 DataLoader 获取批次的数据:
for batch in dataloader:
	text, spec, wav = batch  # 在这里使用 text, spec, wav 进行训练或推理
  • 核心函数:

    1. get_audio_text_pair(self, audiopath_and_text):获取指定路径下的音频和文本,返回处理后的文本、时频谱图和音频波形。

    2. get_audio(self, filename):加载音频文件,并计算其 Mel 时频谱图。如果已缓存时频谱图文件(.spec.pt),则直接加载缓存的时频谱图。

    3. get_text(self, text):处理文本,将其转换为序列化的整数(文本 ID)。支持是否添加空白符和是否使用清洗后的文本。

6.2 TextAudioCollate

用于数据批处理(batching),为模型输入的文本、时频谱图和音频波形做统一的零填充,并根据频谱图长度进行排序,确保数据可以批量化地输入到模型中进行训练。

  • 类功能:

    1. Zero-padding:对文本、时频谱图和音频波形进行零填充,使得批次中的所有样本具有相同的长度。

    2.按序列长度排序:在整理批次时,将批次内的数据根据时频谱图长度从长到短排序。这通常用于提高模型的训练效率,尤其是在 RNN 或 Transformer 等依赖输入长度的模型中,长样本优先处理可以减少不必要的计算开销。

  • 数据处理过程:

    1.ids_sorted_decreasing:通过时频谱图的长度对批次内的样本进行降序排序。这样可以确保较长的样本在批次前面,便于之后的零填充处理。

    2.获取当前批次中最大长度的文本、时频谱图和音频波形,确保后续的零填充能够对齐到最大长度。

    3.为文本、时频谱图和音频波形分别初始化填充张量 text_padded, spec_padded, wav_padded,它们的维度对应于批次大小和最大长度。

    3.遍历排序后的批次样本,将每个样本的文本、时频谱图和音频波形按顺序填入相应的张量中。对于长度不够的部分,自动填充零。

    4.默认情况下返回零填充后的文本、频谱图、音频波形以及它们的长度,也可返回排序 ID。

(text_padded, text_lengths, spec_padded, spec_lengths, wav_padded, wav_lengths,ids_sorted_decreasing)

6.3 TextAudioSpeekerLoader

1.这个类与 TextAudioLoader 类似,区别在于它额外引入了说话人 ID,以便处理包含多个说话人的数据集。

  1. 输入音频、说话人 ID 和文本对。

格式从(audio_path, text),变为 (audio_path, speaker_id, text)

  1. 输出即返回格式为由(text, spec, wav) 变为 (text, spec, wav, sid)
  • TextAudioSpeekerCollate

与TextAudioCollate,多了一个ID号区别不同说话人

6.4 DistributedBuckerSampler

是一个用于分布式数据采样的类,它设计了一个桶(Bucker),将一个音频-文本对分为多个段(buckets),确保每个批次中的样本长度相同。

优点:提高训练时的计算效率和稳定性,数据分割为桶后,减少补零即填充操作(padding)。

缺点:单个数据的量变少,数据划分的个数变多,导致训练迭代同样次数时,数据学习的总量变少,训练时间变长。

注:这个类对单GPU训练没用,可以屏蔽掉这部分。

7. filelists文件夹

用于组织 文本-音频 对的文件, 即text-audio pair,其中音频是记录了音频的路径,后面通过函数加载对应路径的音频,转为mel时频谱

  • Lj-Speech

1行一个数据样本,用’|’ 分割 音频路径 和 文本字符串
在这里插入图片描述

  • VCTK

多了一个speaker id,用于区分是哪一个人
在这里插入图片描述

音素版如下:

末尾为 .cleaned文件

  • Lj-Speech
    在这里插入图片描述

  • VCTK
    在这里插入图片描述

Reference

  • https://github.com/jaywalnut310/vits
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值