目录

语音识别与Wav2Vec2

作者: Moto Hira

本教程展示了如何使用预训练的wav2vec 2.0模型进行语音识别 [论文]。

概述

语音识别的过程如下所示。

  1. 从音频波形中提取声学特征

  2. 逐帧估计声学特征的类别

  3. 从类别概率序列中生成假设

Torchaudio 提供了对预训练权重及相关信息(如预期采样率和类别标签)的便捷访问。它们被打包在一起,并在 torchaudio.pipelines 模块下提供。

准备

import torch
import torchaudio

print(torch.__version__)
print(torchaudio.__version__)

torch.random.manual_seed(0)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

print(device)
2.6.0.dev20241104
2.5.0.dev20241105
cuda
import IPython
import matplotlib.pyplot as plt
from torchaudio.utils import download_asset

SPEECH_FILE = download_asset("tutorial-assets/Lab41-SRI-VOiCES-src-sp0307-ch127535-sg0042.wav")
  0%|          | 0.00/106k [00:00<?, ?B/s]
100%|##########| 106k/106k [00:00<00:00, 38.9MB/s]

创建一个流程

首先,我们将创建一个 Wav2Vec2 模型,该模型负责特征提取和分类。

在 torchaudio 中有两种类型的 Wav2Vec2 预训练权重可供使用。一种是针对 ASR 任务进行微调的,另一种是没有进行微调的。

Wav2Vec2(以及HuBERT)模型是以自监督的方式进行训练的。它们首先仅使用音频进行训练以学习表示,然后通过附加标签对特定任务进行微调。

未进行微调的预训练权重也可以用于其他下游任务的微调,但本教程不涵盖这一点。

我们在这里将使用 torchaudio.pipelines.WAV2VEC2_ASR_BASE_960H

有多个预训练模型可供在 torchaudio.pipelines 中使用。 请查看文档以了解它们是如何训练的详细信息。

该 bundle 对象提供了实例化模型和其他信息的接口。采样率和类别标签的获取方式如下。

bundle = torchaudio.pipelines.WAV2VEC2_ASR_BASE_960H

print("Sample Rate:", bundle.sample_rate)

print("Labels:", bundle.get_labels())
Sample Rate: 16000
Labels: ('-', '|', 'E', 'T', 'A', 'O', 'N', 'I', 'H', 'S', 'R', 'D', 'L', 'U', 'M', 'W', 'C', 'F', 'G', 'Y', 'P', 'B', 'V', 'K', "'", 'X', 'J', 'Q', 'Z')

模型可以按以下方式构建。此过程将自动获取预训练的权重并将其加载到模型中。

model = bundle.get_model().to(device)

print(model.__class__)
Downloading: "https://download.pytorch.org/torchaudio/models/wav2vec2_fairseq_base_ls960_asr_ls960.pth" to /root/.cache/torch/hub/checkpoints/wav2vec2_fairseq_base_ls960_asr_ls960.pth

  0%|          | 0.00/360M [00:00<?, ?B/s]
 15%|#4        | 52.6M/360M [00:00<00:00, 550MB/s]
 30%|###       | 109M/360M [00:00<00:00, 572MB/s]
 45%|####5     | 163M/360M [00:00<00:00, 520MB/s]
 59%|#####9    | 213M/360M [00:00<00:00, 399MB/s]
 75%|#######5  | 270M/360M [00:00<00:00, 456MB/s]
 91%|######### | 327M/360M [00:00<00:00, 497MB/s]
100%|##########| 360M/360M [00:00<00:00, 494MB/s]
<class 'torchaudio.models.wav2vec2.model.Wav2Vec2Model'>

加载数据

我们将使用来自VOiCES数据集的语音数据,该数据集授权于 Creative Commos BY 4.0。

IPython.display.Audio(SPEECH_FILE)


要加载数据,我们使用 torchaudio.load()

如果采样率与管道预期的不同,那么 我们可以使用 torchaudio.functional.resample() 进行重采样。

注意

waveform, sample_rate = torchaudio.load(SPEECH_FILE)
waveform = waveform.to(device)

if sample_rate != bundle.sample_rate:
    waveform = torchaudio.functional.resample(waveform, sample_rate, bundle.sample_rate)

提取音频特征

下一步是从音频中提取声学特征。

注意

为ASR任务微调的Wav2Vec2模型可以一步完成特征提取和分类,但为了教程的完整性,我们在这里也展示如何进行特征提取。

with torch.inference_mode():
    features, _ = model.extract_features(waveform)

