音频课程文档

用于自动语音识别的预训练模型

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 这样的大公司,在大量的未标记音频数据上对编码器进行了数天或数周的预训练。然后,用户可以采用预训练检查点,并使用 CTC 头在少至 10 分钟的标记语音数据上对其进行微调,以在下游语音识别任务上获得强大的性能。

然而,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() 中。为此,我们将使用官方的 Wav2Vec2 base 检查点,该检查点在 100 小时的 LibriSpeech 数据上进行了微调

from transformers import pipeline

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

接下来,我们将从数据集中取一个示例,并将其原始数据传递给 pipeline。由于 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

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

  • CHRISTMAUSCHRISTMAS
  • ROSEROAST
  • SIMALYISSIMILES

这突出了 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 小时)多一个数量级。更重要的是,这 680,000 小时的预训练数据中有 117,000 小时是多语言(或“非英语”)数据。这使得检查点可以应用于 96 种以上的语言,其中许多语言被认为是低资源语言,这意味着该语言缺乏适合训练的大型语料库。

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

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

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

Whisper 检查点有五种配置,模型大小各不相同。最小的四个是在仅英语或多语言数据上训练的。最大的检查点仅限多语言。所有九个预训练检查点都可以在 Hugging Face Hub 上找到。检查点在下表中进行了总结,并提供了指向 Hub 上模型的链接。“VRAM”表示运行最小批大小为 1 的模型所需的 GPU 内存。“相对速度”是检查点相对于最大模型的相对速度。根据此信息,您可以选择最适合您硬件的检查点。

大小 参数 VRAM / GB 相对速度 仅英语 多语言
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 检查点相当。为了抢占我们转向多语言语音识别的先机,我们将加载 base 检查点的多语言变体。我们还将在 GPU 可用时在 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,它告诉模型在进行预测时要生成的最大 token 数

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 转录的目标文本。尽管我们现在知道我们可以做得更好,因为我们的模型还将预测标点符号和大小写,但参考文本中都没有这些。让我们将音频样本转发到 pipeline 以获得我们的文本预测。需要注意的一点是,pipeline 消耗 我们输入的音频输入字典,这意味着该字典无法重复使用。为了规避这种情况,我们将传递音频样本的副本,以便我们可以在后续的代码示例中重复使用相同的音频样本

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" 作为生成关键字参数(generate 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 base 模型的 模型卡

长格式转录和时间戳

到目前为止,我们专注于转录小于 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.

分块样本的优势在于我们不需要 chunk 的结果i i 来转录后续 chunki+1 i + 1 。缝合是在我们转录完所有 chunk 的 chunk 边界之后完成的,因此我们以什么顺序转录 chunk 并不重要。该算法完全是无状态的,因此我们甚至可以同时处理 chunki+1 i + 1 与 chunki i !这使我们能够批量处理这些 chunk 并并行地通过模型运行它们,与按顺序转录它们相比,提供了很大的计算加速。要了解有关 🤗 Transformers 中分块的更多信息,您可以参考这篇博客文章

要激活长格式转录,我们必须在调用 pipeline 时添加一个额外的参数。此参数 chunk_length_s 控制分块片段的长度(以秒为单位)。对于 Whisper,30 秒的 chunk 是最佳的,因为这与 Whisper 期望的输入长度相匹配。

要激活批处理,我们需要将参数 batch_size 传递给 pipeline。将所有内容放在一起,我们可以使用分块和批处理按如下方式转录长音频样本

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 模型,并在少量精选数据语料库上对其进行训练,这个过程称为微调。我们将展示,只需增加 10 个小时的额外数据,我们就可以将 Whisper 模型在低资源语言上的性能提高 100% 以上。在下一节中,我们将介绍选择数据集进行微调的过程。

< > 在 GitHub 上更新