目录

使用 Tacotron2 的文本转语音

作者 Yao-Yuan YangMoto Hira

概述

本教程介绍如何使用 在 torchaudio 中预训练 Tacotron2。

文本转语音管道如下所示:

  1. 文本预处理

    首先,将输入文本编码为元件列表。在这个 教程中,我们将使用英文字符和音素作为符号。

  2. 频谱图生成

    从编码的文本中,生成频谱图。为此,我们使用 model。Tacotron2

  3. 时域转换

    最后一步是将频谱图转换为波形。这 从频谱图生成语音的过程也称为 Vocoder。 在本教程中,使用了三种不同的声码器,WaveRNNGriffin-Lim、 和 Nvidia 的 WaveGlow

整个过程如下图所示。

https://download.pytorch.org/torchaudio/tutorial-assets/tacotron2_tts_pipeline.png

所有相关组件都捆绑在 中。 但本教程还将介绍幕后过程。

制备

首先,我们安装必要的依赖项。除了 之外,还需要执行基于音素的 编码。torchaudioDeepPhonemizer

# When running this example in notebook, install DeepPhonemizer
# !pip3 install deep_phonemizer

import torch
import torchaudio
import matplotlib
import matplotlib.pyplot as plt

import IPython

matplotlib.rcParams['figure.figsize'] = [16.0, 4.8]

torch.random.manual_seed(0)
device = "cuda" if torch.cuda.is_available() else "cpu"

print(torch.__version__)
print(torchaudio.__version__)
print(device)

外:

1.10.0+cpu
0.10.0+cpu
cpu

文本处理

基于字符的编码

在本节中,我们将介绍如何使用基于字符的编码 工程。

由于预训练的 Tacotron2 模型需要一组特定的符号 表中,其功能与 中提供的功能相同。这 部分更多地用于解释编码的基础。torchaudio

首先,我们定义品种集。例如,我们可以使用 .然后,我们将映射 输入文本的每个字符都放入相应的 符号。'_-!\'(),.:;? abcdefghijklmnopqrstuvwxyz'

以下是此类处理的示例。在示例中,symbol 不在表中的 API 的 URL 将被忽略。

symbols = '_-!\'(),.:;? abcdefghijklmnopqrstuvwxyz'
look_up = {s: i for i, s in enumerate(symbols)}
symbols = set(symbols)

def text_to_sequence(text):
  text = text.lower()
  return [look_up[s] for s in text if s in symbols]

text = "Hello world! Text to speech!"
print(text_to_sequence(text))

外:

[19, 16, 23, 23, 26, 11, 34, 26, 29, 23, 15, 2, 11, 31, 16, 35, 31, 11, 31, 26, 11, 30, 27, 16, 16, 14, 19, 2]

如上所述,symbol table 和 indices 必须匹配 预训练的 Tacotron2 模型期望什么。 提供 transform 与预训练模型一起。例如,您可以 instantiate 并使用 type 的 Transform,如下所示。torchaudio

processor = torchaudio.pipelines.TACOTRON2_WAVERNN_CHAR_LJSPEECH.get_text_processor()

text = "Hello world! Text to speech!"
processed, lengths = processor(text)

print(processed)
print(lengths)

外:

tensor([[19, 16, 23, 23, 26, 11, 34, 26, 29, 23, 15,  2, 11, 31, 16, 35, 31, 11,
         31, 26, 11, 30, 27, 16, 16, 14, 19,  2]])
tensor([28], dtype=torch.int32)

该对象将文本或文本列表作为输入。 当提供文本列表时,返回的变量 表示输出中每个已处理令牌的有效长度 批。processorlengths

可以按如下方式检索中间表示。

print([processor.tokens[i] for i in processed[0, :lengths[0]]])

外:

['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!', ' ', 't', 'e', 'x', 't', ' ', 't', 'o', ' ', 's', 'p', 'e', 'e', 'c', 'h', '!']

基于音素的编码

基于音素的编码类似于基于字符的编码,但它 使用基于音素的符号表和 G2P (字素到音素) 型。

G2P 模型的细节不在本教程的讨论范围之内,我们将 看看转换是什么样子的就知道了。

与基于字符的编码类似,编码过程是 预期与预先训练的 Tacotron2 模型的训练对象相匹配。 具有用于创建流程的接口。torchaudio

