From 67fcfd5a29949e48deea19001b9b0b120c2465a1 Mon Sep 17 00:00:00 2001 From: kaleo <1311766212@qq.com> Date: Tue, 19 Mar 2024 19:44:59 +0800 Subject: [PATCH] translated zh-CN chapter 5 --- chapters/zh-CN/_toctree.yml | 43 +- chapters/zh-CN/chapter5/asr_models.mdx | 360 ++++++++++++ chapters/zh-CN/chapter5/choosing_dataset.mdx | 100 ++++ chapters/zh-CN/chapter5/demo.mdx | 81 +++ chapters/zh-CN/chapter5/evaluation.mdx | 336 ++++++++++++ chapters/zh-CN/chapter5/fine-tuning.mdx | 513 ++++++++++++++++++ chapters/zh-CN/chapter5/hands_on.mdx | 25 + chapters/zh-CN/chapter5/introduction.mdx | 32 ++ .../zh-CN/chapter5/supplemental_reading.mdx | 10 + 9 files changed, 1476 insertions(+), 24 deletions(-) create mode 100644 chapters/zh-CN/chapter5/asr_models.mdx create mode 100644 chapters/zh-CN/chapter5/choosing_dataset.mdx create mode 100644 chapters/zh-CN/chapter5/demo.mdx create mode 100644 chapters/zh-CN/chapter5/evaluation.mdx create mode 100644 chapters/zh-CN/chapter5/fine-tuning.mdx create mode 100644 chapters/zh-CN/chapter5/hands_on.mdx create mode 100644 chapters/zh-CN/chapter5/introduction.mdx create mode 100644 chapters/zh-CN/chapter5/supplemental_reading.mdx diff --git a/chapters/zh-CN/_toctree.yml b/chapters/zh-CN/_toctree.yml index e002d61a..fb952a1a 100644 --- a/chapters/zh-CN/_toctree.yml +++ b/chapters/zh-CN/_toctree.yml @@ -65,30 +65,25 @@ # - local: chapter4/hands_on # title: 实战练习 # -#- title: 第5单元:自动语音识别(ASR) -# sections: -# - local: chapter5/introduction -# title: 单元简介 -# - local: chapter5/choosing_dataset -# title: 选择数据集 -# - local: chapter5/asr_models -# title: 自动语音识别的与训练模型 -# - local: chapter5/preprocessing_data -# title: 数据加载和预处理 -# - local: chapter5/evaluation -# title: 自动语音识别的评价指标 -# - local: chapter5/fine-tuning -# title: 自动语音识别模型的微调 -# - local: chapter5/speaker_diarization -# title: 说话人识别 -# - local: chapter5/quiz -# title: 习题 -# quiz: 5 -# - local: chapter5/hands_on -# title: 实战练习 -# - local: chapter5/supplemental_reading -# title: 补充阅读 -# +- title: 第5单元:自动语音识别 (ASR) + sections: + - local: chapter5/introduction + title: 单元简介 + - local: chapter5/asr_models + title: 语音识别的预训练模型 + - local: chapter5/choosing_dataset + title: 选择数据集 + - local: chapter5/evaluation + title: 语音识别的评价指标 + - local: chapter5/fine-tuning + title: 微调语音识别模型 + - local: chapter5/demo + title: 创建 demo + - local: chapter5/hands_on + title: 实战练习 + - local: chapter5/supplemental_reading + title: 补充阅读 + #- title: 第6单元:由文字合成语音(TTS) # sections: # - local: chapter6/introduction diff --git a/chapters/zh-CN/chapter5/asr_models.mdx b/chapters/zh-CN/chapter5/asr_models.mdx new file mode 100644 index 00000000..129eb4c9 --- /dev/null +++ b/chapters/zh-CN/chapter5/asr_models.mdx @@ -0,0 +1,360 @@ +# 语音识别的预训练模型 + +在本节中,我们将介绍如何使用 `pipeline()` 来使用预训练的语音识别模型。 +在[第 2 单元](../chapter2/asr_pipeline)中,我们介绍了 `pipeline()` 这种执行语音识别任务的简便方法, +所有的预处理和后处理都在幕后进行,而且它还可以灵活地快速实验 Hugging Face Hub 上的任何预训练检查点。 +在本单元中,我们将更深入地探索语音识别模型的不同属性,以及利用它们来处理不同的任务的方法。 + +如第 3 单元详细介绍的那样,语音识别模型大致分为两类: + +1. 连接时序分类(Connectionist Temporal Classification, CTC):仅编码器(encoder-only)的模型,顶部带有线性分类(CTC)头 +2. 序列到序列(Sequence-to-sequence, Seq2Seq):编码器-解码器(encoder-decoder)模型,编码器和解码器之间带有交叉注意力机制 + +在 2022 年之前,CTC 是这两种架构中更受欢迎的一种,以 encoder-only 模型为主,例如 Wav2Vec2、HuBERT 和 XLSR 在语音的预训练/微调范式中取得了突破。 +大公司如 Meta 和 Microsoft 在大量无标签音频数据上对编码器进行了多天甚至数周的预训练。 +用户采用一个预训练的检查点,并在少至 **10 分钟** 的有标注的语音数据上进行微调,就可以在下游语音识别任务中取得强大的性能。 + +然而,CTC 模型也有其缺点。在编码器上附加一个简单的线性层可以得到一个小巧、快速的完整模型,但可能容易出现语音拼写错误。我们将用 Wav2Vec2 模型演示这一点。 + +## 探索 CTC 模型 + +让我们加载 [LibriSpeech ASR](hf-internal-testing/librispeech_asr_dummy) 数据集的一小部分,以展示 Wav2Vec2 的语音转写能力: + +```python +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 个音频样本中的一个,检查音频样本及其转写: + +```python +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 base](facebook/wav2vec2-base-100h) 检查点: + +```python +from transformers import pipeline + +pipe = pipeline("automatic-speech-recognition", model="facebook/wav2vec2-base-100h") +``` + +接下来,我们将从数据集中取一个示例,并将其数据传递给 pipeline。由于 `pipeline` 会 *消耗* 我们传入的字典(意味着它无法被多次读取), +我们将传递数据的副本。这样,我们可以安全地在以下示例中重复使用同一个音频样本: + +```python +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 月](https://openai.com/blog/whisper/) 发布的用于语音识别的预训练模型。 +与之前完全在 **无标注** 音频数据上预训练的 CTC 前辈不同,Whisper 是在大量 **有标注** 的音频-转写数据上预训练的,整整有 680,000 小时。 + +这是比用于训练 Wav2Vec 2.0 的无标签音频数据(60,000 小时)多一个数量级的数据。更重要的是,这些预训练数据中有 117,000 小时是多语言(或“非英语”)数据。 +这导致了预训练出的检查点可以应用于 96 种以上语言,其中包括许多 _低资源_ 的语言,即这些语言缺乏适合训练的大型数据集。 + +当扩展到 680,000 小时有标注的预训练数据时,Whisper 模型显示出强大的泛化能力,适用于许多数据集和领域。 +预训练检查点取得了能与最先进的语音识别系统相媲美的结果,在 LibriSpeech 的 test-clean 子集上实现了接近 3% 的词错误率(WER), +并在 TED-LIUM 上创造了新的最佳记录,WER 为 4.7%(参考 [Whisper 论文](https://cdn.openai.com/papers/whisper.pdf) 中的表 8)。 + +最重要的是 Whisper 能处理长音频样本、对输入噪声有鲁棒性,且能预测带大小写和标点符号的转写。这使其成为实际应用中可选的语音识别系统之一。 + +本节的剩余部分将向您展示如何使用 🤗 Transformers 中的预训练 Whisper 模型进行语音识别。 +在许多情况下,预训练的 Whisper 检查点表现出色,我们建议在解决任何语音识别问题时第一步先考虑用它。 +通过微调,预训练检查点可以针对特定数据集和语言进行调整,以进一步优化效果,我们将在接下来的 [微调](fine-tuning) 小节中演示如何做到这一点。 + +Whisper 检查点有五种配置,每种配置的模型大小不同。最小的四个分别在仅英语和多语言数据集上训练,而最大的检查点仅有多语言。 +一共九个预训练检查点都可以在 [Hugging Face Hub](https://huggingface.co/models?search=openai/whisper) 上找到。 +以下表格总结了这些检查点,并附有 Hub 上模型的链接。“VRAM”表示以最小批次大小(batch size)为 1 时运行模型所需的 GPU 内存。 +“Rel Speed”是与最大型号相比的相对速度。根据这些信息,您可以选择最适合您硬件的检查点。 + +| 大小 | 参数量 | VRAM / GB | 相对速度 | 仅英语 | 多语言 | +|--------|--------|----------|------------|----------------------------------------------------|---------------------------------------------------| +| tiny | 39 M | 1.4 | 32 | [✓](https://huggingface.co/openai/whisper-tiny.en) | [✓](https://huggingface.co/openai/whisper-tiny) | +| base | 74 M | 1.5 | 16 | [✓](https://huggingface.co/openai/whisper-base.en) | [✓](https://huggingface.co/openai/whisper-base) | +| small | 244 M | 2.3 | 6 | [✓](https://huggingface.co/openai/whisper-small.en) | [✓](https://huggingface.co/openai/whisper-small) | +| mdeium | 769 M | 4.2 | 2 | [✓](https://huggingface.co/openai/whisper-medium.en) | [✓](https://huggingface.co/openai/whisper-medium) | +| large | 1550 M | 7.5 | 1 | x | [✓](https://huggingface.co/openai/whisper-large-v2)| + +让我们加载 [Whisper Base](https://huggingface.co/openai/whisper-base) 检查点,它的大小与我们之前使用的 Wav2Vec2 检查点相当。 +考虑到我们接下来会做多语言语音识别,我们将加载基础检查点的多语言变体。我们还将模型加载到 GPU(如果有)或 CPU 上,如有必要的话 `pipeline()` 随后会负责将所有输入/输出从 CPU 移动到 GPU: + +```python +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`,它告诉模型在进行预测时生成的最大词元数: + +```python +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 那里看到的语音错误—— _Christmas_ 和 _roast_ 都拼写正确。我们看到模型在转写 _SIMILES_ 时出了错, +误写为 _similarly_,但这次预测是英语中的有效单词。使用更大的 Whisper 检查点可以帮助进一步减少转写错误,但代价是需要更多的计算和更长的转写时间。 + +该模型应当有能力处理 96 种语言,所以现在让我们暂时离开英语语音识别,走向全球 🌎! +[Multilingual LibriSpeech](https://huggingface.co/datasets/facebook/multilingual_librispeech) (MLS) 数据集是 LibriSpeech 数据集的多语言版本, +包含六种语言的带标注音频数据。我们将从 MLS 数据集的西班牙语子集中加载一个样本,使用 _流式_(streaming)模式,这样我们就不必下载整个数据集: + +```python +dataset = load_dataset( + "facebook/multilingual_librispeech", "spanish", split="validation", streaming=True +) +sample = next(iter(dataset)) +``` + +我们再次检查文本转写并试听音频片段: + +```python +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 会 _消耗_ 我们输入的音频输入字典,即字典不能被重复读取。 +为了解决这个问题,我们将传递一个音频样本的 _副本_,这样我们就可以在后续代码示例中重复使用同一个音频样本: + +```python +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.'} +``` + +太好了——这看起来与我们的参考文本非常相似(可以说更好,因为它有标点和大小写!)。您会注意到,我们以 _生成关键字参数_(generate kwarg)的形式传递了 `"task"`。 +将 `"task"` 设置为 `"transcribe"` 会迫使 Whisper 执行 _语音识别_ 任务,即转写的语言就是音频中使用的语音。 +Whisper 还能够执行与之密切相关的 _语音翻译_ 任务,西班牙语音频可以被翻译成英语文本。要实现这一点,我们将 `"task"` 设置为 `"translate"`: + +```python +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 的 [模型卡片](https://huggingface.co/openai/whisper-base#usage)。 + +## 长篇转写和时间戳 + +到目前为止,我们一直专注于转写不到 30 秒的短音频样本。但我们也提到过 Whisper 的一个优势是可以处理长音频样本,我们将在这里处理这个任务! + +让我们通过拼接 MLS 数据集中连续几条样本来创建一个长音频文件。因为 MLS 数据集是通过将长有声读物录音分割成较短片段来整理的, +所以拼接样本是重构较长有声读物段落的一种方法,拼接出的音频在各个样本间应该是连贯的。 + +我们将目标音频长度设为 5 分钟,并在达到这个值时停止拼接样本: + +```python +import numpy as np + +target_length_in_m = 5 + +# 将分钟数转为秒数(* 60),再转为采用点数(* sampling rate) +sampling_rate = pipe.feature_extractor.sampling_rate +target_length_in_samples = target_length_in_m * 60 * sampling_rate + +# 遍历数据集,拼接样本直到长度达到目标 +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) + +# 结果如何? +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 网络中的内存随序列长度平方增长:输入长度加倍会使内存需求增加四倍,所以传递超长音频文件会导致内存不足(out-of-memory, OOM)错误 + +在 🤗 Transformers 中进行长篇转写的方式是将输入音频 _分块_(chunking)为更小、更易管理的段落,每个段落与前一个段落有少量重叠。 +这样,我们可以在段落边界处准确地将段落拼接在一起,因为我们可以找到段落之间的重叠并相应地合并转写: + +
+ 🤗 Transformers 分块算法。来源:https://huggingface.co/blog/asr-chunking。 +
+ +给样本分块的优势在于,转写后续的块 \\( i + 1 \\) 不需要使用之前的块 \\( i \\) 的结果。拼接是在我们已经转写了所有块之后在块边界处进行的,所以转写的顺序并不重要。 +该算法完全是 **无状态的**,所以我们甚至可以同时转写块 \\( i + 1 \\) 和块 \\( i \\)!这允许我们 _批量_ 处理块,并通过并行运行模型,与顺序转写相比大大提升计算速度。 +要了解更多关于 🤗 Transformers 中分块的知识,请参阅这篇 [博客](https://huggingface.co/blog/asr-chunking)。 + +要使用长篇转写,我们在调用 pipeline 时需要添加一个额外参数。这个参数,`chunk_length_s`,控制分块段落的长度(以秒为单位)。对于 Whisper,30 秒的块是最佳的,因为这符合 Whisper 期望的输入长度。 + +要使用批处理,我们需要将 `batch_size` 参数传递给 pipeline。将所有这些放在一起,我们可以像下面这样转写长音频样本,并进行分块和批处理: + +```python +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 还能够预测音频数据的片段级 _时间戳_。这些时间戳指示音频的短片段的开始和结束时间,对于将转写与输入音频对齐特别有用。 +比方说我们想为视频提供字幕(closed caption)——我们需要这些时间戳来知道转写的哪一部分对应于视频的某个段落,以便在那个时间显示正确的转写。 + +启用时间戳预测很简单,我们只需设置参数 `return_timestamps=True`。时间戳与我们之前使用的分块和批处理方法兼容,所以我们可以直接将时间戳参数附加到我们之前的代码上: + +```python +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 论文](https://arxiv.org/pdf/2212.04356.pdf))。 + +为了提高在低资源语言、口音或方言上的性能,我们可以将预训练的 Whisper 模型在一小部分适当选择的数据上继续训练,这个过程称为 _微调_。 +我们将展示如何仅使用十小时的额外数据,就能将 Whisper 模型在低资源语言上的性能提高 100% 以上。在下一节中,我们将介绍选择微调数据集的过程。 diff --git a/chapters/zh-CN/chapter5/choosing_dataset.mdx b/chapters/zh-CN/chapter5/choosing_dataset.mdx new file mode 100644 index 00000000..73d1b2fb --- /dev/null +++ b/chapters/zh-CN/chapter5/choosing_dataset.mdx @@ -0,0 +1,100 @@ +# 选择数据集 + +就像在其他机器学习问题中一样,我们的模型最多只能表现得与我们用来训练它的数据一样好。 +语音识别数据集在组织方式和覆盖的领域方面有很大的不同,我们需要查看它们提供的特征,选出最符合我们标准的数据集。 + +所以在选择数据集之前,我们首先需要了解特征的定义。 + +## 语音数据集的特征 + +### 1. 小时数 + +简单来说,训练小时数体现了数据集的大小,类似 NLP 数据集中的训练样例数量。然而,更大的数据集不一定更好。 +如果我们想要一个泛化能力强的模型,就需要一个具有许多不同发言者、领域和发言风格的**多样化**数据集。 + +### 2. 领域 + +领域(domain)包括数据的来源,比如有声读物、播客、YouTube 还是金融会议。每个领域的数据分布都不同, +例如有声读物在高质量的录音室条件下录制(没有背景噪音),并且文本取自书面文学。而对于 YouTube,音频可能包含更多背景噪音和更非正式的语音风格。 + +我们需要保证选择的领域与推理时的条件相匹配。例如,如果我们在有声读物上训练我们的模型,就不能期望它在嘈杂的环境中表现良好。 + +### 3. 说话风格 + +说话风格(speaking style)分为两类: + +* 叙述性(Narrated):按照给定的文本朗读 +* 自发性(Spontaneous):没有固定剧本的对话 + +音频和文本数据反映了说话的风格。由于叙述文本是有剧本的,它表达得会比较清晰,没有任何错误: + +``` +“Consider the task of training a model on a speech recognition dataset” +``` + +而自发性言语使用更口语化的说话风格,包括重复、犹豫和错误: + +``` +“Let's uhh let's take a look at how you'd go about training a model on uhm a sp- speech recognition dataset” +``` + +### 4. 转写风格 + +转写风格指的是目标文本是否有标点、区分大小写等。如果我们希望系统生成可用于出版物或会议转写的完全格式化文本,我们需要带有标点和大小写的训练数据。 +如果我们只需要未格式化结构的口头言语,那么标点和大小写都不是必需的。在这种情况下,我们可以选择一个没有标点或大小写的数据集,或者选择一个有标点和大小写的数据集,然后通过预处理从目标文本中去除它们。 + +## Hub 上数据集的汇总 + +以下是 Hugging Face Hub 上最受欢迎的英语语音识别数据集,作为您根据自己的标准选择数据集的参考: + +| 数据集 | 训练小时数 | 领域 | 说话风格 | 大小写 | 标点 | 许可证 | 推荐用途 | +|---------------------------------------------------------------------------------------------|---------|--------------------------|----------------|--------|------|------------------|----------------------| +| [LibriSpeech](https://huggingface.co/datasets/librispeech_asr) | 960 | 有声读物 | 叙述性 | ❌ | ❌ | CC-BY-4.0 | 学术基准测试 | +| [Common Voice 11](https://huggingface.co/datasets/mozilla-foundation/common_voice_11_0) | 3000 | 维基百科 | 叙述性 | ✅ | ✅ | CC0-1.0 | 非母语发言者 | +| [VoxPopuli](https://huggingface.co/datasets/facebook/voxpopuli) | 540 | 欧洲议会 | 演讲式 | ❌ | ✅ | CC0 | 非母语发言者 | +| [TED-LIUM](https://huggingface.co/datasets/LIUM/tedlium) | 450 | TED 演讲 | 演讲式 | ❌ | ❌ | CC-BY-NC-ND 3.0 | 技术主题 | +| [GigaSpeech](https://huggingface.co/datasets/speechcolab/gigaspeech) | 10000 | 有声读物、播客、YouTube | 叙述性、自发性 | ❌ | ✅ | apache-2.0 | 多领域鲁棒性 | +| [SPGISpeech](https://huggingface.co/datasets/kensho/spgispeech) | 5000 | 金融会议 | 演讲式、自发性 | ✅ | ✅ | User Agreement | 完全格式化的转写 | +| [Earnings-22](https://huggingface.co/datasets/revdotcom/earnings22) | 119 | 金融会议 | 演讲式、自发性 | ✅ | ✅ | CC-BY-SA-4.0 | 口音多样性 | +| [AMI](https://huggingface.co/datasets/edinburghcstr/ami) | 100 | 会议 | 自发性 | ✅ | ✅ | CC-BY-4.0 | 嘈杂语音环境 | + +以下是多语言语音识别数据集的表格,请注意,我们省略了训练小时数列(因为这取决于每个数据集有哪些语言,占比多少),改成了每个数据集的语言数量: + +| 数据集 | 语言数量 | 领域 | 说话风格 | 大小写 | 标点 | 许可证 | 推荐用途 | +|------------------------------------------------------------------------------------------------|--------|---------------------------|---------|-------|-------|-----------|------------------| +| [Multilingual LibriSpeech](https://huggingface.co/datasets/facebook/multilingual_librispeech) | 6 | 有声读物 | 叙述性 | ❌ | ❌ | CC-BY-4.0 | 学术基准测试 | +| [Common Voice 13](https://huggingface.co/datasets/mozilla-foundation/common_voice_13_0) | 108 | 维基百科文本和众筹的言语 | 叙述性 | ✅ | ✅ | CC0-1.0 | 多样化的发言者集合 | +| [VoxPopuli](https://huggingface.co/datasets/facebook/voxpopuli) | 15 | 欧洲议会 | 自发性 | ❌ | ✅ | CC0 | 欧洲语言 | +| [FLEURS](https://huggingface.co/datasets/google/fleurs) | 101 | 欧洲议会 | 自发性 | ❌ | ❌ | CC-BY-4.0 | 多语言评估 | + +有关两个表中涵盖的音频数据集的详细分析,请参阅博文 [音频数据集完全指南](https://huggingface.co/blog/audio-datasets#a-tour-of-audio-datasets-on-the-hub)。 +虽然 Hub 上有超过 180 个语音识别数据集,但可能没有一个数据集符合您的需求。在这种情况下,也可以通过 🤗 Datasets 库来使用您自己的音频数据。要创建自定义音频数据集,请参考指南 [创建音频数据集](https://huggingface.co/docs/datasets/audio_dataset)。 +自定义音频数据集时,请考虑在 Hub 上分享最终构建出的数据集,以便社区中的其他人可以从您的成果中受益——音频社区包容且广大,您感谢其他人的贡献,其他人会像一样感谢您的贡献。 + +好了!既然我们已经了解了选择 ASR 数据集的所有标准,那么我们为这个教程选择一个吧。我们知道 Whisper 已经在高资源语言(如英语和西班牙语)上做得很好了, +所以我们将专注于低资源的多语言转写。我们想保留 Whisper 预测标点和大小写的能力,所以从第二张表来看,Common Voice 13 是一个很好的候选数据集! + +## Common Voice 13 + +Common Voice 13 是一个众筹的数据集,发言者用不同语言朗读并录制维基百科的文本。它是由 Mozilla 基金会发布的一系列 Common Voice 数据集之一。 +在撰写本文时,Common Voice 13 是最新版的数据集,拥有迄今为止任何版本中最多的语言和每种语言最多的小时数。 + +我们可以通过查看 Hub 上的数据集页面来获取 Common Voice 13 数据集的全部语言列表:[mozilla-foundation/common_voice_13_0](https://huggingface.co/datasets/mozilla-foundation/common_voice_13_0)。 +您第一次查看此页面时会被要求接受使用条款,之后您将获得完全访问数据集的权限。 + +一旦我们提交了使用数据集的认证信息,我们将看到数据集预览。数据集预览向我们展示了每种语言数据集的前 100 个样本。更重要的是,它已加载了可播放的音频样本。 +对于这个单元,我们将选择 [_Dhivehi_](https://en.wikipedia.org/wiki/Maldivian_language)(或 _迪维希语_),一种在南亚岛国马尔代夫使用的印度-雅利安语。 +虽然我们在本教程中只示范了迪维希语,但这里介绍的步骤适用于 Common Voice 13 数据集中 108 种语言的任何一种,以及 Hugging Face Hub 上 180 多个音频数据集的任何一个,没有语言或方言的限制。 + +我们可以通过使用下拉菜单将子集设置为 `dv` 来选择 Common Voice 13 的 Dhivehi 子集(`dv` 是迪维希语的语言代码): + +
+ 从数据集预览中选择 Dhivehi 集 +
+ +如果我们点击第一个样本上的播放按钮,我们可以听到音频并查看相应的文本。浏览训练和测试集的样本,以更好地了解我们正在处理的音频和文本数据。 +从语调和风格上可以看出,录音来自叙述性言语。您可能会注意到每条数据的发言者和录音质量的有很大不同,这是众筹数据的一个共同特征。 + +数据集预览是在开始大规模使用之前感性认知音频数据集的绝佳方式。您可以选择 Hub 上的任何数据集,浏览样本并播放不同子集的音频,评估它是否适合您的需求。选择了一个数据集后,加载数据并开始使用就非常简单了。 + +目前我个人不会说迪维希语,而且估计绝大多数读者也不会!为了知道我们微调后的模型是否有效,我们需要一种严格的方法来**评估**它对未见过的数据的转写准确性。我们将在下一节中详细介绍这一点! diff --git a/chapters/zh-CN/chapter5/demo.mdx b/chapters/zh-CN/chapter5/demo.mdx new file mode 100644 index 00000000..0dc017fa --- /dev/null +++ b/chapters/zh-CN/chapter5/demo.mdx @@ -0,0 +1,81 @@ +# 用 Gradio 创建 demo + +现在我们已经对迪维希语语音识别微调了 Whisper 模型,让我们继续前进,构建一个 [Gradio](https://gradio.app) demo 来向社区展示它! + +首先,我们需要使用 `pipeline()` 类加载微调后的检查点——这一点我们从 [预训练模型](asr_models) 章节开始就非常熟悉了。 +您可以更改 `model_id` 为您在 Hugging Face Hub 上微调模型的命名空间,或者使用预训练的 [Whisper 模型](https://huggingface.co/models?sort=downloads&search=openai%2Fwhisper-) 来进行零样本语音识别: + +```python +from transformers import pipeline + +model_id = "sanchit-gandhi/whisper-small-dv" # 改为您的模型 id +pipe = pipeline("automatic-speech-recognition", model=model_id) +``` + +其次,我们将定义一个函数,该函数接收输入音频的文件路径并将其输入 pipeline。在这里,pipeline 会自动加载音频文件, +将其重采样到正确的采样率,并使用模型运行推理。然后我们可以简单地返回转写文本作为函数的输出。 +为了确保我们的模型可以处理任意长度的音频输入,我们将启用 *分块*,正如 [预训练模型](asr_models) 章节所描述的: + +```python +def transcribe_speech(filepath): + output = pipe( + filepath, + max_new_tokens=256, + generate_kwargs={ + "task": "transcribe", + "language": "sinhalese", + }, # 修改成您微调过的语言 + chunk_length_s=30, + batch_size=8, + ) + return output["text"] +``` + +我们将使用 Gradio 的 [blocks](https://gradio.app/docs/#blocks) 功能在我们的 demo 中启动两个标签页:一个用于麦克风转写,另一个用于文件上传。 + +```python +import gradio as gr + +demo = gr.Blocks() + +mic_transcribe = gr.Interface( + fn=transcribe_speech, + inputs=gr.Audio(sources="microphone", type="filepath"), + outputs=gr.outputs.Textbox(), +) + +file_transcribe = gr.Interface( + fn=transcribe_speech, + inputs=gr.Audio(sources="upload", type="filepath"), + outputs=gr.outputs.Textbox(), +) +``` + +最后,我们使用刚刚定义的两个 blocks 启动 Gradio demo: + +```python +with demo: + gr.TabbedInterface( + [mic_transcribe, file_transcribe], + ["Transcribe Microphone", "Transcribe Audio File"], + ) + +demo.launch(debug=True) +``` + +这将启动一个类似于在 Hugging Face Space 上运行的 Gradio demo: + + + +如果您希望在 Hugging Face Hub 上托管您的 demo,您可以使用这个 Space 作为您微调的模型的模板。 + +点击链接将模板 demo 复制到您的账户:https://huggingface.co/spaces/course-demos/whisper-small?duplicate=true + +我们建议将您的 Space 命名为与您的微调模型类似的名称(例如 whisper-small-dv-demo)并将可见性设置为“公开”。 + +将 Space 复制到您的账户后,请点击“Files and versions”->“app.py”->“edit”,然后更改模型标识符为您的微调模型(第6行)。滚动到页面底部并点击 +“Commit changes to main”,demo 将重启,这次使用的是您的微调模型。您可以与您的朋友和家人分享这个 demo,以便他们可以使用您训练的模型! + +查看我们的视频教程,以更好地了解如何复制 Space 👉️ [YouTube 视频](https://www.youtube.com/watch?v=VQYuvl6-9VE) + +我们期待在 Hub 上看到您的 demos! diff --git a/chapters/zh-CN/chapter5/evaluation.mdx b/chapters/zh-CN/chapter5/evaluation.mdx new file mode 100644 index 00000000..d4c68bcf --- /dev/null +++ b/chapters/zh-CN/chapter5/evaluation.mdx @@ -0,0 +1,336 @@ +# 语音识别的评价指标 + +如果您熟悉自然语言处理(NLP)中的[莱文斯坦距离(Levenshtein distance)](https://en.wikipedia.org/wiki/Levenshtein_distance), +那么您会发现评估语音识别系统的指标颇为相似!如果您不熟悉,也不用担心,我们会从头到尾解释这些指标,确保您了解它们的不同之处以及它们的含义。 + +在评估语音识别系统时,我们将系统的预测与目标文本的转写进行比较,并标注出现的任何错误。我们将这些错误归类为以下三种类别: + +1. 替换错误(Substitution, S):在预测中转写了**错误的词**(例如,将 "sat" 转写为 "sit") +2. 插入错误(Insertion, I):在预测中**多加了一个词** +3. 删除错误(Deletion, D):在预测中**漏掉了一个词** + +这些错误类别在所有语音识别指标中都是相同的,不同的是我们计算这些错误的粒度:我们可以在 _词级别_ 或者 _字符级别_ 上进行计算。 + +我们将使用一个示例来解释每个指标的定义。这里,我们有一个 _基准_ 或 _参考_ 文本序列: + +```python +reference = "the cat sat on the mat" +``` + +以及一个来自我们尝试评估的语音识别系统的预测序列: + +```python +prediction = "the cat sit on the" +``` + +我们可以看到,预测相当接近,但有些词不太对。我们将用三种最流行的语音识别指标评估此预测,看看会算出什么样的结果。 + +## 词错误率 + +*词错误率(WER)* 是语音识别的“事实上的”指标。它在*词级别*上计算替换、插入和删除错误,即错误是逐词标注的。以我们的示例为例: + +| 参考 | the | cat | sat | on | the | mat | +|----- |-----|-----|---------|-----|-----|-----| +| 预测 | the | cat | **sit** | on | the | | +| 标签 | ✅ | ✅ | S | ✅ | ✅ | D | + +在这里,我们有: + +* 1 个替换错误("sit" 而不是 "sat") +* 0 个插入错误 +* 1 个删除错误(缺少 "mat") + +这样总共有 2 个错误。要得到错误率,我们将错误数除以参考文本中的总词数(N),在这个例子中为 6: + +$$ +\begin{aligned} +WER &= \frac{S + I + D}{N} \\ +&= \frac{1 + 0 + 1}{6} \\ +&= 0.333 +\end{aligned} +$$ + +好的!所以我们得到的 WER 是 0.333,或 33.3%。注意,“sit”这个词只有一个字符是错误的, +但整个词都被标记为不正确。这是 WER 的一个特点:无论错误多么轻微,拼写错误都会受到重罚。 + +WER *越低越好*:较低的 WER 意味着我们的预测中错误较少,因此完美的语音识别系统的 WER 应为零(无错误)。 + +我们来看看如何使用 🤗 Evaluate 来计算 WER。我们需要两个库来计算我们的 WER 指标:🤗 Evaluate 用于 API 接口,JIWER 用于执行计算: + +``` +pip install --upgrade evaluate jiwer +``` + +太好了!我们现在可以加载 WER 指标,并为我们的示例计算出结果: + +```python +from evaluate import load + +wer_metric = load("wer") + +wer = wer_metric.compute(references=[reference], predictions=[prediction]) + +print(wer) +``` + +**打印输出:** + +``` +0.3333333333333333 +``` + +0.33,或 33.3%,与预期一致!我们现在知道了这个 WER 计算的幕后情况。 + +现在,这里有一个坑……您认为 WER 的上限是多少?您可能会认为是 1 或 100% 对吧?并不是!由于 WER 是错误与单词数(N)的比率,因此 WER 没有上限! +让我们以一个预测了 10 个词而目标只有 2 个词的示例为例。如果我们的所有预测都是错误的(10 个错误),我们将得到 WER 为 10 / 2 = 5,或 500%! +这是在训练 ASR 系统并看到超过 100% 的 WER 时需要牢记的一点。如果您看到这种情况,可能意味着什么地方出了问题……😅 + +## 词准确率 + +我们可以将 WER 反过来,得到一个*越高越好*的指标。与其衡量词错误率,不如衡量我们系统的*词准确率(WAcc)*: + +$$ +\begin{equation} +WAcc = 1 - WER \nonumber +\end{equation} +$$ + +WAcc 也是在词级别上测量的,它只是将 WER 重新表述为准确率指标,而不是错误率指标。 +WAcc 在语音文献中很少被引用——我们倾向于以词错误为中心思考系统的预测,因此更喜欢与这些错误类型标注更相关的错误率指标。 + +## 字符错误率 + +我们将整个词 “sit” 标记为错误似乎有点不公平,事实上只有一个字母是不正确的。这是因为我们是在词的级别上评估我们的系统,只能逐词标注错误。 +*字符错误率(CER)* 则在*字符级别*上评估系统。这意味着我们将单词分解为它们的各个字符,并在字符级别上标注错误: + +| 参考 | t | h | e | | c | a | t | | s | a | t | | o | n | | t | h | e | | m | a | t | +|------|-----|-----|-----|-----|-----|-----|-----|-----|-----|-------|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----| +| 预测 | t | h | e | | c | a | t | | s | **i** | t | | o | n | | t | h | e | | | | | +| 标签 | ✅ | ✅ | ✅ | | ✅ | ✅ | ✅ | | ✅ | S | ✅ | | ✅ | ✅ | | ✅ | ✅ | ✅ | | D | D | D | + +我们现在可以看到,对于词 “sit”,“s” 和 “t” 被标记为正确。只有 “i” 被标记为替换错误(S)。因此,我们奖励了我们的系统部分正确的预测 🤝 + +在我们的例子中,我们有 1 个字符替换,0 个插入和 3 个删除。总共有 14 个字符。所以,我们的 CER 是: + +$$ +\begin{aligned} +CER &= \frac{S + I + D}{N} \\ +&= \frac{1 + 0 + 3}{14} \\ +&= 0.286 +\end{aligned} +$$ + +好的!我们得到了一个 CER 为 0.286,或 28.6%。注意这比我们的 WER 低——现在我们对拼写错误的惩罚要少得多了。 + +## 我应该使用哪个指标? + +一般来说,WER 比 CER 更多地用于评估语音系统。这是因为 WER 要求系统对预测的上下文有更深入的理解。在我们的例子中,“sit” 使用了错误的时态。 +一个理解句子中动词和时态关系的系统会预测出正确的动词时态 “sat”。我们希望训练我们的语音系统达到这种理解水平。 +所以,尽管 WER 比 CER 更不宽容,但它也更有助于我们开发出我们想要的智能系统。因此,我们通常使用 WER,并鼓励您也这样做! +然而,在某些情况下,使用 WER 是不可能的。某些语言,如汉语和日语,没有“词”的概念,因此 WER 没有意义。在这里,我们转而使用 CER。 + +在我们的例子中,我们只在计算 WER 时使用了一句话。但真正在评估一个系统时,我们通常会使用包含几千句话的整个测试集。 +在评估多个句子时,我们会将所有句子的 S、I、D 和 N 求和,然后根据上面定义的公式计算 WER。这样可以更好地估计未见数据的 WER。 + +## 规范化 + +如果我们在带有标点和大小写的数据上训练 ASR 模型,它将学会在其转写中预测大小写和标点。当我们想将我们的模型用于实际的语音识别应用, +如记录会议或口述时,这很有用,因为预测的转写将完全格式化,带有大小写和标点,这种风格被称为*正字法*(orthographic)。 + +然而,我们可以选择*规范化*(normalise)数据集以消除任何大小写和标点。规范化数据集使语音识别任务变得更容易:模型不再需要区分大写和小写字符, +也不需要单独从音频数据中预测标点(例如,分号发出什么声音?)。因此,词错误率自然更低(结果更好)。Whisper 论文演示了规范化转写对 WER 结果的巨大影响(参见 [Whisper 论文](https://cdn.openai.com/papers/whisper.pdf) 的第 4.4 节)。 +虽然我们获得了更低的 WER,但模型并不一定更适合生产。缺乏大小写和标点使模型预测的文本难以阅读。以 [前一节](asr_models) 中的示例为例, +我们在 LibriSpeech 数据集的同一音频样本上运行了 Wav2Vec2 和 Whisper。Wav2Vec2 模型既不预测标点也不预测大小写,而 Whisper 则预测了这两者。 +对比两种转写,我们看到 Whisper 的转写更易于阅读: + +``` +Wav2Vec2: 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 +Whisper: 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 的预测用于下游应用,我们需要使用额外的后处理来恢复标点和大小写。 + +在规范化和不规范化之间有一个折中方案:我们可以在正字法转写上训练我们的系统,然后在计算 WER 之前对预测和目标进行规范化。 +这样,我们既能够训练系统预测带有完整格式的文本,又通过规范化转写提高了 WER。 + +Whisper 模型发布时带有一个能有效处理大小写、标点和数字格式化等规范化任务的规范器(normaliser)。让我们将规范器应用于 Whisper 转写,以演示如何对它们进行规范化: + +```python +from transformers.models.whisper.english_normalizer import BasicTextNormalizer + +normalizer = BasicTextNormalizer() + +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." +normalized_prediction = normalizer(prediction) + +normalized_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 ' +``` + +太好了!我们可以看到文本全部小写,并且没有标点。现在让我们定义参考转写,然后计算参考和预测之间的规范化 WER: + +```python +reference = "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" +normalized_referece = normalizer(reference) + +wer = wer_metric.compute( + references=[normalized_referece], predictions=[normalized_prediction] +) +wer +``` + +**输出:** + +``` +0.0625 +``` + +6.25%——这大概就是我们能预期 Whisper 基础模型在 LibriSpeech 验证集上达到的效果。正如我们在这里看到的,我们预测了一个符合正字法的转写,但通过在计算 WER 之前规范化参考和预测提升了 WER。 + +您如何规范化转写最终取决于您的需求。我们建议在正字法文本上进行训练,并在规范化文本上进行评估,以获得两全其美的效果。 + +## 汇总 + +好的!到目前为止,我们在本单元已经介绍了三个主题:预训练模型、数据集选择和评估。让我们来做点有趣的,将它们结合在一起做一个端到端的样例 🚀 +我们将评估预训练的 Whisper 模型在 Common Voice 13 Dhivehi 测试集上的表现,为下一节关于微调的内容做铺垫。我们会将算出的 WER 作为微调的 _基准_,并尝试超越这个目标 🥊 + +首先,我们将使用 `pipeline()` 类加载预训练的 Whisper 模型。我们现在对这个过程应该非常熟悉了!我们唯一的新步骤是在 GPU 上使用半精度(float16)加载模型——这样会加速推理且几乎不影响 WER。 + +```python +from transformers import pipeline +import torch + +if torch.cuda.is_available(): + device = "cuda:0" + torch_dtype = torch.float16 +else: + device = "cpu" + torch_dtype = torch.float32 + +pipe = pipeline( + "automatic-speech-recognition", + model="openai/whisper-small", + torch_dtype=torch_dtype, + device=device, +) +``` + +接下来,我们将加载 Common Voice 13 的迪维希语测试子集。您可能还记得,从上一节中我们了解到 Common Voice 13 是*受限的*, +这意味着我们必须同意数据集的使用条款才能访问数据集。我们现在可以将我们的 Hugging Face 账户链接到我们的笔记本,以便我们可以从当前使用的机器访问数据集。 + +将笔记本链接到 Hub 非常简单——只需要依照提示输入您的 Hub 身份验证令牌即可。在 [这里](https://huggingface.co/settings/tokens) 找到您的 Hub 身份验证令牌: + +```python +from huggingface_hub import notebook_login + +notebook_login() +``` + +太好了!我们将笔记本链接到我们的 Hugging Face 账户之后,就可以下载 Common Voice 数据集了。 +下载和预处理需要几分钟,以下代码会从 Hugging Face Hub 获取数据并在您的笔记本上自动准备: + +```python +from datasets import load_dataset + +common_voice_test = load_dataset( + "mozilla-foundation/common_voice_13_0", "dv", split="test" +) +``` + + + 如果在加载数据集时遇到身份验证问题,请确保您已经通过以下链接在 Hugging Face Hub 上接受了数据集的使用条款:https://huggingface.co/datasets/mozilla-foundation/common_voice_13_0 + + +用与推理单个样本相同的方式评估整个数据集——我们所要做的就是**循环**处理输入的所有音频,而不是仅推理单个样本。为此,我们首先将我们的数据集转换为 `KeyDataset`。 +我们要做的只是挑选出我们要传递给模型的特定的数据列(在我们的案例中,那是 `"audio"` 列),忽略其余的(如目标转写,在推理时用不上)。 +然后我们遍历这个转换后的数据集,将模型输出的预测结果保存到列表中。以下代码单元将在 GPU 上以半精度运行时大约需要五分钟,内存峰值为 12GB: + +```python +from tqdm import tqdm +from transformers.pipelines.pt_utils import KeyDataset + +all_predictions = [] + +# 运行流式推理 +for prediction in tqdm( + pipe( + KeyDataset(common_voice_test, "audio"), + max_new_tokens=128, + generate_kwargs={"task": "transcribe"}, + batch_size=32, + ), + total=len(common_voice_test), +): + all_predictions.append(prediction["text"]) +``` + + + 如果在运行上述单元时遇到 CUDA 内存不足(out-of-memory, OOM)问题,请将 `batch_size` 逐渐减小 2 的倍数,直到找到适合您设备的批次大小。 + + +最后,我们可以计算 WER。让我们首先计算正字法 WER,即没有任何后处理时的 WER: + +```python +from evaluate import load + +wer_metric = load("wer") + +wer_ortho = 100 * wer_metric.compute( + references=common_voice_test["sentence"], predictions=all_predictions +) +wer_ortho +``` + +**输出:** + +``` +167.29577268612022 +``` + +好吧……167% 意味着我们的模型输出的是垃圾 😜 别担心,我们的目标是通过在迪维希语训练集上微调模型来改善这一点! + +接下来,我们将评估规范化 WER,即规范化后处理过再得到的 WER。我们必须过滤掉规范化后为空的样本,否则我们参考中的总词数(N)将为零,这将导致我们的计算中出现除以零的错误: + +```python +from transformers.models.whisper.english_normalizer import BasicTextNormalizer + +normalizer = BasicTextNormalizer() + +# 计算规范化 WER +all_predictions_norm = [normalizer(pred) for pred in all_predictions] +all_references_norm = [normalizer(label) for label in common_voice_test["sentence"]] + +# 过滤掉参考文本被规范化后为零值的样本 +all_predictions_norm = [ + all_predictions_norm[i] + for i in range(len(all_predictions_norm)) + if len(all_references_norm[i]) > 0 +] +all_references_norm = [ + all_references_norm[i] + for i in range(len(all_references_norm)) + if len(all_references_norm[i]) > 0 +] + +wer = 100 * wer_metric.compute( + references=all_references_norm, predictions=all_predictions_norm +) + +wer +``` + +**输出:** + +``` +125.69809089960707 +``` + +我们再次看到通过规范化我们的参考和预测,我们显著降低了 WER:基线模型在正字法的测试中的 WER 为 168%,而规范化 WER 为 126%。 + +好了!这些是我们在通过微调提高 Whisper 模型语音识别迪维希语的能力时需要超越的目标。继续阅读,开始动手尝试微调示例吧 🚀 diff --git a/chapters/zh-CN/chapter5/fine-tuning.mdx b/chapters/zh-CN/chapter5/fine-tuning.mdx new file mode 100644 index 00000000..b1a9f46c --- /dev/null +++ b/chapters/zh-CN/chapter5/fine-tuning.mdx @@ -0,0 +1,513 @@ +# 微调语音识别模型 + +在本节中,我们将手把手指导如何在 Common Voice 13 数据集上微调 Whisper 以进行语音识别。 +我们将使用模型的“small”版本和一个相对轻量级的数据集,使您能够在任何 16GB 以上的 GPU 上用少量磁盘空间相对快速地微调, +例如 Google Colab 免费提供的 16GB T4 GPU。 + +如果您的 GPU 太小,或在训练过程中遇到内存问题,可以遵循我们之后提供的建议来减少内存使用量。 +相反,如果您可以访问更大的 GPU,您可以修改训练参数以最大化您的吞吐量。因此,无论您的 GPU 规格如何,都可以参考本指南! + +同样,本指南简要介绍了如何针对迪维希语微调 Whisper 模型。然而,这里介绍的步骤可以推广到 Common Voice 数据集中的任何语言, +更一般地,可以推广到 Hugging Face Hub 上的任何 ASR 数据集。您可以更改代码以快速切换到您选择的语言,并在您的母语上微调 Whisper 模型 🌍 + +好了,废话少说,让我们开始我们的微调流程吧! + +## 配置环境 + +我们强烈建议您在训练时直接将模型检查点上传至 [Hugging Face Hub](https://huggingface.co/)。Hub 提供: + +- 集成的版本控制:您可以确保训练过程中不会丢失任何模型检查点。 +- Tensorboard 日志:跟踪训练过程中的重要指标。 +- 模型卡片:记录模型的功能及其预期用例。 +- 社区:与社区共享和协作的简便方式!🤗 + +将 notebook 链接到 Hub 非常简单——只需要输入您的 Hub 认证令牌。在 [这里](https://huggingface.co/settings/tokens) 找到您的 Hub 认证令牌并根据提示输入它: + +```python +from huggingface_hub import notebook_login + +notebook_login() +``` + +**输出:** + +```bash +Login successful +Your token has been saved to /root/.huggingface/token +``` + +## 加载数据集 + +[Common Voice 13](https://huggingface.co/datasets/mozilla-foundation/common_voice_13_0) 包含大约十小时的标注过的迪维希语数据, +这对于微调来说是极少量的数据,因此我们将依赖在预训练期间 Whisper 获得的广泛的多语言 ASR 知识来弥补迪维希语的低资源量。 + +使用 🤗 Datasets 下载和准备数据非常简单。我们可以用一行代码下载并准备好 Common Voice 13 的数据。由于迪维希语资源非常少, +我们将结合 `train` 和 `validation` 子集,提供大约七小时的训练数据,并使用三小时的 `test` 数据作为我们保留的测试集: + +```python +from datasets import load_dataset, DatasetDict + +common_voice = DatasetDict() + +common_voice["train"] = load_dataset( + "mozilla-foundation/common_voice_13_0", "dv", split="train+validation" +) +common_voice["test"] = load_dataset( + "mozilla-foundation/common_voice_13_0", "dv", split="test" +) + +print(common_voice) +``` + +**输出:** + +``` +DatasetDict({ + train: Dataset({ + features: ['client_id', 'path', 'audio', 'sentence', 'up_votes', 'down_votes', 'age', 'gender', 'accent', 'locale', 'segment', 'variant'], + num_rows: 4904 + }) + test: Dataset({ + features: ['client_id', 'path', 'audio', 'sentence', 'up_votes', 'down_votes', 'age', 'gender', 'accent', 'locale', 'segment', 'variant'], + num_rows: 2212 + }) +}) +``` + + + 您可以将语言标识符从 `"dv"` 更改为您选择的语言标识符。要查看 Common Voice 13 中所有可选的语言, + 请查看 Hugging Face Hub 上的数据集卡片:https://huggingface.co/datasets/mozilla-foundation/common_voice_13_0 + + +大多数 ASR 数据集只提供输入音频样本(`audio`)和相应的转写文本(`sentence`)。Common Voice 包含额外的元数据,如 `accent` 和 `locale`, +我们做 ASR 时可以忽略这些信息。为了让代码尽可能通用,我们只用输入音频和转写文本来进行微调,丢弃额外的元数据信息: + +```python +common_voice = common_voice.select_columns(["audio", "sentence"]) +``` + +## 特征提取器、分词器和处理器 + +ASR 流程可以分解为三个阶段: + +1. 特征提取器将原始音频输入预处理为 log-mel 频谱图 +2. 模型执行序列到序列的映射 +3. 分词器将预测的词元后处理为文本 + +在 🤗 Transformers 中,Whisper 模型有一个关联的特征提取器和分词器,分别称为 [WhisperFeatureExtractor](https://huggingface.co/docs/transformers/main/model_doc/whisper#transformers.WhisperFeatureExtractor) 和 [WhisperTokenizer](https://huggingface.co/docs/transformers/main/model_doc/whisper#transformers.WhisperTokenizer)。 +为了简化我们的工作,这两个类的对象又可以被封装在一个类中,称为 [WhisperProcessor](https://huggingface.co/docs/transformers/model_doc/whisper#transformers.WhisperProcessor)。 +我们可以调用 WhisperProcessor 来执行音频预处理和文本词元后处理。这样我们在训练期间只需要跟踪两个对象:处理器和模型。 + +当执行多语言微调时,我们需要在实例化处理器时设置 `"language"` 和 `"task"`。`"language"` 应设置为源音频语言, +任务设置为 `"transcribe"` 以进行语音识别或 `"translate"` 以进行语音翻译。这些参数会影响分词器的行为,应正确设置以确保目标标签被正确编码。 + +我们可以通过导入语言列表来查看 Whisper 支持的所有可能语言: + +```python +from transformers.models.whisper.tokenization_whisper import TO_LANGUAGE_CODE + +TO_LANGUAGE_CODE +``` + +如果您浏览此列表,您会注意到许多语言都存在,但迪维希语是少数不存在的之一!这意味着 Whisper 没有在迪维希语上进行预训练。 +然而,这并不意味着我们不能在其上微调 Whisper。这样做,我们将教会 Whisper 一种新的语言,一种预训练检查点不支持的语言。这很酷,对吧! + +当您在一种新语言上微调时,Whisper 可以利用它在其他 96 种语言上的预训练知识。总的来说,所有现代语言在语言学上至少与 Whisper 已经 +知道的 96 种语言中的一种类似,所以我们可以利用起这种跨语言的知识。 + +我们需要做的是找到 Whisper 在预训练期间学习过**最相似**的语言。维基百科的迪维希语词条指出,迪维希语与斯里兰卡的僧伽罗语密切相关。 +如果我们再次检查语言代码,我们可以看到僧伽罗语存在于 Whisper 语言集中,因此我们可以安全地将我们的语言参数设置为 `"sinhalese"`。 + +好!我们将从预训练检查点加载我们的处理器,将语言设置为 `"sinhalese"`,任务设置为 `"transcribe"`,如上所述: + +```python +from transformers import WhisperProcessor + +processor = WhisperProcessor.from_pretrained( + "openai/whisper-small", language="sinhalese", task="transcribe" +) +``` + +值得重申的是,在大多数情况下,您会发现您想要微调的语言在预训练语言集中,这种情况下,您可以直接将语言设置为您的源音频语言! +请注意,对于仅英语的微调,这两个参数应省略,这种情况下语言(`"English"`)和任务(`"transcribe"`)都是唯一默认的选项。 + +## 预处理数据 + +让我们看看数据集特征。特别注意 `"audio"` 列——这详细说明了我们输入音频的采样率: + +```python +common_voice["train"].features +``` + +**输出:** + +``` +{'audio': Audio(sampling_rate=48000, mono=True, decode=True, id=None), + 'sentence': Value(dtype='string', id=None)} +``` + +由于我们的输入音频采样率为 48kHz,我们需要在将其传递给 Whisper 特征提取器之前将其 _下采样_ 到 16kHz,16kHz 是 Whisper 模型期望的采样率。 + +我们将使用数据集的 [`cast_column`](https://huggingface.co/docs/datasets/main/en/package_reference/main_classes#datasets.Dataset.cast_column) 方法 +将音频输入设置为正确的采样率。此操作不会就地更改音频,而是指示数据集在加载音频样本时即时地重采样: + +```python +from datasets import Audio + +sampling_rate = processor.feature_extractor.sampling_rate +common_voice = common_voice.cast_column("audio", Audio(sampling_rate=sampling_rate)) +``` + +现在我们可以编写一个函数来准备我们的数据以供模型使用: + +1. 调用 `sample["audio"]` 在逐个样本上加载和重采样音频数据。如上所述,🤗 Datasets 在加载时即时执行任何必要的重采样操作。 +2. 用特征提取器从我们的一维音频数组计算 log-mel 频谱图输入特征。 +3. 用分词器将转写文本编码为标签 id。 + +```python +def prepare_dataset(example): + audio = example["audio"] + + example = processor( + audio=audio["array"], + sampling_rate=audio["sampling_rate"], + text=example["sentence"], + ) + + # 计算输入音频样本的长度,以秒计 + example["input_length"] = len(audio["array"]) / audio["sampling_rate"] + + return example +``` + +我们可以使用 🤗 Datasets 的 `.map` 方法将数据预处理函数应用于我们所有的训练样本。 +我们将从原始训练数据中移除原有的列(音频和文本),只留下 `prepare_dataset` 函数返回的列: + +```python +common_voice = common_voice.map( + prepare_dataset, remove_columns=common_voice.column_names["train"], num_proc=1 +) +``` +最后,我们定义一个函数来过滤掉音频样本长度超过 30s 的训练数据。这些样本否则会被 Whisper 特征提取器截断,这可能影响训练的稳定性。 +我们定义一个函数,对于小于 30s 的样本返回 `True`,对于更长的样本返回 `False`: + +```python +max_input_length = 30.0 + + +def is_audio_in_length_range(length): + return length < max_input_length +``` + +我们通过 🤗 Datasets 的 `.filter` 方法将我们的过滤函数应用于我们的训练数据集的所有样本: + +```python +common_voice["train"] = common_voice["train"].filter( + is_audio_in_length_range, + input_columns=["input_length"], +) +``` + +让我们检查一下通过这个过滤步骤我们移除了多少训练数据: + +```python +common_voice["train"] +``` + +**输出:** + +``` +Dataset({ + features: ['input_features', 'labels', 'input_length'], + num_rows: 4904 +}) +``` + +好的!在过滤前后我们有相同数量的样本,所以原数据集中没有超过 30s 的样本。如果您切换语言,情况可能不同,因此为了稳健性最好保留这个过滤步骤。 +有了这些,我们的数据已经完全准备好进行训练了!让我们继续看看如何使用这些数据来微调 Whisper。 + +## 训练和评估 + +现在我们已经准备好了我们的数据,我们准备好深入训练流程了。 +[🤗 Trainer](https://huggingface.co/transformers/master/main_classes/trainer.html?highlight=trainer) 将为我们完成大部分繁重的工作。我们所要做的就是: + +- 定义一个数据整理器:数据整理器接受我们预处理的数据并准备好 PyTorch 张量以供模型使用。 +- 评估指标:我们希望使用词错误率(WER)指标来评估模型,需要定义一个处理这种计算的 `compute_metrics` 函数。 +- 加载预训练检查点:我们需要加载一个预训练检查点并为训练正确配置它。 +- 定义训练参数:这将被 🤗 Trainer 用来构建训练计划。 + +一旦我们完成了模型的微调,我们将在测试数据上评估它,以验证我们是否正确地训练了它转写迪维希语语音。 + +### 定义数据整理器 + +序列到序列语音模型的数据整理器很独特,它能独立地处理 `input_features` 和 `labels`:`input_features` 必须由特征提取器处理,而 `labels` 由分词器处理。 + +`input_features` 已经填充到 30s 并转换为固定维度的 log-Mel 频谱图,所以我们所要做的就是使用特征提取器的 `.pad` 方法, +并设置参数 `return_tensors=pt`,将它们转换为批量的 PyTorch 张量。请注意,这里没有应用额外的填充,因为输入是固定维度的, +`input_features` 仅被转换为 PyTorch 张量。 + +另一方面,`labels` 是未填充的。我们首先使用分词器的 `.pad` 方法将序列填充到批次中的最大长度。然后用 `-100` 替换填充词元, +以便在计算损失函数时**不**考虑这些词元。我们还要去掉标签序列开头的转写起始(start of transcript)词元,因为我们稍后在训练期间会添加它。 + +我们可以利用我们之前定义的 `WhisperProcessor` 来执行特征提取器和分词器操作: + +```python +import torch + +from dataclasses import dataclass +from typing import Any, Dict, List, Union + + +@dataclass +class DataCollatorSpeechSeq2SeqWithPadding: + processor: Any + + def __call__( + self, features: List[Dict[str, Union[List[int], torch.Tensor]]] + ) -> Dict[str, torch.Tensor]: + # 分离输入特征和标签,它们长度不同,填充方式也不同 + # 首先以 PyTorch 张量格式返回音频 + input_features = [ + {"input_features": feature["input_features"][0]} for feature in features + ] + batch = self.processor.feature_extractor.pad(input_features, return_tensors="pt") + + # 获取分词后得到的标签序列 + label_features = [{"input_ids": feature["labels"]} for feature in features] + # 把标签序列填充到最大长度 + labels_batch = self.processor.tokenizer.pad(label_features, return_tensors="pt") + + # 用 -100 来代替填充进的标签,从而不影响损失函数的计算 + labels = labels_batch["input_ids"].masked_fill( + labels_batch.attention_mask.ne(1), -100 + ) + + # 如果在之前分词时添加了 bos 词元,那就剪切掉,因为之后还会加上的 + if (labels[:, 0] == self.processor.tokenizer.bos_token_id).all().cpu().item(): + labels = labels[:, 1:] + + batch["labels"] = labels + + return batch +``` + +我们现在可以初始化我们刚刚定义的数据整理器: + +```python +data_collator = DataCollatorSpeechSeq2SeqWithPadding(processor=processor) +``` + +继续! + +### 评估指标 + +接下来,我们定义我们在测试集上将使用的评估指标。我们将使用 [评价指标](evaluation) 章节中介绍的词错误率(WER)指标,这是评估 ASR 系统的“事实上”的指标。 + +我们从 🤗 Evaluate 加载 WER 指标: + +```python +import evaluate + +metric = evaluate.load("wer") +``` + +然后我们只需定义一个函数,它接受我们模型的预测并返回 WER 指标。这个函数称为 `compute_metrics`,首先用 `pad_token_id` 替换 `label_ids` 中的 `-100` +(撤销我们在数据整理器中为了在损失函数中正确忽略填充词元做的步骤)。然后,它将预测的标签 id 解码为字符串。最后,它计算预测结果和参考标签之间的 WER。 +在这里,我们可以选择评估“标准化”的转录和预测,这些转录和预测都移除了标点和大小写。我们建议您遵循这个步骤,通过标准化转录改进 WER。 + +```python +from transformers.models.whisper.english_normalizer import BasicTextNormalizer + +normalizer = BasicTextNormalizer() + + +def compute_metrics(pred): + pred_ids = pred.predictions + label_ids = pred.label_ids + + # 用 pad_token_id 替换 -100 + label_ids[label_ids == -100] = processor.tokenizer.pad_token_id + + # 我们希望在计算指标时不要组合起词元 + pred_str = processor.batch_decode(pred_ids, skip_special_tokens=True) + label_str = processor.batch_decode(label_ids, skip_special_tokens=True) + + # 计算普通的 WER + wer_ortho = 100 * metric.compute(predictions=pred_str, references=label_str) + + # 计算标准化的 WER + pred_str_norm = [normalizer(pred) for pred in pred_str] + label_str_norm = [normalizer(label) for label in label_str] + # 过滤,从而在评估时只计算 reference 非空的样本 + pred_str_norm = [ + pred_str_norm[i] for i in range(len(pred_str_norm)) if len(label_str_norm[i]) > 0 + ] + label_str_norm = [ + label_str_norm[i] + for i in range(len(label_str_norm)) + if len(label_str_norm[i]) > 0 + ] + + wer = 100 * metric.compute(predictions=pred_str_norm, references=label_str_norm) + + return {"wer_ortho": wer_ortho, "wer": wer} +``` + +### 加载预训练检查点 + +现在让我们加载预训练的 Whisper small 检查点。用 🤗 Transformers 这是小菜一碟! + +```python +from transformers import WhisperForConditionalGeneration + +model = WhisperForConditionalGeneration.from_pretrained("openai/whisper-small") +``` + +我们将 `use_cache` 设置为 `False` 以进行训练,因为我们正在使用 [梯度检查点](https://huggingface.co/docs/transformers/v4.18.0/en/performance#gradient-checkpointing), +两者不兼容。我们还将覆盖两个生成参数以控制模型在推理期间的行为:我们将通过设置 `language` 和 `task` 参数在生成期间强制规定语言和任务标签, +并重新启用缓存以加速推理: + +```python +from functools import partial + +# 在训练期间不使用缓存,因为它和梯度检查点不兼容 +model.config.use_cache = False + +# 为生成设置语言和任务,并重新启用缓存 +model.generate = partial( + model.generate, language="sinhalese", task="transcribe", use_cache=True +) +``` + +## 定义训练配置 + +在最后一步,我们定义了所有与训练相关的参数。在这里,我们将训练步骤数设置为 500。这足以看到与预训练 Whisper 模型相比 WER 大幅提升, +同时确保微调可以在大约 45 分钟内在 Google Colab 免费版上运行完。有关训练参数的更多详情,请参阅 Seq2SeqTrainingArguments [文档](https://huggingface.co/docs/transformers/main_classes/trainer#transformers.Seq2SeqTrainingArguments)。 + +```python +from transformers import Seq2SeqTrainingArguments + +training_args = Seq2SeqTrainingArguments( + output_dir="./whisper-small-dv", # 在 HF Hub 上的输出目录的名字 + per_device_train_batch_size=16, + gradient_accumulation_steps=1, # 每次 batch size 下调到一半就把这个参数上调到两倍 + learning_rate=1e-5, + lr_scheduler_type="constant_with_warmup", + warmup_steps=50, + max_steps=500, # 如果您有自己的 GPU 或者 Colab 付费计划,上调到 4000 + gradient_checkpointing=True, + fp16=True, + fp16_full_eval=True, + evaluation_strategy="steps", + per_device_eval_batch_size=16, + predict_with_generate=True, + generation_max_length=225, + save_steps=500, + eval_steps=500, + logging_steps=25, + report_to=["tensorboard"], + load_best_model_at_end=True, + metric_for_best_model="wer", + greater_is_better=False, + push_to_hub=True, +) +``` + + + 如果您不想将模型检查点上传到 Hub,请设置 `push_to_hub=False`。 + + +我们可以将训练参数传递给 🤗 Trainer,连同我们的模型、数据集、数据整理器和 `compute_metrics` 函数一起: + +```python +from transformers import Seq2SeqTrainer + +trainer = Seq2SeqTrainer( + args=training_args, + model=model, + train_dataset=common_voice["train"], + eval_dataset=common_voice["test"], + data_collator=data_collator, + compute_metrics=compute_metrics, + tokenizer=processor, +) +``` + +有了这个,我们就准备好开始训练了! + +### 训练 + +要启动训练,只需执行: + +```python +trainer.train() +``` + +训练大约需要 45 分钟,取决于您的 GPU 或 Google Colab 给分配的 GPU 。由于 GPU 不同,当您开始训练时,可能会遇到 CUDA `"out-of-memory"` 错误。 +在这种情况下,您可以两倍两倍地逐渐减少 `per_device_train_batch_size` 并使用 [`gradient_accumulation_steps`](https://huggingface.co/docs/transformers/main_classes/trainer#transformers.Seq2SeqTrainingArguments.gradient_accumulation_steps) 来补偿。 + +**输出:** + +| Training Loss | Epoch | Step | Validation Loss | Wer Ortho | Wer | +|:-------------:|:-----:|:----:|:---------------:|:---------:|:-------:| +| 0.136 | 1.63 | 500 | 0.1727 | 63.8972 | 14.0661 | + +我们最终的 WER 是 14.1%——对于七小时的训练数据和仅 500 训练步骤来说不错,这相当于与预训练模型相比提高了 112%! +这意味着我们已经将一个之前不了解迪维希语的模型微调为在不到一小时内以足够的准确性识别迪维希语语音 🤯 + +最重要的是,这与其他 ASR 系统相比如何。为此,我们可以查看 autoevaluate [排行榜](https://huggingface.co/spaces/autoevaluate/leaderboards?dataset=mozilla-foundation%2Fcommon_voice_13_0&only_verified=0&task=automatic-speech-recognition&config=dv&split=test&metric=wer), +一个根据语言和数据集分类模型并根据它们的 WER 排名的排行榜。 + +查看排行榜,我们看到我们训练了 500 步的模型令人信服地击败了我们在前一节评估的预训练 [Whisper Small](https://huggingface.co/openai/whisper-small) 检查点。干得好 👏 + +我们看到有一些检查点比我们训练的表现更好。Hugging Face Hub 的美妙之处在于它是一个*协作*平台——如果我们没有时间或资源自己进行更长时间的训练运行, +我们可以加载社区中其他人已经训练并慷慨分享的检查点(记得对他们心怀感谢!)。您将能够以与预训练检查点相同的方式使用 `pipeline` 类加载这些检查点, +正如我们之前所做的!所以没有什么能阻止您挑选排行榜上最佳模型来用于您的任务! + +我们可以在将训练结果推送到 Hub 时自动将我们的检查点提交到排行榜——我们只需设置适当的关键字参数 (kwargs)。您可以根据您的数据集、语言和模型名称相应地更改这些值: + +```python +kwargs = { + "dataset_tags": "mozilla-foundation/common_voice_13_0", + "dataset": "Common Voice 13", # 训练数据集 + "language": "dv", + "model_name": "Whisper Small Dv - Sanchit Gandhi", # 给模型起个“漂亮”的名字 + "finetuned_from": "openai/whisper-small", + "tasks": "automatic-speech-recognition", +} +``` + +现在可以将训练结果上传到 Hub,请执行 `push_to_hub` 命令: + +```python +trainer.push_to_hub(**kwargs) +``` + +这将在 `"您的用户名/您给模型起的名字"` 下保存训练日志和模型权重。作为示例,请查看 `sanchit-gandhi/whisper-small-dv` 上的上传文件。 + +虽然在 Common Voice 13 迪维希语测试数据上微调的模型提供了令人满意的结果,但这绝非最佳。本指南的目的是演示如何使用 🤗 Trainer 微调 ASR 模型以进行多语言语音识别。 + +如果您有自己的 GPU 或订阅了 Google Colab 付费计划,您可以将 `max_steps` 增加到 4000 步,通过更多步骤的训练进一步降低大约 3% 的 WER。 +如果您决定进行 4000 步的训练,我们还建议将学习率调度器更改为*线性*计划(设置 `lr_scheduler_type="linear"`),因为这将在长时间的训练中提升更多性能。 + +通过优化训练超参数,如 _学习率_ 和 _dropout_,并使用更大的预训练检查点(`medium` 或 `large`),可能进一步改善结果。我们将这留作读者的练习。 + +## 分享您的模型 + +您现在可以使用 Hub 上的链接与任何人共享此模型。大家可以直接将标识符 `"您的用户名/您给模型起的名字"` 加载到 `pipeline()` 对象中。 +例如,要加载微调的检查点 ["sanchit-gandhi/whisper-small-dv"](https://huggingface.co/sanchit-gandhi/whisper-small-dv): + +```python +from transformers import pipeline + +pipe = pipeline("automatic-speech-recognition", model="sanchit-gandhi/whisper-small-dv") +``` + +## 结论 + +在本节中,我们逐步介绍了如何使用 🤗 Datasets、Transformers 和 Hugging Face Hub 微调 Whisper 模型以进行语音识别。 +我们首先加载了 Common Voice 13 数据集的迪维希语子集,并通过计算 log-mel 频谱图和分词文本对其进行了预处理。然后我们定义了数据整理器、评估指标和训练参数, +使用 🤗 Trainer 来训练和评估我们的模型。我们将微调的模型上传到 Hugging Face Hub,并展示了如何使用 `pipeline()` 类共享和使用它。 + +如果您一直学到了这里,您现在应该有了一个用于语音识别的微调检查点,干得好!🥳 更重要的是,您现在掌握了所有必要的工具,可以在任何语音识别数据集或领域上微调 Whisper 模型。 +那么,您还在等什么呢!从 [选择数据集](choosing_dataset) 章节中介绍的数据集里挑一个,或选择您自己的数据集,看看您是否可以获得 SOTA!排行榜在等着您…… diff --git a/chapters/zh-CN/chapter5/hands_on.mdx b/chapters/zh-CN/chapter5/hands_on.mdx new file mode 100644 index 00000000..1ee8f7f6 --- /dev/null +++ b/chapters/zh-CN/chapter5/hands_on.mdx @@ -0,0 +1,25 @@ +# 实战练习 + +在本单元中,我们探讨了微调 ASR 模型的挑战,验证了在新语言上微调像 Whisper(即使是小型检查点)模型需要不少时间和资源。 +为了提供实践经验,我们设计了一个练习,让您能够在使用较小数据集的情况下,体验微调 ASR 模型的过程。 +这个练习的主要目的是让您熟悉过程,而不是取得生产级别的结果。我们故意设置了一个低指标,以确保即使在资源有限的情况下,您也应该能够达到它。 + +以下是指导说明: + +* 使用 `"PolyAI/minds14"` 数据集的美式英语("en-US")子集,微调 `"openai/whisper-tiny"` 模型。 +* 使用 **前450 个样本**进行训练,其余的用于评估。确保在使用 `.map` 方法预处理数据集时设置 `num_proc=1`(这将确保您的模型正确提交以进行评估)。 +* 评估模型时,使用本单元描述的 `wer` 和 `wer_ortho` 指标。但是,*不要* 通过乘以 100 将指标转换为百分比(例如,如果 WER 是 42%,我们将期望在此练习中看到 0.42 的值)。 + +您微调了一个模型后,请使用以下 `kwargs` 将其上传到 🤗 Hub: + +``` +kwargs = { + "dataset_tags": "PolyAI/minds14", + "finetuned_from": "openai/whisper-tiny", + "tasks": "automatic-speech-recognition", +} +``` + +如果您的模型的标准化 WER(`wer`)低于 **0.37**,您将通过这个任务。 + +请随意构建您的模型 demo,并在 Discord 上分享!如果您有任何问题,欢迎在 #audio-study-group 频道发布。 diff --git a/chapters/zh-CN/chapter5/introduction.mdx b/chapters/zh-CN/chapter5/introduction.mdx new file mode 100644 index 00000000..cdda6287 --- /dev/null +++ b/chapters/zh-CN/chapter5/introduction.mdx @@ -0,0 +1,32 @@ +# 单元简介 + +在本节中,我们将探讨如何使用 Transformers 将语音转换为文本,这一任务被称为 _语音识别_。 + +
+ 语音到文本的示意图 +
+ +语音识别,也称为自动语音识别(ASR)或语音转文本(STT),是最受欢迎和令人兴奋的语音处理任务之一。 +它广泛应用于包括口述、语音助手、视频字幕和会议记录在内的多种应用中。 + +您可能在不知不觉中多次使用过语音识别系统,比如说您智能手机中的数字助手(Siri、Google Assistant、Alexa)! +当您使用这些助手时,它们首先要做的就是将您的语音转写为书面文本,准备用于各种下游任务(比如为您查询天气预报 🌤️)。 + +试试下面的语音识别 demo。您可以使用麦克风录制自己的声音,或拖放音频样本文件进行转写: + + + +语音识别是一项具有挑战性的任务,它需要对音频和文本都有所了解。输入的音频可能有很多背景噪音,并且可能由具有各种口音的说话人发出,这使得从中识别出语音变得困难。 +书面文本可能包含无声音的字符,如标点符号,这些信息仅从音频中推断很困难。这些都是我们在构建有效的语音识别系统时必须克服的障碍! + +现在我们已经定义了我们的任务,我们可以开始更详细地研究语音识别。通过本单元的学习,您将对各种可用的预训练语音识别模型有一个良好的基本理解,并了解如何通过 🤗 Transformers 库使用它们。 +您还将了解对一个领域或某种特定语言微调 ASR 模型的过程,使您能够为遇到的任何任务构建一个高效的系统。您将能够向您的亲朋好友现场演示您的模型,一个能够将任何语音转换为文本的模型! + +具体而言,我们将介绍: + +* [语音识别的预训练模型](asr_models) +* [选择数据集](choosing_dataset) +* [语音识别的评价指标](evaluation) +* [微调语音识别模型](fine-tuning) +* [创建 demo](demo) +* [实战练习](hands_on) diff --git a/chapters/zh-CN/chapter5/supplemental_reading.mdx b/chapters/zh-CN/chapter5/supplemental_reading.mdx new file mode 100644 index 00000000..b61217e0 --- /dev/null +++ b/chapters/zh-CN/chapter5/supplemental_reading.mdx @@ -0,0 +1,10 @@ +# Supplemental reading and resources + +本单元介绍了语音识别的实践,语音识别是音频领域最受欢迎的任务之一。 +想了解更多吗?在这里,您可以找到更多资源,加深您对这个主题的理解,提升学习体验。 + +* [Whisper Talk](https://www.youtube.com/live/fZMiD8sDzzg?feature=share) by Jong Wook Kim: a presentation on the Whisper model, explaining the motivation, architecture, training and results, delivered by Whisper author Jong Wook Kim +* [End-to-End Speech Benchmark (ESB)](https://arxiv.org/abs/2210.13352): a paper that comprehensively argues for using the orthographic WER as opposed to the normalised WER for evaluating ASR systems and presents an accompanying benchmark +* [Fine-Tuning Whisper for Multilingual ASR](https://huggingface.co/blog/fine-tune-whisper): an in-depth blog post that explains how the Whisper model works in more detail, and the pre- and post-processing steps involved with the feature extractor and tokenizer +* [Fine-tuning MMS Adapter Models for Multi-Lingual ASR](https://huggingface.co/blog/mms_adapters): an end-to-end guide for fine-tuning Meta AI's new [MMS](https://ai.facebook.com/blog/multilingual-model-speech-recognition/) speech recognition models, freezing the base model weights and only fine-tuning a small number of *adapter* layers +* [Boosting Wav2Vec2 with n-grams in 🤗 Transformers](https://huggingface.co/blog/wav2vec2-with-ngram): a blog post for combining CTC models with external language models (LMs) to combat spelling and punctuation errors