音频课程文档

自动语音识别预训练模型

Hugging Face's logo
加入 Hugging Face 社区

并获得增强型文档体验

开始

自动语音识别预训练模型

在本节中,我们将介绍如何使用pipeline() 利用预训练模型进行语音识别。在单元 2中,我们介绍了pipeline() 作为一种运行语音识别任务的简便方法,所有预处理和后处理都隐藏在后台,并且可以灵活地快速尝试 Hugging Face Hub 上的任何预训练检查点。在本单元中,我们将深入了解语音识别模型的不同属性,以及如何使用它们来解决一系列不同的任务。

如单元 3 中所述,语音识别模型主要分为两类

  1. 连接性时间分类 (CTC):仅编码器 模型,顶部带有线性分类 (CTC) 头
  2. 序列到序列 (Seq2Seq):编码器-解码器 模型,在编码器和解码器之间具有交叉注意力机制

在 2022 年之前,CTC 是两种架构中更受欢迎的一种,仅编码器模型(如 Wav2Vec2、HuBERT 和 XLSR)在语音的预训练/微调范式中取得了突破。大型公司(如 Meta 和 Microsoft)在数天或数周内对海量未标记的音频数据进行了编码器预训练。然后,用户可以获取一个预训练的检查点,并在少至10 分钟的标记语音数据上使用 CTC 头对其进行微调,从而在后续的语音识别任务中获得良好的性能。

但是,CTC 模型也存在缺点。将简单的线性层附加到编码器可以得到一个较小、较快的整体模型,但容易出现语音拼写错误。我们将在下面演示 Wav2Vec2 模型的情况。

探测 CTC 模型

让我们加载LibriSpeech ASR 数据集的一个小片段,以演示 Wav2Vec2 的语音转录能力

from datasets import load_dataset

dataset = load_dataset(
    "hf-internal-testing/librispeech_asr_dummy", "clean", split="validation"
)
dataset

输出

Dataset({
    features: ['file', 'audio', 'text', 'speaker_id', 'chapter_id', 'id'],
    num_rows: 73
})

我们可以选择 73 个音频样本中的一个,并检查音频样本以及转录文本

from IPython.display import Audio

sample = dataset[2]

print(sample["text"])
Audio(sample["audio"]["array"], rate=sample["audio"]["sampling_rate"])

输出

HE TELLS US THAT AT THIS FESTIVE SEASON OF THE YEAR WITH CHRISTMAS AND ROAST BEEF LOOMING BEFORE US SIMILES DRAWN FROM EATING AND ITS RESULTS OCCUR MOST READILY TO THE MIND

好的!圣诞节和烤牛肉,听起来很棒!🎄 在选择数据样本后,我们现在将一个微调的检查点加载到pipeline() 中。为此,我们将使用在 100 小时 LibriSpeech 数据上微调的官方Wav2Vec2 基础 检查点

from transformers import pipeline

pipe = pipeline("automatic-speech-recognition", model="facebook/wav2vec2-base-100h")

接下来,我们将从数据集中获取一个示例,并将它的原始数据传递给管道。由于pipeline 消耗 我们传递给它的任何字典(意味着它不能被重复使用),因此我们将传递数据的副本。这样,我们就可以安全地重复使用以下示例中的相同音频样本

pipe(sample["audio"].copy())

输出

{"text": "HE TELLS US THAT AT THIS FESTIVE SEASON OF THE YEAR WITH CHRISTMAUS AND ROSE BEEF LOOMING BEFORE US SIMALYIS DRAWN FROM EATING AND ITS RESULTS OCCUR MOST READILY TO THE MIND"}

我们可以看到 Wav2Vec2 模型在转录此样本方面做得相当不错 - 乍一看它看起来普遍正确。让我们将目标和预测并排放置,并突出显示差异

Target:      HE TELLS US THAT AT THIS FESTIVE SEASON OF THE YEAR WITH CHRISTMAS AND ROAST BEEF LOOMING BEFORE US SIMILES DRAWN FROM EATING AND ITS RESULTS OCCUR MOST READILY TO THE MIND
Prediction:  HE TELLS US THAT AT THIS FESTIVE SEASON OF THE YEAR WITH **CHRISTMAUS** AND **ROSE** BEEF LOOMING BEFORE US **SIMALYIS** DRAWN FROM EATING AND ITS RESULTS OCCUR MOST READILY TO THE MIND

