音频课程文档

文本转语音的预训练模型

Hugging Face's logo
加入 Hugging Face 社区

并获得增强的文档体验

开始使用

文本转语音的预训练模型

与 ASR(自动语音识别)和音频分类任务相比,可用的预训练模型检查点明显较少。在 🤗 Hub 上,你会发现将近 300 个合适的检查点。在这些预训练模型中,我们将重点关注 🤗 Transformers 库中现成的两种架构——SpeechT5 和 Massive Multilingual Speech (MMS)。在本节中,我们将探讨如何在 Transformers 库中使用这些预训练模型进行 TTS。

SpeechT5

SpeechT5 是微软的 Junyi Ao 等人发布的一个模型,能够处理一系列语音任务。虽然在本单元中,我们侧重于文本转语音方面,但该模型可以用于语音转文本任务(自动语音识别或说话人识别),以及语音转语音任务(例如语音增强或不同声音之间的转换)。这得益于模型的设计和预训练方式。

SpeechT5 的核心是一个常规的 Transformer 编码器-解码器模型。与其他 Transformer 一样,编码器-解码器网络使用隐藏表示对序列到序列的转换进行建模。这个 Transformer 主干对于 SpeechT5 支持的所有任务都是相同的。

这个 Transformer 辅以六个特定于模态(语音/文本)的 pre-netspost-nets。输入语音或文本(取决于任务)通过相应的 pre-net 进行预处理,以获得 Transformer 可以使用的隐藏表示。然后,Transformer 的输出被传递到 post-net,后者将使用它在目标模态中生成输出。

这就是该架构的样子(图片来自原始论文)

SpeechT5 architecture from the original paper

SpeechT5 首先使用大规模的未标记语音和文本数据进行预训练,以获得不同模态的统一表示。在预训练阶段,所有 pre-nets 和 post-nets 同时使用。

预训练后,整个编码器-解码器主干针对每个单独的任务进行微调。在此步骤中,仅使用与特定任务相关的 pre-nets 和 post-nets。例如,要将 SpeechT5 用于文本转语音,您需要用于文本输入的文本编码器 pre-net 和用于语音输出的语音解码器 pre-net 和 post-net。

这种方法可以获得针对不同语音任务进行微调的多个模型,所有这些模型都受益于对未标记数据的初始预训练。

尽管微调模型最初使用来自共享预训练模型的相同权重集,但最终版本都大相径庭。例如,您不能拿一个微调过的 ASR 模型,然后替换掉 pre-nets 和 post-net 来获得一个可用的 TTS 模型。SpeechT5 很灵活,但没那么灵活;)

让我们看看 SpeechT5 专门用于 TTS 任务的 pre-nets 和 post-nets 是什么样子的

  • 文本编码器前置网络:一个文本嵌入层,将文本标记映射到编码器期望的隐藏表示。这类似于 BERT 等 NLP 模型中发生的情况。
  • 语音解码器前置网络:它将对数梅尔谱图作为输入,并使用一系列线性层将谱图压缩成隐藏表示。
  • 语音解码器后置网络:它预测要添加到输出谱图的残差,并用于优化结果。

结合起来,这就是 SpeechT5 文本转语音架构的样子

SpeechT5 architecture for TTS

如您所见,输出是对数梅尔谱图,而不是最终的波形。如果您还记得,我们在第 3 单元中简要提到了这个主题。生成音频的模型通常会生成对数梅尔谱图,需要使用称为声码器的额外神经网络将其转换为波形。

让我们看看如何做到这一点。

首先,让我们从 🤗 Hub 加载经过微调的 TTS SpeechT5 模型,以及用于分词和特征提取的处理器对象

from transformers import SpeechT5Processor, SpeechT5ForTextToSpeech

processor = SpeechT5Processor.from_pretrained("microsoft/speecht5_tts")
model = SpeechT5ForTextToSpeech.from_pretrained("microsoft/speecht5_tts")

接下来,对输入文本进行分词。

inputs = processor(text="Don't count the days, make the days count.", return_tensors="pt")

SpeechT5 TTS 模型不仅限于为单个说话人创建语音。相反,它使用所谓的说话人嵌入来捕捉特定说话人的声音特征。

说话人嵌入是一种以紧凑方式表示说话人身份的方法,无论语音长度如何,都以固定大小的向量表示。这些嵌入捕捉了说话人声音、口音、语调和其他区分不同说话人的独特特征的基本信息。此类嵌入可用于说话人验证、说话人识别、说话人分离等。生成说话人嵌入的最常用技术包括:

  • I-Vectors(身份向量):I-Vectors 基于高斯混合模型(GMM)。它们将说话人表示为从说话人特定 GMM 的统计数据派生的低维固定长度向量,并以无监督方式获得。
  • X-Vectors:X-Vectors 是使用深度神经网络(DNN)派生的,通过结合时间上下文来捕获帧级说话人信息。

X-Vectors 是一种最先进的方法,与 I-Vectors 相比,在评估数据集上表现出卓越的性能。深度神经网络用于获取 X-Vectors:它训练以区分说话人,并将可变长度的语音映射到固定维度的嵌入。您还可以加载预先计算的 X-Vector 说话人嵌入,它将封装特定说话人的说话特征。

