如何在 Ubuntu 20.04 上使用 Mozilla TTS 开始训练自定义语音模型? 准备音频文件准备元数据准备 config.json 文件准备scale_stats.npy

问题描述

我想使用我录制的音频样本在 Mozilla TTS 中创建自定义语音,但不知道如何开始。 Mozilla TTS 项目有文档和教程,但我无法将这些部分放在一起 - 似乎缺少一些初学者需要了解的基本信息。

我有一些问题:

  1. 我看到 Mozilla TTS 有一个 Docker 映像,但它的文档涵盖了创建语音并且没有提到培训。我可以使用 Docker 镜像进行训练吗?
  2. 如果我无法使用 Docker 映像进行训练,我如何使用 Python 3 获取在我的系统上运行的 Mozilla TTS 的功能副本?我已尝试按照项目提供的命令进行操作,但出现依赖项错误、版本冲突或关于没有足够权限安装软件包的错误
  3. 我需要什么信息来训练模型?我需要什么音频格式?我发现我需要一个 Metadata.csv 文件——我需要在该文件中放入什么?我在配置文件自定义了什么?
  4. 大多数配置都引用了一个 scale_stats.npy 文件——我该如何生成它?
  5. 我如何开展培训?

解决方法

经过大量研究和实验后,我可以分享我的经验来回答我自己的问题。

Mozilla TTS Docker 镜像能否用于训练(TL;DR:“否”)

Mozilla TTS docker 镜像确实适合播放,似乎不适合用于训练。至少,即使在容器内运行 shell 时,我也无法进行培训。但是在弄清楚是什么导致 PIP 不满意之后,在 Ubuntu 中启动和运行 Mozilla TTS 的过程变得非常简单。

使用 Python 3、PIP 和虚拟环境安装 Mozilla TTS

Mozilla TTS 的文档没有提及任何有关虚拟环境的内容,但恕我直言,它确实应该提及。虚拟环境可确保您机器上基于 Python 的不同应用程序的依赖关系不会发生冲突。

我在 WSL 上运行 Ubuntu 20.04,所以已经安装了 Python 3。鉴于此,在我的主文件夹中,以下是我用来获取 Mozilla TTS 工作副本的命令:

sudo apt-get install espeak

git clone https://github.com/mozilla/TTS mozilla-tts
python3 -m venv mozilla-tts

cd mozilla-tts
./bin/pip install -e .

这在我的主文件夹中创建了一个名为 ~/mozilla-tts 的文件夹,其中包含 Mozilla TTS 代码。该文件夹设置为虚拟环境,这意味着只要我通过 ~/mozilla-tts/bin/python 和 PIP 通过 ~/mozilla-tts/bin/pip 执行 python 命令,Python 将只使用该虚拟环境中存在的包。这样就无需在运行 pip 时成为 root 用户(因为我们不会影响系统范围的包),并确保没有包冲突。得分!

训练模型的先决条件

为了在训练模型时获得最佳结果,您需要:

  1. 符合以下条件的短录音(至少 100 个):
    • 16 位单声道 PCM WAV 格式。
    • 在 1 到 10 秒之间。
    • 采样率为 22050 Hz。
    • 尽量减少背景噪音和失真。
    • 在开头、中间和结尾都没有长时间的沉默。
  2. 一个 metadata.csv 文件,引用每个 WAV 文件并指示 WAV 文件中所说的文本。
  3. 针对您的数据集和所选声码器(例如 Tacotron、WavGrad 等)量身定制的配置文件。
  4. 具有快速 CPU 的机器(最好是支持 CUDA 的 nVidia GPU 和至少 12 GB 的 GPU RAM;如果 GPU RAM 少于 8 GB,则无法有效使用 CUDA)。
  5. 大量 RAM(最好至少有 16 GB 的 RAM)。

准备音频文件

如果您的音频源与 WAV 格式不同,则您需要使用 AudacitySoX 之类的程序将文件转换为 WAV 格式。您还应该修剪掉来自扬声器的噪音、嗯、啊和其他声音的音频部分,而这些声音并不是您真正在训练的单词。

如果您的音频源不完美(即有一些背景噪音)、采用不同的格式,或者恰好是更高的采样率或不同的分辨率(例如 24 位、32 位等) ,您可以执行一些清理和转换。这是基于 Mozilla TTS Discourse 论坛中的 earlier script 的脚本:

from pathlib import Path

import os
import subprocess
import soundfile as sf
import pyloudnorm as pyln
import sys

src = sys.argv[1]
rnn = "/PATH/TO/rnnoise_demo"

paths = Path(src).glob("**/*.wav")

for filepath in paths:
    target_filepath=Path(str(filepath).replace("original","converted"))
    target_dir=os.path.dirname(target_filepath)

    if (str(filepath) == str(target_filepath)):
        raise ValueError("Source and target path are identical: " + str(target_filepath))

    print("From: " + str(filepath))
    print("To: " + str(target_filepath))

    # Stereo to Mono; upsample to 48000Hz
    subprocess.run(["sox",filepath,"48k.wav","remix","-","rate","48000"])
    subprocess.run(["sox","-c","1","-r","48000","-b","16","-e","signed-integer","-t","raw","temp.raw"]) # convert wav to raw
    subprocess.run([rnn,"temp.raw","rnn.raw"]) # apply rnnoise
    subprocess.run(["sox","48k","rnn.raw","wav","rnn.wav"]) # convert raw back to wav

    subprocess.run(["mkdir","-p",str(target_dir)])
    subprocess.run(["sox","rnn.wav",str(target_filepath),"highpass","100","lowpass","7000","22050"]) # apply high/low pass filter and change sr to 22050Hz

    data,rate = sf.read(target_filepath)

    # peak normalize audio to -1 dB
    peak_normalized_audio = pyln.normalize.peak(data,-1.0)

    # measure the loudness first
    meter = pyln.Meter(rate) # create BS.1770 meter
    loudness = meter.integrated_loudness(data)

    # loudness normalize audio to -25 dB LUFS
    loudness_normalized_audio = pyln.normalize.loudness(data,loudness,-25.0)

    sf.write(target_filepath,data=loudness_normalized_audio,samplerate=22050)

    print("")