将目标文本与预测的转录文本进行比较,我们可以看到所有单词的声音都是正确的,但有些单词的拼写不准确。例如

  • CHRISTMAUS vs. CHRISTMAS
  • ROSE vs. ROAST
  • SIMALYIS vs. SIMILES

这突出了 CTC 模型的缺点。CTC 模型本质上是一个“仅声学”模型:它由一个编码器组成,编码器从音频输入中形成隐藏状态表示,以及一个线性层,将隐藏状态映射到字符

这意味着系统几乎完全依赖于给定的声学输入(音频的语音声音)进行预测,因此倾向于以语音的方式转录音频(例如,CHRISTMAUS)。它对先前和后续字母的语言建模上下文不太重视,因此容易出现语音拼写错误。更智能的模型会识别出 CHRISTMAUS 不是英语词汇表中的有效词,并在进行预测时将其更正为 CHRISTMAS。在我们的预测中还缺少两个重要功能 - 大小写和标点符号 - 这限制了模型转录在实际应用中的效用。

毕业到 Seq2Seq

Seq2Seq 模型登场!如单元 3 中所述,Seq2Seq 模型由一个编码器和一个解码器组成,它们通过交叉注意力机制连接在一起。编码器起着与以前相同的角色,计算音频输入的隐藏状态表示,而解码器起着语言模型的作用。解码器处理来自编码器的整个隐藏状态表示序列,并生成相应的文本转录。使用音频输入的全局上下文,解码器能够在进行预测时使用语言建模上下文,动态更正拼写错误,从而避免了语音预测的问题。

Seq2Seq 模型有两个缺点

  1. 它们在解码时本质上较慢,因为解码过程是一步一步进行的,而不是一次完成的
  2. 它们对数据的需求更大,需要大量的训练数据才能收敛

特别是,对大量训练数据的需求一直是 Seq2Seq 架构在语音方面发展的一个瓶颈。标记的语音数据很难获得,当时最大的注释数据集只有 10,000 小时。这种情况在 2022 年Whisper发布后发生了改变。Whisper 是一种用于语音识别的预训练模型,由 OpenAI 的作者 Alec Radford 等人在2022 年 9 月发布。与完全在未标记音频数据上进行预训练的 CTC 前辈不同,Whisper 在大量的标记音频转录数据上进行预训练,准确地说,是 680,000 小时。

这比用于训练 Wav2Vec 2.0 的未标记音频数据(60,000 小时)多了一个数量级。更重要的是,这 117,000 小时的预训练数据是多语言(或“非英语”)数据。这会导致可以应用于 96 种以上语言的检查点,其中许多语言被认为是低资源语言,这意味着该语言缺乏适合训练的大量语料库。

当扩展到 680,000 小时的标记预训练数据时,Whisper 模型展示了对许多数据集和领域进行泛化的强大能力。预训练的检查点在 LibriSpeech pipe 的 test-clean 子集上取得了与最先进的管道系统相当的结果,词错误率 (WER) 接近 3%,在 TED-LIUM 上取得了新的最先进水平,WER 为 4.7%(c.f. Whisper 论文 的表 8)。

Whisper 处理长音频样本的能力、对输入噪声的鲁棒性以及预测大小写和标点符号转录的能力尤其重要。这使其成为实际语音识别系统的可行候选者。

本节的其余部分将向您展示如何使用预训练的 Whisper 模型通过 🤗 Transformers 进行语音识别。在许多情况下,预训练的 Whisper 检查点表现出色,并能提供良好的结果,因此我们鼓励您尝试使用预训练的检查点作为解决任何语音识别问题的首要步骤。通过微调,预训练的检查点可以针对特定数据集和语言进行调整,以进一步改善这些结果。我们将在接下来的关于 微调 的小节中演示如何做到这一点。

