通过 SpeechT5 实现语音合成、识别等多种功能

发布于 2023 年 2 月 8 日
在 GitHub 上更新

我们很高兴地宣布,SpeechT5 现已在 🤗 Transformers 中可用,这是一个开源库,提供了易于使用的最先进机器学习模型的实现。

SpeechT5 最初在微软亚洲研究院的论文 SpeechT5: Unified-Modal Encoder-Decoder Pre-Training for Spoken Language Processing 中被描述。该论文作者发布的官方检查点可在 Hugging Face Hub 上找到。

如果你想直接上手,这里有一些 Spaces 上的演示

引言

SpeechT5 不是一种,不是两种,而是将三种语音模型集于一个架构中。

它可以做

  • 语音到文本 (speech-to-text) 用于自动语音识别或说话人识别,
  • 文本到语音 (text-to-speech) 用于合成音频,以及
  • 语音到语音 (speech-to-speech) 用于在不同声音之间转换或执行语音增强。

SpeechT5 背后的主要思想是在文本到语音、语音到文本、文本到文本和语音到语音的混合数据上预训练一个单一模型。这样,模型可以同时从文本和语音中学习。这种预训练方法的结果是一个模型,它拥有一个由文本和语音共享的 统一空间 的隐藏表示。

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

为了让同一个 Transformer 能够同时处理文本和语音数据,添加了所谓的 前置网络 (pre-nets)后置网络 (post-nets)。前置网络的任务是将输入的文本或语音转换为 Transformer 使用的隐藏表示。后置网络接收 Transformer 的输出,并将其再次转换为文本或语音。

下图展示了 SpeechT5 的架构(引自原始论文)。

SpeechT5 architecture diagram

在预训练期间,所有的前置网络和后置网络都同时使用。预训练之后,整个编码器-解码器主干在一个单一任务上进行微调。这样一个微调后的模型只使用特定于该任务的前置网络和后置网络。例如,要使用 SpeechT5 进行文本到语音转换,你需要为文本输入换上文本编码器前置网络,为语音输出换上语音解码器前置和后置网络。

注意:尽管微调后的模型最初使用来自共享预训练模型的同一组权重,但最终版本最终都大不相同。例如,你不能拿一个微调过的 ASR 模型,换掉前置网络和后置网络来得到一个能工作的 TTS 模型。SpeechT5 很灵活,但没 *那么* 灵活。

文本到语音

SpeechT5 是我们添加到 🤗 Transformers 中的 第一个文本到语音模型,我们计划在不久的将来添加更多的 TTS 模型。

对于 TTS 任务,模型使用以下前置网络和后置网络

  • 文本编码器前置网络。 一个文本嵌入层,将文本 token 映射到编码器期望的隐藏表示。类似于 NLP 模型如 BERT 中发生的情况。

  • 语音解码器前置网络。 它将一个对数梅尔频谱图作为输入,并使用一系列线性层将频谱图压缩成隐藏表示。这个设计取自 Tacotron 2 TTS 模型。

  • 语音解码器后置网络。 它预测一个残差来添加到输出频谱图上,并用于改善结果,也来自 Tacotron 2。

微调模型的架构如下所示。

SpeechT5 architecture for text-to-speech

这里有一个完整的示例,展示如何使用 SpeechT5 文本到语音模型来合成语音。你也可以在这个交互式的 Colab notebook中跟着操作。

SpeechT5 尚未在最新版本的 Transformers 中提供,因此你需要从 GitHub 安装它。同时安装额外的依赖 sentencepiece,然后重启你的运行时。

pip install git+https://github.com/huggingface/transformers.git
pip install sentencepiece

首先,我们从 Hub 加载微调模型,以及用于 tokenization 和特征提取的处理器对象。我们将使用的类是 SpeechT5ForTextToSpeech

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 模型不限于为单个说话人创建语音。相反,它使用所谓的 说话人嵌入 (speaker embeddings),这些嵌入捕获了特定说话人的声音特征。我们将从 Hub 上的一个数据集中加载这样一个说话人嵌入。

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) 的张量。这个特定的说话人嵌入描述了一个女性的声音。这些嵌入是使用这个脚本CMU ARCTIC 数据集中获得的,但任何 X-Vector 嵌入都应该有效。