要使用上面的脚本,您需要检查并构建 the RNNoise project

sudo apt update
sudo apt-get install build-essential autoconf automake gdb git libffi-dev zlib1g-dev libssl-dev

git clone https://github.com/xiph/rnnoise.git
cd rnnoise
./autogen.sh
./configure
make

您还需要安装 SoX:

sudo apt install sox

而且,您需要通过 pyloudnorm 安装 ./bin/pip

然后,只需自定义脚本,使 rnn 指向 rnnoise_demo 命令的路径(构建 RNNoise 后,您可以在 examples 文件夹中找到它)。然后,运行脚本,将源路径(您拥有 WAV 文件的文件夹)作为第一个命令行参数传递。 确保“原始”一词出现在路径中的某处。脚本会自动将转换后的文件放到对应的路径中,将original改为converted;例如,如果你的源路径是/path/to/files/original,脚本会自动放置/path/to/files/converted 中的转换结果。

准备元数据

Mozilla TTS 支持多种不同的数据加载器,但最常见的一种是 LJSpeech。要使用它,我们可以组织我们的数据集以遵循 LJSpeech 约定。

首先,组织您的文件,使其具有如下结构:

- metadata.csv
- wavs/
  - audio1.wav
  - audio2.wav
  ...
  - last_audio.wav

音频文件的命名似乎并不重要。但是,文件必须位于名为 wavs 的文件夹中。不过,如果需要,您可以在 wavs 中使用子文件夹。

metadata.csv 文件应采用以下格式:

audio1|line that's spoken in the first file
audio2|line that's spoken in the second file
last_audio|line that's spoken in the last file

注意:

  • 没有标题行。
  • 列用竖线符号 (|) 连接在一起。
  • 每个 WAV 文件应该有一行。
  • WAV 文件名在第一列中,没有 wavs/ 文件夹前缀,也没有 .wav 后缀。
  • WAV 中所说内容的文字描述写在第二列中,并拼写出所有数字和缩写。

(我确实观察到 Mozilla TTS 文档中的步骤让您将元数据文件打乱,然后将其拆分为“训练”集(metadata_train.csv)和“验证”集(metadata_val.csv ),但存储库中提供的所有示例配置都没有实际配置为使用这些文件。我已经提交了 an issue,因为这对初学者来说令人困惑/违反直觉。)

准备 config.json 文件

您需要准备一个配置文件,描述如何配置您的自定义 TTS。在准备训练、执行训练和从自定义 TTS 生成音频时,Mozilla TTS 的多个部分使用此文件。不幸的是,尽管此文件非常重要,但 Mozilla TTS 的文档在很大程度上掩盖了如何自定义此文件。

首先,从 Mozilla 存储库创建 the default Tacotron config.json file 的副本。然后,请务必至少自定义 audio.stats_pathoutput_pathphoneme_cache_pathdatasets.path 文件。

如果您愿意,您可以自定义其他参数,但默认值是一个不错的起点。例如,您可以更改 run_name 以控制包含数据集的文件夹的命名。

不要更改 datasets.name 参数(将其设置为“ljspeech”);否则,您将收到与未定义数据集类型相关的奇怪错误。 数据集 name 似乎指的是使用的 数据加载器 的类型,而不是您所说的数据集。同样,我也没有冒险更改 model 设置,因为我还不知道系统如何使用该值。

准备scale_stats.npy

大多数训练配置依赖于基于训练集生成的名为 scale_stats.npy 的统计文件。您可以使用 Mozilla TTS 存储库中的 ./TTS/bin/compute_statistics.py 脚本来生成此文件。此脚本需要您的 config.json 文件作为输入,这是检查到目前为止一切正常的良好步骤。

如果您在本教程开始时创建的 Mozilla TTS 文件夹中,您可以运行以下命令示例(调整路径以适合您的项目):

./bin/python ./TTS/bin/compute_statistics.py --config_path /path/to/your/project/config.json --out_path /path/to/your/project/scale_stats.npy

如果成功,这将在 scale_stats.npy 下生成一个 /path/to/your/project/scale_stats.npy 文件。 请确保您的 audio.stats_path 文件的 config.json 设置中的路径与此路径匹配。

训练模型

现在是关键时刻——是时候开始训练您的模型了!

如果您在本教程开始时创建的 Mozilla TTS 文件夹中,您可以运行以下命令来训练 Tacotron 模型(调整路径以适合您的项目):

./bin/python ./TTS/bin/train_tacotron.py --config_path /path/to/your/project/config.json

这个过程需要几个小时,甚至几天。如果您的机器支持 CUDA 并对其进行了正确配置,则该过程将比仅依靠 CPU 运行得更快。

如果您收到与“信号错误”或“收到信号”相关的任何错误,这通常表明您的机器没有足够的内存来执行操作。您可以run the training with less parallelism,但它会运行得更慢。