Whisper 检查点有五种不同模型尺寸的配置。最小的四个是在英语或多语言数据上训练的。最大的检查点仅支持多语言。所有九个预训练的检查点都可以在 Hugging Face Hub 上获得。下表汇总了检查点,其中包含指向 Hub 上模型的链接。“VRAM”表示使用最小批次大小 1 运行模型所需的 GPU 内存。“Rel Speed”是检查点相对于最大模型的相对速度。根据这些信息,您可以选择最适合您硬件的检查点。

大小 参数 VRAM / GB Rel Speed 仅英语 多语言
tiny 39 M 1.4 32
base 74 M 1.5 16
small 244 M 2.3 6
medium 769 M 4.2 2
large 1550 M 7.5 1 x

让我们加载 Whisper Base 检查点,它与我们之前使用的 Wav2Vec2 检查点的大小相当。为了抢先进行多语言语音识别,我们将加载基础检查点的多语言变体。如果可用,我们还将在 GPU 上加载模型,否则将在 CPU 上加载模型。pipeline() 随后会根据需要将所有输入/输出从 CPU 移动到 GPU。

import torch
from transformers import pipeline

device = "cuda:0" if torch.cuda.is_available() else "cpu"
pipe = pipeline(
    "automatic-speech-recognition", model="openai/whisper-base", device=device
)

太棒了!现在让我们像以前一样转录音频。我们所做的唯一更改是传递一个额外的参数 max_new_tokens,它告诉模型在做出预测时要生成的令牌的最大数量。

pipe(sample["audio"], max_new_tokens=256)

输出

{'text': ' He tells us that at this festive season of the year, with Christmas and roast beef looming before us, similarly is drawn from eating and its results occur most readily to the mind.'}

很简单吧!您会注意到第一眼就能看到大小写和标点符号。这立即使得转录比 Wav2Vec2 的非大小写和无标点转录更容易阅读。让我们将转录与目标并排放置。

Target:     HE TELLS US THAT AT THIS FESTIVE SEASON OF THE YEAR WITH CHRISTMAS AND ROAST BEEF LOOMING BEFORE US SIMILES DRAWN FROM EATING AND ITS RESULTS OCCUR MOST READILY TO THE MIND
Prediction: He tells us that at this festive season of the year, with **Christmas** and **roast** beef looming before us, **similarly** is drawn from eating and its results occur most readily to the mind.

Whisper 在纠正我们从 Wav2Vec2 中看到的语音错误方面做得很好 - Christmasroast 的拼写都是正确的。我们看到模型仍然难以处理 SIMILES,将其错误地转录为 similarly,但这一次预测是英语词汇中一个有效的词。使用更大的 Whisper 检查点可以帮助进一步减少转录错误,但代价是需要更多计算量和更长的转录时间。

我们已经承诺了一个可以处理 96 种语言的模型,所以让我们暂时离开英语语音识别,走向全球 🌎!Multilingual LibriSpeech (MLS) 数据集是 LibriSpeech 数据集的多语言等效数据集,包含六种语言的带标签音频数据。我们将从 MLS 数据集的西班牙语部分加载一个样本,使用流式模式,这样我们就不必下载整个数据集。

dataset = load_dataset(
    "facebook/multilingual_librispeech", "spanish", split="validation", streaming=True
)
sample = next(iter(dataset))

同样,我们将检查文本转录,并收听音频片段。

print(sample["text"])
Audio(sample["audio"]["array"], rate=sample["audio"]["sampling_rate"])

输出

entonces te delelitarás en jehová y yo te haré subir sobre las alturas de la tierra y te daré á comer la heredad de jacob tu padre porque la boca de jehová lo ha hablado

这是我们使用 Whisper 转录的目标文本。虽然我们现在知道我们可能做得更好,因为我们的模型也会预测标点符号和大小写,而这些都没有出现在参考文本中。让我们将音频样本转发到管道以获得我们的文本预测。需要注意的是,管道消耗了我们输入的音频输入字典,这意味着字典无法重复使用。为了避免这种情况,我们将传递音频样本的副本,这样我们就可以在接下来的代码示例中重复使用相同的音频样本。

pipe(sample["audio"].copy(), max_new_tokens=256, generate_kwargs={"task": "transcribe"})