下面的代码说明了如何创建和使用该过程。后 场景中,使用 package 创建 G2P 模型,并且 作者发布的预训练权重 is 获取。DeepPhonemizerDeepPhonemizer

bundle = torchaudio.pipelines.TACOTRON2_WAVERNN_PHONE_LJSPEECH

processor = bundle.get_text_processor()

text = "Hello world! Text to speech!"
with torch.inference_mode():
  processed, lengths = processor(text)

print(processed)
print(lengths)

外:

  0%|          | 0.00/63.6M [00:00<?, ?B/s]
  0%|          | 48.0k/63.6M [00:00<04:03, 274kB/s]
  0%|          | 248k/63.6M [00:00<01:24, 782kB/s]
  2%|1         | 1.02M/63.6M [00:00<00:26, 2.50MB/s]
  6%|5         | 3.58M/63.6M [00:00<00:08, 7.39MB/s]
 10%|#         | 6.53M/63.6M [00:00<00:04, 12.0MB/s]
 13%|#2        | 8.12M/63.6M [00:00<00:04, 12.1MB/s]
 17%|#7        | 10.9M/63.6M [00:01<00:03, 15.0MB/s]
 20%|#9        | 12.7M/63.6M [00:01<00:03, 14.5MB/s]
 24%|##4       | 15.4M/63.6M [00:01<00:03, 16.5MB/s]
 27%|##6       | 17.1M/63.6M [00:01<00:03, 15.6MB/s]
 31%|###1      | 19.8M/63.6M [00:01<00:02, 17.1MB/s]
 34%|###4      | 21.7M/63.6M [00:01<00:02, 16.3MB/s]
 38%|###8      | 24.2M/63.6M [00:01<00:02, 17.3MB/s]
 41%|####1     | 26.1M/63.6M [00:02<00:02, 16.6MB/s]
 45%|####4     | 28.6M/63.6M [00:02<00:02, 17.4MB/s]
 48%|####8     | 30.6M/63.6M [00:02<00:02, 16.9MB/s]
 52%|#####1    | 33.0M/63.6M [00:02<00:01, 17.4MB/s]
 55%|#####5    | 35.0M/63.6M [00:02<00:01, 16.8MB/s]
 59%|#####8    | 37.4M/63.6M [00:02<00:01, 17.4MB/s]
 62%|######2   | 39.5M/63.6M [00:02<00:01, 16.9MB/s]
 66%|######5   | 41.9M/63.6M [00:03<00:01, 17.6MB/s]
 69%|######8   | 43.9M/63.6M [00:03<00:01, 16.8MB/s]
 73%|#######2  | 46.3M/63.6M [00:03<00:01, 17.5MB/s]
 76%|#######5  | 48.3M/63.6M [00:03<00:00, 16.9MB/s]
 80%|#######9  | 50.8M/63.6M [00:03<00:00, 17.4MB/s]
 83%|########2 | 52.7M/63.6M [00:03<00:00, 16.9MB/s]
 87%|########6 | 55.2M/63.6M [00:03<00:00, 17.4MB/s]
 90%|########9 | 57.1M/63.6M [00:03<00:00, 16.9MB/s]
 94%|#########3| 59.6M/63.6M [00:04<00:00, 17.4MB/s]
 97%|#########6| 61.5M/63.6M [00:04<00:00, 16.8MB/s]
100%|##########| 63.6M/63.6M [00:04<00:00, 15.4MB/s]
tensor([[54, 20, 65, 69, 11, 92, 44, 65, 38,  2, 11, 81, 40, 64, 79, 81, 11, 81,
         20, 11, 79, 77, 59, 37,  2]])
tensor([25], dtype=torch.int32)

请注意,编码值与 基于字符的编码。

中间表示形式如下所示。

print([processor.tokens[i] for i in processed[0, :lengths[0]]])

外:

['HH', 'AH', 'L', 'OW', ' ', 'W', 'ER', 'L', 'D', '!', ' ', 'T', 'EH', 'K', 'S', 'T', ' ', 'T', 'AH', ' ', 'S', 'P', 'IY', 'CH', '!']

频谱图生成

Tacotron2是我们用来从 编码文本。型号详情请参考

使用预训练权重实例化 Tacotron2 模型很容易, 但是,请注意,需要处理 Tacotron2 模型的输入 通过匹配的文本处理器。

捆绑匹配的 模型和处理器组合在一起,以便轻松创建管道。

有关可用捆绑包及其用法,请参阅

