亚马逊AWS官方博客

构建文生音场景定制化人声解决方案

1. 概述

在社交和阅读这类涉及内容互动的产品中,会用到文字转语音能力支持交互的使用场景,亚马逊云科技提供了云原生的 TTS 服务 Amazon Polly(https://thinkwithwp.com/polly/) ,Amazon Polly 使用深度学习技术来合成听起来自然的人类语音,借助多种语言的数十种逼真的声音,让您可以将文字或者文章转换为语音。它也支持通过 SSML 这种基于 XML 的 W3C 标准标记语言,支持生成的语言去调整说话风格、语速、音调和音量。但是,在一些产品中,需要去通过定制化的人声去生成语音,本篇文章提供了一个基于开源项目构建的解决方案供大家参考。

2. 架构描述

本方案是基于两个开源项目构建,So-Vits-SVC 支持自定义音色模型的训练和对给定语音做音色替换。TTS 部分可以选择 Amazon Polly,或者 Bark 这个开源的 TTS 项目。以下是解决方案的架构示意图。

  1. Amazon Polly 支持文字生音频
  2. 使用 SageMaker Notebook 部署 Bark TTS 开源项目支持文字生音频
  3. API Server 为业务接口服务,可以按照实际业务场景做集成开发(本文暂未实现)
  4. So-Vits-SVC Training Server 用来部署自定义人声模型训练
  5. So-Vits-SVC Inference Server 用来部署自定义人声推理服务
  6. Amazon S3 对象存储,支持保存音频训练数据集和生成的模型

3. 构建测试环境

3.1 So-Vits-SVC 服务环境

So-Vits-SVC 是基于 VITS 的开源项目,VITS(Variational Inference with adversarial learning for end-to-end Text-to-Speech)是一种结合变分推理(variational inference)、标准化流(normalizing flows)和对抗训练的高表现力语音合成模型。So-Vits-SVC 项目在 Github 上目前更新到 4.1-Stable 版本。

创建 GPU 的 EC2 实例,部署代码和安装环境依赖

训练和推理的过程需要使用 GPU,本测试使用 Amazon EC2 G4dn.2xlarge 机型(NVIDIA T4 GPU),建议使用亚马逊云科技官方提供的 Deep Learning AMI GPU PyTorch 1.13.1 (Ubuntu 20.04) 系统镜像。创建 EC2 的配置如下图所示,EBS 磁盘建议 100GB 以上。

启动实例后,通过 SSH 工具登录的终端界面,把项目代码 clone 到本地目录。

git clone https://github.com/svc-develop-team/so-vits-svc.git -b 4.1-Stable

安装 Conda 定制工作环境,建议使用 python 3.8 版本

# 安装官网最新版(Anaconda3-2021.05-Linux-x86_64.sh)
wget https://repo.anaconda.com/archive/Anaconda3-2021.05-Linux-x86_64.sh
bash Anaconda3-2021.05-Linux-x86_64.sh

# 安装完成后,每次进入终端会默认使用 conda 环境,可以输入以下命令关闭默认进入 conda:
conda config --set auto_activate_base false
conda init bash

# 创建 so-vits-svc 的工作环境
conda create -n sovits-38 python=3.8
conda activate sovits-38

安装依赖的 Libs

# 指定 requirements.txt 文件的依赖
(sovits-38) ~$ cd so-vits-svc
(sovits-38) ~/so-vits-svc$ pip install -r requirements.txt

训练流程配置 – 数据集准备

我们需要准备目标人物的音频素材(如果是需要以你自己声音为基础的转换模型,则需要录制你自己的声音)。音频素材的多少会直接影响到你模型训练的质量,一般建议提供 120 分钟以上的人声素材。

如果准备的音频素材带有背景音,建议可以使用 UVR 工具(https://ultimatevocalremover.com/)进行人声和背景声的分离处理,推荐使用 Demucs – v3/UVR_Model_1 模型。

如果需要切割声音,转换到 wav 格式,可以使用 FFMPEG。

ffmpeg -i somefile.mp3 -f segment -segment_time 60 -c copy out%03d.wav

准备好的声音文件,按照 speaker 命名文件夹上传到 dataset_raw 目录,具体结构参考下图。

声音底模和相关依赖模型准备(其他依赖模型可以参考项目 README 说明)

checkpoint_best_legacy_500.pt (必须) 放在./pretrain 目录下

预训练底模文件 G_0.pth D_0.pth (推荐使用)放在./logs/44k 目录下

配置文件准备

在 ./logs/44k 目录下,名为 config.json 的配置文件定义了训练相关的参数。

需要修改 【”n_speakers”: 200】和【”speaker0″: 0】,这里 speaker0 需要和声音数据集目录名保持一致。

{
  "train": {
    "log_interval": 200,
    "eval_interval": 800,
    "seed": 1234,
    "epochs": 10000,
    "learning_rate": 0.0001,
    "betas": [
      0.8,
      0.99
    ],
    "eps": 1e-09,
    "batch_size": 6,
    "fp16_run": false,
    "half_type": "fp16",
    "lr_decay": 0.999875,
    "segment_size": 10240,
    "init_lr_ratio": 1,
    "warmup_epochs": 0,
    "c_mel": 45,
    "c_kl": 1.0,
    "use_sr": true,
    "max_speclen": 512,
    "port": "8001",
    "keep_ckpts": 3,
    "all_in_mem": false,
    "vol_aug": false
  },
  "data": {
    "training_files": "filelists/train.txt",
    "validation_files": "filelists/val.txt",
    "max_wav_value": 32768.0,
    "sampling_rate": 44100,
    "filter_length": 2048,
    "hop_length": 512,
    "win_length": 2048,
    "n_mel_channels": 80,
    "mel_fmin": 0.0,
    "mel_fmax": 22050,
    "unit_interpolate_mode": "nearest"
  },
  "model": {
    "inter_channels": 192,
    "hidden_channels": 192,
    "filter_channels": 768,
    "n_heads": 2,
    "n_layers": 6,
    "kernel_size": 3,
    "p_dropout": 0.1,
    "resblock": "1",
    "resblock_kernel_sizes": [
      3,
      7,
      11
    ],
    "resblock_dilation_sizes": [
      [
        1,
        3,
        5
      ],
      [
        1,
        3,
        5
      ],
      [
        1,
        3,
        5
      ]
    ],
    "upsample_rates": [
      8,
      8,
      2,
      2,
      2
    ],
    "upsample_initial_channel": 512,
    "upsample_kernel_sizes": [
      16,
      16,
      4,
      4,
      4
    ],
    "n_layers_q": 3,
    "n_flow_layer": 4,
    "use_spectral_norm": false,
    "gin_channels": 768,
    "ssl_dim": 768,
    "n_speakers": 200,
    "vocoder_name": "nsf-hifigan",
    "speech_encoder": "vec768l12",
    "speaker_embedding": false,
    "vol_embedding": false,
    "use_depthwise_conv": false,
    "flow_share_parameter": false,
    "use_automatic_f0_prediction": true
  },
  "spk": {
    "speaker0": 0
  }
}

训练命令参考

# 重采样,成功运行后,在.\dataset\44k文件夹中会有说话人的wav语音
(sovits-38) ~/so-vits-svc$ python resample.py
CPU count: 8
./dataset_raw/speaker0
100%|███████████████████████████████| 32/32 [00:00<00:00, 66.65it/s]

# 自动划分训练集,验证集,测试集,自动生成配置文件
(sovits-38) ~/so-vits-svc$ python preprocess_flist_config.py
100%|███████████████████████████████| 1/1 [00:00<00:00, 872.54it/s]
Writing ./filelists/train.txt
100%|███████████████████████████████| 30/30 [00:00<00:00, 687590.82it/s]
Writing ./filelists/val.txt
100%|███████████████████████████████| 2/2 [00:00<00:00, 80659.69it/s]
Writing configs/config.json
Writing configs/diffusion.yaml

# 生成hubert和f0
(sovits-38) ~/so-vits-svc$ python preprocess_hubert_f0.py
vec768l12
dio
False
  0%|                                                                                                                                                                                                                | 0/1 [00:00<?, ?it/s]Loading speech encoder for content...
load model(s) from pretrain/checkpoint_best_legacy_500.pt
Loaded speech encoder.
100%|█████████████████████████████████| 32/32 [01:22<00:00,  2.58s/it]
100%|█████████████████████████████████| 1/1 [01:29<00:00, 89.91s/it]

# 执行训练命令
(sovits-38) ~/so-vits-svc$ python train.py -c configs/config.json -m 44k
INFO:44k:{'train': {'log_interval': 200, 'eval_interval': 800, 'seed': 1234, 'epochs': 10000, 'learning_rate': 0.0001, 'betas': [0.8, 0.99], 'eps': 1e-09, 'batch_size': 6, 'fp16_run': False, 'half_type': 'fp16', 'lr_decay': 0.999875, 'segment_size': 10240, 'init_lr_ratio': 1, 'warmup_epochs': 0, 'c_mel': 45, 'c_kl': 1.0, 'use_sr': True, 'max_speclen': 512, 'port': '8001', 'keep_ckpts': 3, 'all_in_mem': False, 'vol_aug': False}, 'data': {'training_files': 'filelists/train.txt', 'validation_files': 'filelists/val.txt', 'max_wav_value': 32768.0, 'sampling_rate': 44100, 'filter_length': 2048, 'hop_length': 512, 'win_length': 2048, 'n_mel_channels': 80, 'mel_fmin': 0.0, 'mel_fmax': 22050, 'unit_interpolate_mode': 'nearest'}, 'model': {'inter_channels': 192, 'hidden_channels': 192, 'filter_channels': 768, 'n_heads': 2, 'n_layers': 6, 'kernel_size': 3, 'p_dropout': 0.1, 'resblock': '1', 'resblock_kernel_sizes': [3, 7, 11], 'resblock_dilation_sizes': [[1, 3, 5], [1, 3, 5], [1, 3, 5]], 'upsample_rates': [8, 8, 2, 2, 2], 'upsample_initial_channel': 512, 'upsample_kernel_sizes': [16, 16, 4, 4, 4], 'n_layers_q': 3, 'n_flow_layer': 4, 'use_spectral_norm': False, 'gin_channels': 768, 'ssl_dim': 768, 'n_speakers': 1, 'vocoder_name': 'nsf-hifigan', 'speech_encoder': 'vec768l12', 'speaker_embedding': False, 'vol_embedding': False, 'use_depthwise_conv': False, 'flow_share_parameter': False, 'use_automatic_f0_prediction': True}, 'spk': {'speaker0': 0}, 'model_dir': './logs/44k'}
./logs/44k/G_0.pth
emb_g.weight is not in the checkpoint,please check your checkpoint.If you're using pretrain model,just ignore this warning.
INFO:44k:emb_g.weight is not in the checkpoint
load
INFO:44k:Loaded checkpoint './logs/44k/G_0.pth' (iteration 0)
./logs/44k/D_0.pth
load
INFO:44k:Loaded checkpoint './logs/44k/D_0.pth' (iteration 0)
./logs/44k/D_0.pth
/opt/conda/envs/sovits-38/lib/python3.8/site-packages/torch/autograd/__init__.py:200: UserWarning: Grad strides do not match bucket view strides. This may indicate grad was not created according to the gradient layout contract, or that the param's strides changed since DDP was constructed.  This is not an error, but may impair performance.
grad.sizes() = [32, 1, 4], strides() = [4, 1, 1]
bucket_view.sizes() = [32, 1, 4], strides() = [4, 4, 1] (Triggered internally at ../torch/csrc/distributed/c10d/reducer.cpp:323.)
  Variable._execution_engine.run_backward(  # Calls into the C++ engine to run the backward pass
INFO:44k:====> Epoch: 1, cost 25.09 s
INFO:44k:====> Epoch: 2, cost 8.45 s
INFO:44k:====> Epoch: 3, cost 8.64 s
INFO:44k:====> Epoch: 4, cost 8.58 s
INFO:44k:====> Epoch: 5, cost 8.48 s

获取训练后的模型

配置文件默认的 【”epochs”: 10000】和 【”eval_interval”: 800】配置是训练 10000 epochs,每 800 epochs 生成一个过程中的模型,例如 G_800.pth/D_800.pth,实际推理过程中,我们只需要 G_***.pth 这个模型文件即可,一般建议选择 5000 epochs 以上的模型去测试效果。

3.2 准备原始语音

a. 通过 Amazon Polly 生成语音文件

在亚马逊云的控制台,打开 Polly 的服务界面,目前 Polly 支持三种语音生成类型(Neural/Long-Form/Standard),输入需要需要生成语音的文字,点击 Listen 按钮试听效果,或者点击 Download 按钮把音频下载到本地。我们还可以使用 SSML 去定义更丰富的语音表达能力。具体的使用指南请参考使用文档(https://docs.thinkwithwp.com/polly/latest/dg/getting-started-console.html)。

b. 通过开源项目 Bark 生成语音文件

Bark 是由 Suno 创建的基于 Transformer 的 TTS 模型。Bark 可以生成高度逼真的多语言语音以及其他音频 – 包括音乐、背景噪音和简单的音效。项目地址(https://github.com/suno-ai/bark)。工程目录中提供了Jupyter Notebook 的样例可以方便测试。

我们使用亚马逊云科技的 SageMaker Notebook 服务,可以很方便地部署和测试。

创建 GPU 的 Notebook,建议使用 G4dn 或者 G5 的机型,分别对应 GPU 是 Nvidia T4 和 Nvidia A10G。

打开 Jupyter Notebook 的使用界面,下载 bark 的工程代码到 Notebook 的工作目录,在 bar/notebooks 路径下,打开 long_form_generation.ipynb 文件,可以按照样例代码生成指定内容的音频文件,且支持下载到本地。

4. 自定义人声替换

So-Vits-SVC 项目提供了 webUI 方便测试,我们可以修改工程目录下的 webUI.py 文件,开放 EC2 的安全组 8080 端口,通过本地浏览器直接访问。

修改 webUI.py 文件,开放对外访问端口,在文件的最后一行,把 app.launch() 修改为 app.launch(server_name=’0.0.0.0′, server_port=8080)

# 启动 webUI
(sovits-38) ~/so-vits-svc$ python webUI.py
sh: 1: start: not found
Running on local URL:  http://0.0.0.0:8080

To create a public link, set `share=True` in `launch()`

在浏览器地址栏输入 HTTP://PublicIP:8080 访问 webUI dashboard,上传生成的模型文件和对应的 config.json 文件,点击加载模型按钮,加载完成可以看到我们训练的人声定义名称。

推理设置,需要打开自动 F0 预测,选择 dio 预测器,其他配置保持默认。

把需要转换的源音频文件拖入上传区域,点击音频转换按钮生成。

推理生成的音频支持在线播放和下载。

5. 总结

本文提供了文生音场景定制化人声解决方案的一种参考实现。在 TTS 场景,我们可以按照实际业务场景选择 Amazon Polly 或者开源产品 Bark 等多种实现。在自定义人声转换能力方面,So-Vits-SVC 是目前比较热门的一个开源项目,提供了多种编码器和可调参数,可以通过测试去不断优化模型训练的效果。

参考文档

  1. So-Vits-SVC 官方 Github:https://github.com/svc-develop-team/so-vits-svc
  2. Bark 官方 Github:https://github.com/suno-ai/bark
  3. Amazon Polly:https://thinkwithwp.com/cn/polly/
  4. Amazon SageMaker:https://thinkwithwp.com/cn/pm/sagemaker/
  5. Amazon SageMaker Notebook:https://docs.thinkwithwp.com/sagemaker/latest/dg/nbi.html

本篇作者

原野

AWS 资深客户经理,负责 AI 行业。

唐健

AWS 解决方案架构师,负责基于 AWS 的云计算方案的架构设计,同时致力于 AWS 云服务在移动应用与互联网行业的应用和推广。拥有多年移动互联网研发及技术团队管理经验,丰富的互联网应用架构项目经历。