输出

{'text': ' Entonces te deleitarás en Jehová y yo te haré subir sobre las alturas de la tierra y te daré a comer la heredad de Jacob tu padre porque la boca de Jehová lo ha hablado.'}

太好了 - 这看起来与我们的参考文本非常相似(可以说更好,因为它有标点符号和大小写!)。您会注意到,我们转发了 "task" 作为生成关键字参数(生成 kwarg)。将 "task" 设置为 "transcribe" 会强制 Whisper 执行语音识别任务,其中音频以与语音发音相同的语言进行转录。Whisper 也能够执行密切相关的语音翻译任务,其中西班牙语音频可以翻译成英语文本。为了实现这一点,我们将 "task" 设置为 "translate"

pipe(sample["audio"], max_new_tokens=256, generate_kwargs={"task": "translate"})

输出

{'text': ' So you will choose in Jehovah and I will raise you on the heights of the earth and I will give you the honor of Jacob to your father because the voice of Jehovah has spoken to you.'}

现在我们知道可以切换语音识别和语音翻译,我们可以根据需要选择我们的任务。要么我们从语言 X 的音频识别到语言 X 的文本(例如,西班牙语音频到西班牙语文本),要么我们从任何语言 X 的音频翻译到英语文本(例如,西班牙语音频到英语文本)。

要了解更多关于如何使用 "task" 参数来控制生成的文本属性的信息,请参阅 模型卡,了解 Whisper 基础模型。

长文本转录和时间戳

到目前为止,我们专注于转录少于 30 秒的短音频样本。我们提到 Whisper 的一个吸引人的地方是它能够处理长的音频样本。我们将在这里处理这个任务!

让我们通过连接 MLS 数据集中连续的样本,创建一个长的音频文件。由于 MLS 数据集是通过将长的有声读物录音分割成较短的片段而精心策划的,所以连接样本是重建更长的有声读物段落的一种方法。因此,生成的音频应该在整个样本中都是连贯的。

我们将目标音频长度设置为 5 分钟,并在达到此值后停止连接样本。

import numpy as np

target_length_in_m = 5

# convert from minutes to seconds (* 60) to num samples (* sampling rate)
sampling_rate = pipe.feature_extractor.sampling_rate
target_length_in_samples = target_length_in_m * 60 * sampling_rate

# iterate over our streaming dataset, concatenating samples until we hit our target
long_audio = []
for sample in dataset:
    long_audio.extend(sample["audio"]["array"])
    if len(long_audio) > target_length_in_samples:
        break

long_audio = np.asarray(long_audio)

# how did we do?
seconds = len(long_audio) / 16000
minutes, seconds = divmod(seconds, 60)
print(f"Length of audio sample is {minutes} minutes {seconds:.2f} seconds")

输出

Length of audio sample is 5.0 minutes 17.22 seconds

好了!5 分钟 17 秒的音频需要转录。将这个长的音频样本直接转发到模型有两个问题。

  1. Whisper 从本质上设计用于处理 30 秒的样本:任何短于 30 秒的样本都会用静音填充到 30 秒,任何长于 30 秒的样本都会通过剪切额外的音频截断到 30 秒,因此如果我们直接传递音频,我们只会获得前 30 秒的转录。
  2. Transformer 网络中的内存随序列长度的平方而扩展:将输入长度加倍会使内存需求增加四倍,因此传递超长的音频文件必然会导致内存不足 (OOM) 错误。

🤗 Transformers 中长文本转录的工作方式是将输入音频分割成更小、更易于管理的片段。每个片段与前一个片段有一小部分重叠。这使我们能够在边界处准确地将片段拼接起来,因为我们可以找到片段之间的重叠并将转录相应地合并。

🤗 Transformers chunking algorithm. Source: https://huggingface.co/blog/asr-chunking.

分割样本的优点是我们不需要结果i i 来转录后续的片段i+1 i + 1 。拼接是在我们转录完所有片段的片段边界处完成的,因此我们以什么顺序转录片段并不重要。该算法完全无状态,因此我们甚至可以做片段i+1 i + 1 与片段i i 同时进行!这使我们能够批处理片段,并以并行方式将其通过模型运行,与依次转录相比,这提供了巨大的计算加速。要了解更多关于 🤗 Transformers 中分割的信息,您可以参考这篇博客文章