bundle = torchaudio.pipelines.TACOTRON2_WAVERNN_PHONE_LJSPEECH
processor = bundle.get_text_processor()
tacotron2 = bundle.get_tacotron2().to(device)

text = "Hello world! Text to speech!"

with torch.inference_mode():
  processed, lengths = processor(text)
  processed = processed.to(device)
  lengths = lengths.to(device)
  spec, _, _ = tacotron2.infer(processed, lengths)


plt.imshow(spec[0].cpu().detach())
Tacotron2 流水线教程

外:

Downloading: "https://download.pytorch.org/torchaudio/models/tacotron2_english_phonemes_1500_epochs_wavernn_ljspeech.pth" to /root/.cache/torch/hub/checkpoints/tacotron2_english_phonemes_1500_epochs_wavernn_ljspeech.pth

  0%|          | 0.00/107M [00:00<?, ?B/s]
  7%|7         | 7.83M/107M [00:00<00:01, 81.9MB/s]
 15%|#4        | 15.6M/107M [00:00<00:01, 78.6MB/s]
 47%|####7     | 50.9M/107M [00:00<00:00, 209MB/s]
 82%|########1 | 87.9M/107M [00:00<00:00, 278MB/s]
100%|##########| 107M/107M [00:00<00:00, 245MB/s]

<matplotlib.image.AxesImage object at 0x7fb3cc9ca220>

请注意,method perfos multinomial sampling, 因此,生成频谱图的过程会产生随机性。Tacotron2.infer

fig, ax = plt.subplots(3, 1, figsize=(16, 4.3 * 3))
for i in range(3):
  with torch.inference_mode():
    spec, spec_lengths, _ = tacotron2.infer(processed, lengths)
  print(spec[0].shape)
  ax[i].imshow(spec[0].cpu().detach())
plt.show()
Tacotron2 流水线教程

外:

torch.Size([80, 155])
torch.Size([80, 167])
torch.Size([80, 164])

波形生成

生成频谱图后,最后一个过程是恢复 waveform 来自频谱图。

torchaudio提供基于 和 的声码器。GriffinLimWaveRNN

WaveRNN

继续上一节,我们可以实例化匹配的 WaveRNN 模型。

bundle = torchaudio.pipelines.TACOTRON2_WAVERNN_PHONE_LJSPEECH

processor = bundle.get_text_processor()
tacotron2 = bundle.get_tacotron2().to(device)
vocoder = bundle.get_vocoder().to(device)

text = "Hello world! Text to speech!"

with torch.inference_mode():
  processed, lengths = processor(text)
  processed = processed.to(device)
  lengths = lengths.to(device)
  spec, spec_lengths, _ = tacotron2.infer(processed, lengths)
  waveforms, lengths = vocoder(spec, spec_lengths)

fig, [ax1, ax2] = plt.subplots(2, 1, figsize=(16, 9))
ax1.imshow(spec[0].cpu().detach())
ax2.plot(waveforms[0].cpu().detach())

torchaudio.save("_assets/output_wavernn.wav", waveforms[0:1].cpu(), sample_rate=vocoder.sample_rate)
IPython.display.Audio("_assets/output_wavernn.wav")
Tacotron2 流水线教程

外:

Downloading: "https://download.pytorch.org/torchaudio/models/wavernn_10k_epochs_8bits_ljspeech.pth" to /root/.cache/torch/hub/checkpoints/wavernn_10k_epochs_8bits_ljspeech.pth

  0%|          | 0.00/16.7M [00:00<?, ?B/s]
100%|##########| 16.7M/16.7M [00:00<00:00, 298MB/s]


林磊

使用 Griffin-Lim 声码器与 WaveRNN 相同。您可以实例化 vocode 对象 和 method 并传递 spectrogram。get_vocoder

bundle = torchaudio.pipelines.TACOTRON2_GRIFFINLIM_PHONE_LJSPEECH

processor = bundle.get_text_processor()
tacotron2 = bundle.get_tacotron2().to(device)
vocoder = bundle.get_vocoder().to(device)

with torch.inference_mode():
  processed, lengths = processor(text)
  processed = processed.to(device)
  lengths = lengths.to(device)
  spec, spec_lengths, _ = tacotron2.infer(processed, lengths)
waveforms, lengths = vocoder(spec, spec_lengths)