现在,我们可以告诉模型根据输入的 token 和说话人嵌入来生成语音。

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

这将输出一个形状为 (140, 80) 的张量,包含一个对数梅尔频谱图。第一个维度是序列长度,它可能在每次运行时都不同,因为语音解码器前置网络总是对输入序列应用 dropout。这为生成的语音增加了一些随机变化。

为了将预测的对数梅尔频谱图转换为实际的语音波形,我们需要一个 声码器 (vocoder)。理论上,你可以使用任何适用于 80-bin 梅尔频谱图的声码器,但为了方便,我们在 Transformers 中提供了一个基于 HiFi-GAN 的声码器。这个声码器的权重,以及微调 TTS 模型的权重,都由 SpeechT5 的原作者友情提供。

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

from transformers import SpeechT5HifiGan
vocoder = SpeechT5HifiGan.from_pretrained("microsoft/speecht5_hifigan")

要从频谱图生成音频,请执行以下操作

with torch.no_grad():
    speech = vocoder(spectrogram)

我们还提供了一个快捷方式,这样你就不需要制作频谱图的中间步骤。当你将声码器对象传递给 generate_speech 时,它会直接输出语音波形。

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

最后,将语音波形保存到文件中。SpeechT5 使用的采样率始终是 16 kHz。

import soundfile as sf
sf.write("tts_example.wav", speech.numpy(), samplerate=16000)