让我们从 Hub 上的数据集中加载这样的说话人嵌入。这些嵌入是使用此脚本CMU ARCTIC 数据集中获得的,但任何 X-Vector 嵌入都应该有效。

from datasets import load_dataset

embeddings_dataset = load_dataset("Matthijs/cmu-arctic-xvectors", split="validation")

import torch

speaker_embeddings = torch.tensor(embeddings_dataset[7306]["xvector"]).unsqueeze(0)

说话人嵌入是一个形状为 (1, 512) 的张量。这个特定的说话人嵌入描述了一个女性声音。

此时,我们已经有足够的输入来生成对数梅尔谱图作为输出,您可以这样做

spectrogram = model.generate_speech(inputs["input_ids"], speaker_embeddings)

这会输出一个形状为 (140, 80) 的张量,其中包含对数梅尔谱图。第一个维度是序列长度,它在不同运行之间可能会有所不同,因为语音解码器前置网络总是将 dropout 应用于输入序列。这会给生成的语音添加一些随机变异性。

然而,如果我们要生成语音波形,我们需要指定一个声码器用于谱图到波形的转换。理论上,您可以使用任何适用于 80 频段梅尔谱图的声码器。方便的是,🤗 Transformers 提供了一个基于 HiFi-GAN 的声码器。它的权重由 SpeechT5 的原始作者慷慨提供。

HiFi-GAN 是一种最先进的生成对抗网络 (GAN),专为高保真语音合成而设计。它能够从谱图输入生成高质量和逼真的音频波形。

从高层次看,HiFi-GAN 由一个生成器和两个判别器组成。生成器是一个全卷积神经网络,它将梅尔谱图作为输入,并学习生成原始音频波形。判别器的作用是区分真实音频和生成音频。这两个判别器侧重于音频的不同方面。

HiFi-GAN 在大量高质量音频录音数据集上进行训练。它使用一种所谓的 对抗性训练,其中生成器和判别器网络相互竞争。最初,生成器会生成低质量的音频,判别器可以很容易地将其与真实音频区分开来。随着训练的进行,生成器会改进其输出,旨在欺骗判别器。反过来,判别器在区分真实音频和生成音频方面变得更加准确。这种对抗性反馈循环有助于两个网络随着时间的推移而改进。最终,HiFi-GAN 学会了生成与训练数据特征非常相似的高保真音频。

加载声码器就像加载任何其他 🤗 Transformers 模型一样简单。

from transformers import SpeechT5HifiGan

vocoder = SpeechT5HifiGan.from_pretrained("microsoft/speecht5_hifigan")

现在您所需要做的就是在生成语音时将其作为参数传递,输出将自动转换为语音波形。

speech = model.generate_speech(inputs["input_ids"], speaker_embeddings, vocoder=vocoder)

让我们听听结果。SpeechT5 使用的采样率始终为 16 kHz。

from IPython.display import Audio

Audio(speech, rate=16000)

太棒了!

随时可以试用 SpeechT5 文本转语音演示,探索其他声音,尝试不同的输入。请注意,此预训练检查点仅支持英语

Bark

Bark 是 Suno AI 在 suno-ai/bark 中提出的基于 Transformer 的文本转语音模型。

与 SpeechT5 不同,Bark 直接生成原始语音波形,无需在推理过程中使用单独的声码器——它已经集成。这种效率是通过利用 Encodec 实现的,它既充当编解码器又充当压缩工具。

使用 Encodec,您可以将音频压缩成轻量级格式以减少内存使用,然后对其进行解压缩以恢复原始音频。此压缩过程由 8 个码本促进,每个码本都由整数向量组成。可以将这些码本视为音频的整数形式表示或嵌入。重要的是要注意,每个后续码本都会提高从先前码本重建音频的质量。由于码本是整数向量,因此可以通过 Transformer 模型学习它们,这些模型在该任务中非常高效。这就是 Bark 专门训练的目的。

更具体地说,Bark 由 4 个主要模型组成

  • BarkSemanticModel(也称为“文本”模型):一个因果自回归 Transformer 模型,它将分词后的文本作为输入,并预测捕获文本含义的语义文本标记。
  • BarkCoarseModel(也称为“粗略声学”模型):一个因果自回归 Transformer,它将 BarkSemanticModel 模型的结果作为输入。它的目标是预测 EnCodec 所需的前两个音频码本。
  • BarkFineModel(“精细声学”模型),这次是一个非因果自编码器 Transformer,它基于先前码本嵌入的总和迭代地预测最后一个码本。
  • 预测了来自 EncodecModel 的所有码本通道后,Bark 使用它来解码输出音频数组。

值得注意的是,前三个模块中的每一个都可以支持条件说话人嵌入,以根据特定的预定义语音调节输出声音。

Bark 是一个高度可控的文本转语音模型,这意味着您可以使用各种设置,我们将在后面看到。

首先,加载模型及其处理器。

