从零实现自动语音识别 (ASR)
Transformer 架构最初是为解决自然语言处理领域的复杂任务而提出的。然而,此后它也被应用于其他模态,并且在这些模态中表现显著优于其前身(CNN、HMM、GMM 等)。也就是说,我们不能简单地套用标准的Transformer架构,就指望它在所有模态下都能达到最先进的水平。
1、为什么语音处理比文本处理难得多?
首先,在语音领域,世界上许多语言甚至没有文字,那么我们如何让机器学习一种无法用离散的、基于文本的标记来表示的语言呢?虽然有一些变通方法,但这需要单独撰写一篇文章来讨论,所以我们在这里就不深入探讨了。
更重要的是,语音是连续信号(与离散的文本相比)。为了将语音转换为数字格式,我们使用采样和量化技术。通俗地说,采样率定义了每秒幅度观测的数量(显然,采样率越高,音频越清晰)。现在,数字音频的标准采样率为 44.1 kHz。因此,对于一段 3 秒的音频,我们有:
44100 * 3 = 132300 个振幅值
对于一个半精度的普通 Transformer 模型,这将产生一个大约包含 (132300)² 个元素的注意力矩阵(因为注意力矩阵的大小与序列长度呈平方关系),这大约需要 32.6 GB 的显存。
为了满足你的好奇心,这相当于一段 1 分钟长的数字音频需要大约 12.7 TB 的显存,这远远超出了当前 GPU 的处理能力,即使采用分布式训练也是如此。
这就是特征提取的用武之地!
2、所有这些振幅值都有用吗?
一句话,没用。
如果我将采样率从 44.1 kHz 降低到 16 kHz(例如),我得到的语音信号的语义仍然保持不变。因此,本质上,语音信号上任意一点的邻域都包含一些局部信息,这些信息只有在邻域包含至少 x 个样本时才有意义。
在处理信号(例如语音)时,我们可以使用两种框架来描述它们:
- 时域
这是我们目前用来描述语音的框架。基本上,我们在每个时间单位(单位取决于信号的采样率)上都有振幅值(响度)。
- 频域
相比之下,频域将信号描述为存在于不同频率上的能量。
过去十年间,研究人员自然而然地开发了在时域上进行语音识别的方法,既有单独进行的,也有联合进行的。
3、我们如何在时域中解决显存(VRAM)问题?
正如我们之前讨论的,在Transformer出现之前,卷积神经网络(CNN)在语音识别领域越来越受欢迎。
请记住——图像是二维信号,而音频是一维信号。由于卷积可以应用于任意维度的信号,因此它是处理波形的理想工具。
2020年,Facebook AI(FAIR)推出了一种名为Wav2Vec2的架构,它能够使用CNN从原始音频中学习语音表示。在详细讨论该方法之前,让我们先快速了解一下他们是如何解决显存问题的。
将原始音频输入以下 FeatureEncoder 模块:
这是 Wav2Vec2 中使用的特征编码器的简化版本(代码由作者编写)。
在这里,ConvLayer 模块首先接收原始音频(作为一维信号),并使用步长 2(在本例中)来缩短其长度。也就是说,我们不再对语音信号中的每个样本应用卷积核,而是仅对语音信号中的每隔一个样本应用卷积核(并使用前一个样本和后一个样本通过加权求和来计算特征向量——因为在本例中卷积核大小为 3)。
对于一个 3 秒的语音信号,我们有 132300 个幅度值(来自上一节)。将此信号输入特征编码器后,我们将每层的样本数减半。因此,
132300/2⁴ = 8268.75 个样本(小数部分使用填充来处理,以确保总样本数为 8268.75)。样本数量始终为正整数)。
这使得注意力矩阵的大小为 8268²,约为 130.41 MB(我们仅使用 4 个卷积块就将大小从 32 GB 减少到 130 MB!!!)
在实际的 Wav2Vec2 架构中,他们使用了 7 个卷积块,每个卷积块有 512 个通道,步长分别为 (5, 2, 2, 2, 2, 2),卷积核宽度分别为 (10, 3, 3, 3, 3, 2, 2)。
这种使用卷积层创建内部表示的方法通常被称为卷积子采样。
4、这些潜在的语音表示如何使用离散标记表示?
在语言中,每个标记都被映射到嵌入空间中的一个向量(这种映射用于将标记从嵌入空间解码回标记空间)。理论上,我们希望实现以下目标:
潜在语音表示向量应映射到预定义“码本”有限集合中最接近的向量。
潜在语音表示是高维嵌入空间中的向量,它们被映射到最接近的离散单元(图像由 Nano Banana Pro 生成)。
这本质上是一个聚类任务——你拥有高维空间中的向量,并且希望使用有限的质心集合来表示它们(当质心 ID 用作有损压缩形式时,我们称该方法为“矢量量化”)。Wav2Vec2 通过使用 Gumbel Softmax 的乘积量化来实现此目标。
4.1 乘积量化 (PQ)
我们首先快速了解一下标准矢量量化的问题。假设你有一个 128 维的向量,并且你想执行矢量量化,也就是说,你想将这些向量映射到 16 位字符串的唯一 ID(例如)。你需要 2¹⁶ 个唯一的 ID(在聚类中,我们称之为“质心”)。所有质心的集合称为“码本”。考虑层次 K 均值聚类 (HKM),它计算所有质心之间的距离,并在每次迭代中将距离最近的质心聚类。在这种情况下,由于数据中存在大量的质心,很容易看出这是一个计算量很大的操作。
为了解决这个问题,乘积量化 (PQ) 于 2009 年被提出,其独特的见解是:“为什么我们要将向量量化为单个唯一的 ID,而不是将其量化为一个更小的唯一 ID 向量呢?” 基于这一原则,作者创建了一种方法,将一个 128 维向量分解为 m 个(例如)不同的子向量,然后将这些子向量映射到子空间中的唯一 ID。
因此,沿用我们之前的示例,128 维向量现在被分解为 8 个 16 维向量。将其量化为每个元素 0.25 位(4 位字符串),我们得到每个子向量 2⁴ = 16 个质心。
现在,对于 m 个子向量,将训练 m 个独立的码本。在推理过程中,对于查询向量,我们可以搜索查询的每个子向量的 m 个最接近的质心向量,将它们连接起来,得到最终答案。
在我们的例子中,码本中的这些质心代表了语音的不同“单元”,可以用作离散的标记。
4.2 Gumbel Softmax
给定一个距离度量向量(例如欧氏距离),该向量表示查询子向量与所有质心子向量之间的距离,如何找到最近的质心?最直接的答案是使用负向量的 argmin 或 argmax 函数。虽然这种方法可以给出正确的输出,但它存在两个问题:
- 梯度不连续性
argmax 函数是一个阶跃函数,这意味着它无法微分(梯度在除最大值处以外的所有位置均为零,最大值处梯度为无穷大)。
- 码本崩溃问题
如果我们总是选择绝对最近的质心(确定性 argmax),模型在训练初期可能会“卡住”,只使用少数几个容易到达的质心。其他质心永远不会被选中,接收不到梯度,最终导致模型崩溃。通过采样而不是仅仅最大化,我们可以鼓励模型探索并利用完整的码本。
所以我们需要的是:一个能够从给定分布中采样的可微函数。
解决第一个问题相当容易。为了将 argmax 函数转换为可微函数,我们可以简单地将其替换为温度小于 1 的 softmax 函数,如下所述。
然而,该函数仍然总是选择距离查询向量最近的质心。我们需要找到一种方法,利用带有温度的 softmax 函数从距离分布中采样。
这就是 Gumbel 分布的用武之地。事实证明,如果你对 0 到 1 之间的均匀分布进行采样,然后使用 Gumbel 分布的累积分布函数 (CDF) 执行逆变换采样,并将结果加到 logits(未归一化的原始分数)上,那么这些 logits 上的 softmax 函数(带温度)将遵循由 logits 的 softmax 函数定义的概率分布。这被称为 Gumbel-softmax 技巧。
逆变换采样是一种通过将从标准均匀分布中采样的随机值代入所需分布的逆累积分布函数来生成任意分布的方法。
目标:避免码本崩溃。
我们简单地绘制了无 Gumbel 噪声和有 Gumbel 噪声两种情况下的概率分布图(作者编写的代码)。
概率分布几乎完全相同(由上述代码生成的图)。
因此,使用 Gumbel-Softmax 技巧可以帮助我们在保持函数可微性的同时实现采样,这意味着我们可以避免码本坍缩和梯度不连续的问题。
Gumbel-Softmax 技巧兼具两者的优点:
- 前向传播:它的行为类似于离散采样(选择一个质心)。
- 后向传播:它允许梯度在“软”概率分布中流动,使整个系统可微。
现在,我们拥有了一种从潜在语音表示生成离散标记的自监督方法,这最终得到一个有限的量化表示集(本文中用 Q 表示)。
由于我们假设读者已经了解 Transformer 架构的来龙去脉,因此在结束时域讨论之前,让我们先重点关注目标函数。
5、Transformer 如何训练以理解这些离散单元?
借鉴 BERT 架构,Wav2Vec2 会屏蔽一些随机时间步,Transformer 的任务是从 K 个表示中选择正确的量化潜在语音表示。这组“干扰项”是从同一语音的所有屏蔽时间步中均匀采样得到的。
很自然地,你会认为像余弦相似度这样的相似性度量可以帮助我们找到“正确”的时间戳,你的想法完全正确!损失函数是缩放后的余弦相似度(由超参数缩放)除以所有缩放后的相似度之和。
请记住,负号的存在是因为我们始终希望最小化损失,而相似度是我们希望最大化的目标。
为了保证数值稳定性,作者在论文中使用了 LSE 技巧。
现在,除了对比损失之外,如果模型反复选择相同的码本条目,我们也希望对其进行惩罚。目标是使选择不同码本条目的分布趋向于均匀分布,因此我们希望最大化码本条目的平均 softmax 分布的熵。作者称之为多样性损失。
因此,最终的目标函数是对比损失和缩放多样性损失之和。
我们花了很多时间讨论如何使用卷积神经网络 (CNN) 和巧妙的损失函数来“暴力”处理一维原始波形。但如果我们不必将信号视为一系列振幅点呢?
6、我们如何在频域中解决显存 (VRAM) 问题?
我们都在高中做过“玻璃棱镜”实验,白光只需穿过玻璃棱镜就能被分解成连续光谱(彩虹)。对于音频信号来说,这个“玻璃棱镜”就是傅里叶变换。
对音频信号进行傅里叶变换后,你会得到整个信号中每个频率级别(例如,100Hz、500Hz、2000Hz)对应的能量级别。然而,对于语音信号而言,对整个信号进行傅里叶变换并不合理,因为我们感兴趣的是每个词对应的频率,而不是一个整体。这就引出了短时傅里叶变换 (STFT),即在一个较短的滑动窗口上进行傅里叶变换。
由于这些小窗口在相同的有限时间步长内都具有频率幅度,因此 STFT 可以得到语音信号的一种独特表示:x 轴代表时间步长,y 轴代表频率区间,颜色代表每个时间步长在所有不同频率上的能量。这种表示方法通常被称为频谱图。
问题 — Com计算机的听觉是“线性”的,也就是说,100Hz 和 200Hz 之间的差异与 1000Hz 和 1100Hz 之间的差异相同。然而,人类对低频的辨别能力远强于对高频的辨别能力,也就是说,100Hz 和 110Hz 之间的差异对人类来说非常明显,但 1000Hz 和 1010Hz 之间的差异却几乎无法分辨。这种模拟人类听觉的标度被称为梅尔标度。
如果您想更详细地了解梅尔频谱图,我强烈建议您阅读这篇博文。
现在让我们来看看这对显存 (VRAM) 使用的影响。如果我们使用 25 毫秒的窗口,并在每次迭代中将窗口滑动 10 毫秒(因为窗口会重叠),那么每秒总共可以处理 100 帧。
因此,对于一段 3 秒的音频,
3 * 100 = 300 个向量
因此,我们的注意力矩阵的大小变为 (300)²,约为 0.17 MB。通过切换到频域,我们可以将注意力矩阵的大小减少约 47 倍!
现在,我们将来看一下 ASR 历史上最具影响力的模型之一——OpenAI 的 Whisper。2022 年,OpenAI 发布了 Whisper 模型,该模型基于 68 万小时的多语言数据进行训练,并在 AI 社区引起了轰动。虽然其架构本身并不新颖,但高质量的训练数据使其取得了巨大的成功。让我们快速了解一下该模型,以理解 ASR 任务在频域中的工作原理。
如上图所示,Whisper 除了在频域运行外,还使用了卷积子采样,这意味着可以进一步节省显存 (VRAM) 使用量。
与 Wav2Vec2 模型的一个显著区别在于,Whisper 的训练范式是监督式的,而非自监督式的。
由于 Whisper 使用的是标准的 Transformer 架构,我们无需赘述。Whisper 的真正精髓在于其输入输出的结构,使其能够同时处理多个任务。
尽管 Whisper 仍然是自动语音识别 (ASR) 领域最主流的模型之一,但其模型架构并未在该领域取得显著突破。
7、ASR 中的其他重要方法
虽然 Transformer 架构在全局注意力方面表现出色,但选择 Transformer 架构进行 ASR 意味着放弃使用 CNN 时可以获得的额外优势——归纳偏置。基于此,谷歌提出了一个有趣的方案——Conformer。
7.1 Conformer(卷积 + Transformer)
CNN 最重要的归纳偏置(就我们的应用场景而言)是局部性——卷积网络能够表示信号中的局部关系。为了兼顾两者的优势,谷歌在 Conformer 论文中提出了一种包含卷积块的 Transformer 编码器。
让我们快速回顾一下论文中的主要思想:
7.2 Macaron FFN
受 Macaron-Net 的启发,Conformer 论文还将编码器中的 FFN 层拆分为两个“半层”。其思路是在输入进入注意力层之前对其进行预处理,然后再进行后处理,所有这些操作都旨在保持信号的“强度”与使用标准 MHSA -> FFN 时的信号强度相同。
这有助于模型更快地收敛,并为理解 FFN 执行的确切功能提供了良好的理论优势。
7.3 卷积模块
在编码器模块中,应用 MHSA 之后,Conformer 论文并没有直接使用线性层处理隐藏状态,而是使用卷积模块在处理隐藏状态之前提取局部上下文。
在该模块内部,梅尔频谱图中所有像素的注意力分数会依次经过深度卷积层(使用来自单个通道的邻域像素处理输入)和逐点卷积层(使用来自所有通道的相同坐标的像素值堆栈处理输入)。这有助于模型捕捉精细的局部声学模式。
7.4 神经转换器
当循环神经网络 (RNN) 和长短期记忆网络 (LSTM) 开始处理时间序列数据时,在语音识别中使用这些模型的一个主要问题是,它们是序列到序列 (STS) 模型,这意味着它们只有在处理完整个序列后才会产生输出。然而,语音识别有时却是一个“在线”任务,即需要即时输出。
随后,在 2015 年,谷歌大脑推出了神经转换器,这是一种解码器,可以处理……编码器会为每个时间步输出信号,并生成零个或多个输出。
但它究竟是如何实现的呢?关键在于一个名为“空白”符号的特殊标记。在标准模型中,每个输入帧都会强制输出一个信号。但在转换器中,模型每毫秒都会做出选择:如果确信输入正确,则发出一个标记;否则,发出一个“空白”标记——这实际上意味着“我正在监听,但目前还没有新信息”,或者“我正在等待下一个声音”。
这种架构是流式自动语音识别 (ASR) 的行业标准。当你与 Siri 或 Google Assistant 对话,并看到单词随着你的说话逐个出现时,很可能就是神经转换器在后台运行。由于这个话题理论内容丰富,而且我们已经讨论了很多,所以我们将在另一篇文章中继续探讨。
从原始音频开始,我们已经看到,像乘积量化和傅里叶变换这样“简单”的数学技巧,如何为我们节省数GB的内存,并使机器真正理解人类语音。
如今的自动语音识别(ASR)系统堪称工程奇迹,它融合了数学、统计学、物理学和计算机科学。虽然你完全可以在不了解梅尔滤波器组工作原理的情况下使用这些模型,但在开发任何应用程序时,了解其理论基础总是有益的,这样才能使其更加优化。
原文链接:Automatic Speech Recognition (ASR) From Scratch (With Intuition)
汇智网翻译整理,转载请标明出处