输出的声音听起来是这样的(下载音频

TTS 模型就介绍到这里!让声音听起来好的关键是使用正确的说话人嵌入。

你可以在 Spaces 上玩一个交互式演示

💡 有兴趣学习如何在自己的数据集或语言上 微调 SpeechT5 TTS 吗?请查看这个 Colab notebook,其中有详细的流程演练。

用于声音转换的语音到语音

从概念上讲,使用 SpeechT5 进行语音到语音建模与文本到语音是相同的。只需将文本编码器前置网络换成语音编码器前置网络即可。模型的其余部分保持不变。

SpeechT5 architecture for speech-to-speech

语音编码器前置网络wav2vec 2.0 的特征编码模块相同。它由卷积层组成,将输入波形下采样为音频帧表示序列。

作为语音到语音任务的一个例子,SpeechT5 的作者提供了一个用于声音转换的微调检查点。要使用它,首先从 Hub 加载模型。请注意,现在的模型类是 SpeechT5ForSpeechToSpeech

from transformers import SpeechT5Processor, SpeechT5ForSpeechToSpeech

processor = SpeechT5Processor.from_pretrained("microsoft/speecht5_vc")
model = SpeechT5ForSpeechToSpeech.from_pretrained("microsoft/speecht5_vc")

我们将需要一些语音音频作为输入。为了这个例子,我们将从 Hub 上的一个小语音数据集中加载音频。你也可以加载自己的语音波形,只要它们是单声道且使用 16 kHz 的采样率。我们这里使用的数据集样本已经是这种格式了。

from datasets import load_dataset
dataset = load_dataset("hf-internal-testing/librispeech_asr_demo", "clean", split="validation")
dataset = dataset.sort("id")
example = dataset[40]

接下来,对音频进行预处理,使其符合模型期望的格式。

sampling_rate = dataset.features["audio"].sampling_rate
inputs = processor(audio=example["audio"]["array"], sampling_rate=sampling_rate, return_tensors="pt")

与 TTS 模型一样,我们需要说话人嵌入。它们描述了目标声音听起来的样子。

import torch
embeddings_dataset = load_dataset("Matthijs/cmu-arctic-xvectors", split="validation")
speaker_embeddings = torch.tensor(embeddings_dataset[7306]["xvector"]).unsqueeze(0)

我们还需要加载声码器,将生成的频谱图转换为音频波形。让我们使用与 TTS 模型相同的声码器。

from transformers import SpeechT5HifiGan
vocoder = SpeechT5HifiGan.from_pretrained("microsoft/speecht5_hifigan")

现在我们可以通过调用模型的 generate_speech 方法来执行语音转换。

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

import soundfile as sf
sf.write("speech_converted.wav", speech.numpy(), samplerate=16000)

更换成不同的声音就像加载一个新的说话人嵌入一样简单。你甚至可以从你自己的声音中制作一个嵌入!

原始输入 (下载)

转换后的声音 (下载)

请注意,此示例中转换后的音频在句子结束前就中断了。这可能是由于两个句子之间的停顿,导致 SpeechT5(错误地)预测已到达序列的末尾。用另一个例子试试,你会发现转换通常是正确的,但有时会过早停止。

你可以在这里玩一个交互式演示。🔥

用于自动语音识别的语音到文本

ASR 模型使用以下前置网络和后置网络

  • 语音编码器前置网络。 这与语音到语音模型使用的前置网络相同,由 wav2vec 2.0 的 CNN 特征编码器层组成。

  • 文本解码器前置网络。 类似于 TTS 模型使用的编码器前置网络,它使用嵌入层将文本 token 映射到隐藏表示。(在预训练期间,这些嵌入在文本编码器和解码器前置网络之间是共享的。)

  • 文本解码器后置网络。 这是所有网络中最简单的,由一个单一的线性层组成,该层将隐藏表示投影到词汇表上的概率。

微调模型的架构如下所示。

SpeechT5 architecture for speech-to-text

如果你之前尝试过任何其他 🤗 Transformers 语音识别模型,你会发现 SpeechT5 同样易于使用。最快入门的方法是使用 pipeline。

from transformers import pipeline
generator = pipeline(task="automatic-speech-recognition", model="microsoft/speecht5_asr")

作为语音音频,我们将使用与上一节中相同的输入,但任何音频文件都适用,因为 pipeline 会自动将音频转换为正确的格式。

from datasets import load_dataset
dataset = load_dataset("hf-internal-testing/librispeech_asr_demo", "clean", split="validation")
dataset = dataset.sort("id")
example = dataset[40]

现在我们可以要求 pipeline 处理语音并生成文本转录。

transcription = generator(example["audio"]["array"])

打印转录结果

a man said to the universe sir i exist

听起来完全正确!SpeechT5 使用的 tokenizer 非常基础,并且在字符级别上工作。因此,ASR 模型不会输出任何标点符号或大写字母。

当然,也可以直接使用模型类。首先,加载微调模型和处理器对象。现在的类是 SpeechT5ForSpeechToText

from transformers import SpeechT5Processor, SpeechT5ForSpeechToText

processor = SpeechT5Processor.from_pretrained("microsoft/speecht5_asr")
model = SpeechT5ForSpeechToText.from_pretrained("microsoft/speecht5_asr")

预处理语音输入

sampling_rate = dataset.features["audio"].sampling_rate
inputs = processor(audio=example["audio"]["array"], sampling_rate=sampling_rate, return_tensors="pt")

最后,告诉模型从语音输入生成文本 token,然后使用处理器的解码函数将这些 token 转换为实际文本。

predicted_ids = model.generate(**inputs, max_length=100)
transcription = processor.batch_decode(predicted_ids, skip_special_tokens=True)

体验语音到文本任务的交互式演示。

结论

SpeechT5 是一个有趣的模型,因为——与大多数其他模型不同——它允许你用相同的架构执行多个任务。只有前置网络和后置网络会改变。通过在这些组合任务上预训练模型,它在微调后能够更好地完成每个单独的任务。

我们只包含了语音识别 (ASR)、语音合成 (TTS) 和声音转换任务的检查点,但论文还提到该模型已成功用于语音翻译、语音增强和说话人识别。它非常多才多艺!

社区

注册登录 以发表评论