要激活长文本转录,我们必须在调用管道时添加一个额外的参数。这个参数 chunk_length_s 控制以秒为单位的分割片段的长度。对于 Whisper,30 秒的片段是最佳的,因为这与 Whisper 预期输入的长度相匹配。

要激活批处理,我们需要将参数 batch_size 传递给管道。将所有这些放在一起,我们可以使用分割和批处理来转录长的音频样本,如下所示。

pipe(
    long_audio,
    max_new_tokens=256,
    generate_kwargs={"task": "transcribe"},
    chunk_length_s=30,
    batch_size=8,
)

输出

{'text': ' Entonces te deleitarás en Jehová, y yo te haré subir sobre las alturas de la tierra, y te daré a comer la
heredad de Jacob tu padre, porque la boca de Jehová lo ha hablado. nosotros curados. Todos nosotros nos descarriamos
como bejas, cada cual se apartó por su camino, mas Jehová cargó en él el pecado de todos nosotros...

我们不会在这里打印整个输出,因为它很长(总共 312 个字)!在 16GB V100 GPU 上,您预计上述代码行大约需要 3.45 秒才能运行,对于 317 秒的音频样本来说已经相当不错了。在 CPU 上,预计运行时间更接近 30 秒。

Whisper 还能够预测音频数据的段级别时间戳。这些时间戳指示短音频片段的开始和结束时间,对于将转录与输入音频对齐特别有用。假设我们要为视频提供字幕 - 我们需要这些时间戳来知道转录的哪一部分对应于视频的某个片段,以便为该时间显示正确的转录。

激活时间戳预测很简单,我们只需要设置参数 return_timestamps=True 即可。时间戳与我们之前使用的分割和批处理方法兼容,因此我们可以简单地将时间戳参数附加到我们之前的调用。

pipe(
    long_audio,
    max_new_tokens=256,
    generate_kwargs={"task": "transcribe"},
    chunk_length_s=30,
    batch_size=8,
    return_timestamps=True,
)["chunks"]

输出

[{'timestamp': (0.0, 26.4),
  'text': ' Entonces te deleitarás en Jehová, y yo te haré subir sobre las alturas de la tierra, y te daré a comer la heredad de Jacob tu padre, porque la boca de Jehová lo ha hablado. nosotros curados. Todos nosotros nos descarriamos como bejas, cada cual se apartó por su camino,'},
 {'timestamp': (26.4, 32.48),
  'text': ' mas Jehová cargó en él el pecado de todos nosotros. No es que partas tu pan con el'},
 {'timestamp': (32.48, 38.4),
  'text': ' hambriento y a los hombres herrantes metas en casa, que cuando vieres al desnudo lo cubras y no'},
 ...

瞧!我们得到了预测的文本以及相应的时间戳。

总结

Whisper 是一个强大的预训练语音识别和翻译模型。与 Wav2Vec2 相比,它具有更高的转录精度,其输出包含标点符号和大小写。它可以用于转录英语以及其他 96 种语言的语音,无论是在短音频片段上,还是通过分割在更长的音频片段上。这些属性使其成为许多语音识别和翻译任务的可行模型,无需进行微调。pipeline() 方法提供了一种简单的方法,可以通过单行 API 调用进行推理,并控制生成的预测。

虽然 Whisper 模型在许多资源丰富的语言上表现出色,但在资源匮乏的语言(即训练数据不足的语言)上,其转录和翻译准确率较低。某些语言的不同口音和方言之间也存在性能差异,包括对不同性别、种族、年龄或其他人口统计特征的说话者的准确率较低(参见 Whisper 论文)。

为了提高在资源匮乏的语言、口音或方言上的性能,我们可以使用预训练的 Whisper 模型,并在经过适当选择的小型语料库上进行训练,这个过程称为微调。我们将证明,只需十个小时的额外数据,我们就能将 Whisper 模型在资源匮乏的语言上的性能提高 100% 以上。在下一节中,我们将介绍选择微调数据集的过程。