返回的特征是一个张量列表。每个张量是某个 transformer 层的输出。

fig, ax = plt.subplots(len(features), 1, figsize=(16, 4.3 * len(features)))
for i, feats in enumerate(features):
    ax[i].imshow(feats[0].cpu(), interpolation="nearest")
    ax[i].set_title(f"Feature from transformer layer {i+1}")
    ax[i].set_xlabel("Feature dimension")
    ax[i].set_ylabel("Frame (time-axis)")
fig.tight_layout()
Feature from transformer layer 1, Feature from transformer layer 2, Feature from transformer layer 3, Feature from transformer layer 4, Feature from transformer layer 5, Feature from transformer layer 6, Feature from transformer layer 7, Feature from transformer layer 8, Feature from transformer layer 9, Feature from transformer layer 10, Feature from transformer layer 11, Feature from transformer layer 12

特征分类

一旦提取了声学特征,下一步就是将它们分类到一组类别中。

Wav2Vec2 模型提供了一种在一步中完成特征提取和分类的方法。

输出的形式是logits。它不是以概率的形式呈现的。

让我们来可视化一下。

plt.imshow(emission[0].cpu().T, interpolation="nearest")
plt.title("Classification result")
plt.xlabel("Frame (time-axis)")
plt.ylabel("Class")
plt.tight_layout()
print("Class labels:", bundle.get_labels())
Classification result
Class labels: ('-', '|', 'E', 'T', 'A', 'O', 'N', 'I', 'H', 'S', 'R', 'D', 'L', 'U', 'M', 'W', 'C', 'F', 'G', 'Y', 'P', 'B', 'V', 'K', "'", 'X', 'J', 'Q', 'Z')

我们可以看到,在时间轴上某些标签有很强的指示性。

生成字幕

从标签概率序列中,我们现在想要生成文本。生成假设的过程通常被称为“解码”。

解码比简单的分类更为复杂,因为某些时间步的解码可能会受到周围观测值的影响。

例如,取一个词,比如 nightknight。即使它们的 先验概率分布不同(在典型的对话中,night 出现的频率远高于 knight),为了准确 地生成包含 knight 的文本,例如 a knight with a sword, 解码过程必须推迟最终决定,直到看到 足够的上下文。

已经提出了许多解码技术,它们需要外部资源,例如词典和语言模型。

在本教程中,为了简化起见,我们将执行贪心解码,这种解码方式不依赖于这些外部组件,而是在每个时间步简单地选择最佳假设。因此,上下文信息未被使用,只能生成一个转录文本。

我们首先从定义贪心解码算法开始。

class GreedyCTCDecoder(torch.nn.Module):
    def __init__(self, labels, blank=0):
        super().__init__()
        self.labels = labels
        self.blank = blank

    def forward(self, emission: torch.Tensor) -> str:
        """Given a sequence emission over labels, get the best path string
        Args:
          emission (Tensor): Logit tensors. Shape `[num_seq, num_label]`.

        Returns:
          str: The resulting transcript
        """
        indices = torch.argmax(emission, dim=-1)  # [num_seq,]
        indices = torch.unique_consecutive(indices, dim=-1)
        indices = [i for i in indices if i != self.blank]
        return "".join([self.labels[i] for i in indices])

现在创建解码器对象并解码转录文本。

decoder = GreedyCTCDecoder(labels=bundle.get_labels())
transcript = decoder(emission[0])

让我们查看结果,并再次聆听音频。

print(transcript)
IPython.display.Audio(SPEECH_FILE)
I|HAD|THAT|CURIOSITY|BESIDE|ME|AT|THIS|MOMENT|


ASR模型使用称为连接时序分类(CTC)的损失函数进行微调。 CTC损失的详细信息在 这里解释。在CTC中,空白令牌(ϵ)是一个 特殊令牌,表示前一个符号的重复。在 解码过程中,这些被简单地忽略。

结论

在本教程中,我们学习了如何使用 Wav2Vec2ASRBundle 来 执行声学特征提取和语音识别。构建模型并获取发射结果仅需两行代码。

model = torchaudio.pipelines.WAV2VEC2_ASR_BASE_960H.get_model()
emission = model(waveforms, ...)

脚本的总运行时间: ( 0 分钟 4.590 秒)

通过 Sphinx-Gallery 生成的画廊

文档

访问 PyTorch 的全面开发人员文档

查看文档

教程

获取面向初学者和高级开发人员的深入教程

查看教程

资源

查找开发资源并解答您的问题

查看资源