音频课程文档

ASR 评估指标

Hugging Face's logo
加入 Hugging Face 社区

并获得增强的文档体验

开始使用

ASR 评估指标

如果您熟悉 NLP 中的 Levenshtein 距离,那么评估语音识别系统的指标您会感到熟悉!如果您不熟悉也不用担心,我们将从头到尾解释,以确保您了解不同的指标并理解它们的含义。

在评估语音识别系统时,我们会将系统的预测与目标文本转录进行比较,并注释任何存在的错误。我们将这些错误分为三类之一

  1. 替换 (S):我们在预测中转录了错误的单词(“sit”而不是“sat”)
  2. 插入 (I):我们在预测中添加了额外的单词
  3. 删除 (D):我们在预测中删除了一个单词

这些错误类别对于所有语音识别指标都是相同的。不同之处在于我们计算这些错误的级别:我们可以在单词级别字符级别上计算它们。

我们将为每个指标定义使用一个运行示例。在这里,我们有一个真实值参考文本序列

reference = "the cat sat on the mat"

以及我们尝试评估的语音识别系统的预测序列

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),在本例中为 6WER=S+I+DN=1+0+16=0.333 \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 指标并计算我们示例的数字

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 系统并看到 WER 超过 100%,请记住这一点。虽然如果您看到这种情况,可能出了问题……😅

词准确率

我们可以将 WER 翻转过来,得到一个越高越好的指标。与其衡量词错误率,不如衡量我们系统的词准确率 (WAcc)WAcc=1WER \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 是CER=S+I+DN=1+0+317=0.235 \begin{aligned} CER &= \frac{S + I + D}{N} \\ &= \frac{1 + 0 + 3}{17} \\ &= 0.235 \end{aligned}

对!我们的 CER 为 0.235,即 23.5%。请注意,这低于我们的 WER - 我们对拼写错误的惩罚要小得多。

我应该使用哪个指标?

一般来说,WER 比 CER 更常用于评估语音系统。这是因为 WER 要求系统对预测的上下文有更好的理解。在我们的示例中,“sit”的时态不正确。了解句子中动词和时态之间关系的系统会预测正确的动词时态“sat”。我们希望鼓励我们的语音系统达到这种理解水平。因此,虽然 WER 不如 CER 宽容,但它更有利于我们想要开发的智能系统类型。因此,我们通常使用 WER,并鼓励您也这样做!但是,在某些情况下不可能使用 WER。某些语言,如普通话和日语,没有“单词”的概念,因此 WER 没有意义。在这里,我们恢复使用 CER。

在我们的示例中,我们在计算 WER 时仅使用了一个句子。在评估真实系统时,我们通常会使用包含数千个句子的整个测试集。在评估多个句子时,我们汇总所有句子中的 S、I、D 和 N,然后根据上面定义的公式计算 WER。这可以更好地估计看不见的数据的 WER。

归一化

如果我们在带有标点符号和大小写的数据上训练 ASR 模型,它将学习在其转录中预测大小写和标点符号。当我们想要将我们的模型用于实际的语音识别应用程序时,例如转录会议或听写,这非常棒,因为预测的转录将完全格式化为大小写和标点符号,这种风格称为正字法

但是,我们也可以选择归一化数据集以删除任何大小写和标点符号。归一化数据集使语音识别任务更容易:模型不再需要区分大写和小写字符,或者必须仅从音频数据中预测标点符号(例如,分号发出什么声音?)。因此,词错误率自然会降低(意味着结果更好)。Whisper 论文证明了归一化转录对 WER 结果的巨大影响(c.f. Whisper 论文的第 4.4 节)。虽然我们获得了较低的 WER,但该模型不一定更适合生产。缺少大小写和标点符号使得模型预测的文本更难阅读。以上一节中的示例为例,我们在 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 模型发布时带有一个归一化器,可以有效地处理大小写、标点符号和数字格式等的归一化。让我们将归一化器应用于 Whisper 转录,以演示我们如何归一化它们

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

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% - 这大约是我们对 LibriSpeech 验证集上的 Whisper 基础模型的期望。正如我们在此处看到的,我们预测了正字法转录,但受益于在计算 WER 之前归一化参考和预测而获得的 WER 提升。

如何归一化转录的选择最终取决于您的需求。我们建议在正字法文本上进行训练,并在归一化文本上进行评估,以获得两全其美的效果。

将所有内容整合在一起

好的!到目前为止,在本单元中我们已经介绍了三个主题:预训练模型、数据集选择和评估。让我们玩得开心,并将它们放在一个端到端示例中 🚀 我们将为下一节关于微调的内容做好准备,方法是在 Common Voice 13 Dhivehi 测试集上评估预训练的 Whisper 模型。我们将把我们获得的 WER 数字作为我们微调运行的基线,或者我们将尝试击败的目标数字 🥊

首先,我们将使用 pipeline() 类加载预训练的 Whisper 模型。这个过程现在将非常熟悉!我们唯一要做的就是以半精度 (float16) 加载模型(如果在 GPU 上运行) - 这将加快推理速度,而几乎不会降低 WER 准确率。

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 的 Dhivehi 测试拆分。您会记得在上一节中,Common Voice 13 是门控的,这意味着我们必须同意数据集的使用条款才能获得对数据集的访问权限。我们现在可以将我们的 Hugging Face 帐户链接到我们的笔记本,以便我们可以从我们当前使用的机器访问数据集。

将笔记本链接到 Hub 非常简单 - 只需在提示时输入您的 Hub 身份验证令牌即可。在此处查找您的 Hub 身份验证令牌 here 并在提示时输入它

from huggingface_hub import notebook_login

notebook_login()

太棒了!将笔记本链接到我们的 Hugging Face 帐户后,我们可以继续下载 Common Voice 数据集。这将需要几分钟时间来下载和预处理,从 Hugging Face Hub 获取数据并在您的笔记本上自动准备数据

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

from tqdm import tqdm
from transformers.pipelines.pt_utils import KeyDataset

all_predictions = []

# run streamed inference
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 内存不足 (OOM) 的情况,请以 2 的因子逐步减小 `batch_size`,直到找到适合您设备的批量大小。

最后,我们可以计算 WER。让我们首先计算正字法 WER,即没有任何后处理的 WER

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% 基本上意味着我们的模型正在输出垃圾 😜 不用担心,我们的目标是通过在 Dhivehi 训练集上微调模型来改进这一点!

接下来,我们将评估归一化 WER,即具有归一化后处理的 WER。我们必须过滤掉归一化后会为空的样本,否则我们的参考 (N) 中的单词总数将为零,这将导致计算中出现除以零错误

from transformers.models.whisper.english_normalizer import BasicTextNormalizer

normalizer = BasicTextNormalizer()

# compute normalised WER
all_predictions_norm = [normalizer(pred) for pred in all_predictions]
all_references_norm = [normalizer(label) for label in common_voice_test["sentence"]]

# filtering step to only evaluate the samples that correspond to non-zero references
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 模型以进行 Dhivehi 语音识别。继续阅读以获取微调示例的实践经验 🚀

< > 在 GitHub 上更新