处理器在这里的作用是双重的

  1. 它用于对输入文本进行分词,即将其切成模型可以理解的小块。
  2. 它存储说话人嵌入,即可以影响生成的语音预设。
from transformers import BarkModel, BarkProcessor

model = BarkModel.from_pretrained("suno/bark-small")
processor = BarkProcessor.from_pretrained("suno/bark-small")

Bark 非常通用,可以生成由说话人嵌入库调节的音频,该库可以通过处理器加载。

# add a speaker embedding
inputs = processor("This is a test!", voice_preset="v2/en_speaker_3")

speech_output = model.generate(**inputs).cpu().numpy()

它还可以生成即用型多语言语音,例如法语和中文。您可以在此处找到支持的语言列表。与下面讨论的 MMS 不同,无需指定所使用的语言,只需将输入文本适应相应的语言即可。

# try it in French, let's also add a French speaker embedding
inputs = processor("C'est un test!", voice_preset="v2/fr_speaker_1")

speech_output = model.generate(**inputs).cpu().numpy()

该模型还可以生成**非语言交流**,例如笑声、叹息和哭泣。您只需使用相应的提示修改输入文本,例如 [clears throat](清嗓子)、[laughter](笑声)或 ...

inputs = processor(
    "[clears throat] This is a test ... and I just took a long pause.",
    voice_preset="v2/fr_speaker_1",
)

speech_output = model.generate(**inputs).cpu().numpy()

Bark 甚至可以生成音乐。您可以通过在单词周围添加 ♪ 音乐音符 ♪ 来提供帮助。

inputs = processor(
    "♪ In the mighty jungle, I'm trying to generate barks.",
)

speech_output = model.generate(**inputs).cpu().numpy()

除了所有这些功能之外,Bark 还支持批处理,这意味着您可以同时处理多个文本条目,但会以更密集的计算为代价。在某些硬件(例如 GPU)上,批处理可以加快整体生成速度,这意味着一次性生成样本可能比逐个生成样本更快。

让我们尝试生成几个示例

input_list = [
    "[clears throat] Hello uh ..., my dog is cute [laughter]",
    "Let's try generating speech, with Bark, a text-to-speech model",
    "♪ In the jungle, the mighty jungle, the lion barks tonight ♪",
]

# also add a speaker embedding
inputs = processor(input_list, voice_preset="v2/en_speaker_3")

speech_output = model.generate(**inputs).cpu().numpy()

让我们逐个听一下输出。

第一个

from IPython.display import Audio

sampling_rate = model.generation_config.sample_rate
Audio(speech_output[0], rate=sampling_rate)

第二个

Audio(speech_output[1], rate=sampling_rate)

第三个

Audio(speech_output[2], rate=sampling_rate)

Bark,像其他 🤗 Transformers 模型一样,可以通过几行代码进行优化,以提高速度和减少内存影响。要了解如何操作,请点击此 Colab 演示笔记本

大规模多语言语音 (MMS)

如果您正在寻找除英语之外的预训练模型怎么办?大规模多语言语音 (MMS) 是另一个涵盖一系列语音任务的模型,但它支持大量语言。例如,它可以合成 1,100 多种语言的语音。

MMS 用于文本转语音是基于 VITS Kim et al., 2021,这是最先进的 TTS 方法之一。

VITS 是一种语音生成网络,可将文本转换为原始语音波形。它像一个条件变分自编码器一样工作,从输入文本估计音频特征。首先,生成以频谱图表示的声学特征。然后使用从 HiFi-GAN 改造的转置卷积层解码波形。在推理过程中,文本编码被上采样并使用流模块和 HiFi-GAN 解码器转换为波形。像 Bark 一样,不需要声码器,因为波形是直接生成的。

MMS 模型最近才添加到 🤗 Transformers 中,因此您必须从源代码安装库
pip install git+https://github.com/huggingface/transformers.git

让我们试试 MMS,看看我们如何合成除英语之外的语言(例如德语)的语音。首先,我们将加载正确的语言的模型检查点和分词器

from transformers import VitsModel, VitsTokenizer

model = VitsModel.from_pretrained("facebook/mms-tts-deu")
tokenizer = VitsTokenizer.from_pretrained("facebook/mms-tts-deu")

您可能会注意到,要加载 MMS 模型,您需要使用 VitsModelVitsTokenizer。这是因为如前所述,用于文本转语音的 MMS 是基于 VITS 模型的。

让我们选择一个德语示例文本,就像这首儿歌的前两行一样

text_example = (
    "Ich bin Schnappi das kleine Krokodil, komm aus Ägypten das liegt direkt am Nil."
)

要生成波形输出,请使用分词器对文本进行预处理,然后将其传递给模型

import torch

inputs = tokenizer(text_example, return_tensors="pt")
input_ids = inputs["input_ids"]


with torch.no_grad():
    outputs = model(input_ids)

speech = outputs["waveform"]

我们来听一下

from IPython.display import Audio

Audio(speech, rate=16000)

太棒了!如果您想尝试使用其他语言的 MMS,请在 🤗 Hub 上查找其他合适的 vits 检查点。

现在让我们看看如何自己微调 TTS 模型!

< > 在 GitHub 上更新