fig, [ax1, ax2] = plt.subplots(2, 1, figsize=(16, 9))
ax1.imshow(spec[0].cpu().detach())
ax2.plot(waveforms[0].cpu().detach())

torchaudio.save("_assets/output_griffinlim.wav", waveforms[0:1].cpu(), sample_rate=vocoder.sample_rate)
IPython.display.Audio("_assets/output_griffinlim.wav")
Tacotron2 流水线教程

外:

Downloading: "https://download.pytorch.org/torchaudio/models/tacotron2_english_phonemes_1500_epochs_ljspeech.pth" to /root/.cache/torch/hub/checkpoints/tacotron2_english_phonemes_1500_epochs_ljspeech.pth

  0%|          | 0.00/107M [00:00<?, ?B/s]
 28%|##8       | 30.6M/107M [00:00<00:00, 321MB/s]
 57%|#####6    | 61.2M/107M [00:00<00:00, 308MB/s]
 90%|######### | 96.9M/107M [00:00<00:00, 337MB/s]
100%|##########| 107M/107M [00:00<00:00, 335MB/s]


波辉

Waveglow 是 Nvidia 发布的声码器。预训练权重为 在 Torch Hub 上发布。可以使用 module 实例化模型。torch.hub

# Workaround to load model mapped on GPU
# https://stackoverflow.com/a/61840832
waveglow = torch.hub.load('NVIDIA/DeepLearningExamples:torchhub', 'nvidia_waveglow', model_math='fp32', pretrained=False)
checkpoint = torch.hub.load_state_dict_from_url('https://api.ngc.nvidia.com/v2/models/nvidia/waveglowpyt_fp32/versions/1/files/nvidia_waveglowpyt_fp32_20190306.pth', progress=False, map_location=device)
state_dict = {key.replace("module.", ""): value for key, value in checkpoint["state_dict"].items()}

waveglow.load_state_dict(state_dict)
waveglow = waveglow.remove_weightnorm(waveglow)
waveglow = waveglow.to(device)
waveglow.eval()

with torch.no_grad():
  waveforms = waveglow.infer(spec)

fig, [ax1, ax2] = plt.subplots(2, 1, figsize=(16, 9))
ax1.imshow(spec[0].cpu().detach())
ax2.plot(waveforms[0].cpu().detach())

torchaudio.save("_assets/output_waveglow.wav", waveforms[0:1].cpu(), sample_rate=22050)
IPython.display.Audio("_assets/output_waveglow.wav")
Tacotron2 流水线教程

外:

Downloading: "https://github.com/NVIDIA/DeepLearningExamples/archive/torchhub.zip" to /root/.cache/torch/hub/torchhub.zip
/root/.cache/torch/hub/NVIDIA_DeepLearningExamples_torchhub/PyTorch/Classification/ConvNets/image_classification/models/common.py:13: UserWarning: pytorch_quantization module not found, quantization will not be available
  warnings.warn(
/root/.cache/torch/hub/NVIDIA_DeepLearningExamples_torchhub/PyTorch/Classification/ConvNets/image_classification/models/efficientnet.py:17: UserWarning: pytorch_quantization module not found, quantization will not be available
  warnings.warn(
/root/.cache/torch/hub/NVIDIA_DeepLearningExamples_torchhub/PyTorch/SpeechSynthesis/Tacotron2/waveglow/model.py:55: UserWarning: torch.qr is deprecated in favor of torch.linalg.qr and will be removed in a future PyTorch release.
The boolean parameter 'some' has been replaced with a string parameter 'mode'.
Q, R = torch.qr(A, some)
should be replaced with
Q, R = torch.linalg.qr(A, 'reduced' if some else 'complete') (Triggered internally at  ../aten/src/ATen/native/BatchLinearAlgebra.cpp:1937.)
  W = torch.qr(torch.FloatTensor(c, c).normal_())[0]
Downloading: "https://api.ngc.nvidia.com/v2/models/nvidia/waveglowpyt_fp32/versions/1/files/nvidia_waveglowpyt_fp32_20190306.pth" to /root/.cache/torch/hub/checkpoints/nvidia_waveglowpyt_fp32_20190306.pth


脚本总运行时间:(2 分 35.062 秒)

由 Sphinx-Gallery 生成的图库

文档

访问 PyTorch 的全面开发人员文档

查看文档

教程

获取面向初学者和高级开发人员的深入教程

查看教程

资源

查找开发资源并解答您的问题

查看资源