16.4 自监督模型:HuBERT
编码器—解码器架构之外,另一种方法是自监督语音模型。这些模型不直接学习把声学输入映射为字母和词元字符串,而是先从声学输入中引导出一组离散语音单元,学习把波形映射到这些诱导出的单元。这一预训练阶段不需要转写,只需要无标注语音文件。预训练后,可以在规模较小的标注数据(与转写配对的音频)上对模型进行微调,使其执行 ASR。这些模型的优点是能够利用大量未转写音频完成大部分训练。
这里介绍一种自监督模型 HuBERT(Hsu et al., 2021)。HuBERT 以及 wav2vec 2.0(Baevski et al., 2020)等类似模型,使用了第 9 章介绍的 BERT 掩码语言模型的相同直觉:遮住输入的一部分,训练模型猜测掩码隐藏的内容。

图 16.8 HuBERT 训练中推理过程的架构示意图。16 kHz 波形文件经过一系列卷积层,其中一些帧被替换为 MASK 词元;随后序列通过 Transformer 堆叠,再经过一个把 Transformer 输出投影到输出嵌入的线性层。将该嵌入与 100/500 个语音类别各自的嵌入计算余弦相似度,得到 logit,再通过 softmax 得到每帧各类别上的概率分布。
16.4.1 HuBERT 的前向过程¶
先只介绍训练中使用的 HuBERT 前向过程,然后再把它放入完整训练流程中,说明反向过程。前面提到,HuBERT 前向过程的输入是原始 16 kHz 波形文件;每个 20 ms 时间帧的输出是诱导语音类别集合 上的概率分布(取决于训练阶段,可以是 100 类或 500 类)。图 16.8 展示了组件示意图。波形文件经过 7 个 512 通道的卷积层;这些卷积层同时学习提取频谱信息,并把输入序列缩短到 20 ms 帧。随后加入位置编码,再应用 GELU 和层归一化。选定的词元随后被 MASK 词元替换;MASK 是一个在所有被掩码帧之间共享的学习嵌入。整个序列通过 Transformer 堆叠,输出再通过线性投影层 。将每个 20 ms 帧的输出嵌入与 100(或 500)个语音类别各自的嵌入计算余弦相似度,得到 100 个 logit,表示当前 20 ms 音频时间步与每个类别的相似度;然后通过 softmax 得到类别上的概率分布。
16.4.2 HuBERT 的学习¶
先讨论如何诱导出作为训练目标的 100 或 500 个语音类别。为了引导这些单元,HuBERT 从 Mel 频率倒谱系数(MFCC)开始。MFCC 是一个 39 维特征向量,突出与语音单元检测相关的信号性质;可以按照 15.6 节概述的方法从声学信号中提取。我们为整个声学训练数据集提取 MFCC 向量(原始 HuBERT 实现使用 960 小时 LibriSpeech 数据,得到 1.72 亿个向量)。然后,使用 16.4.3 节将介绍的 k-means 聚类算法对 MFCC 向量进行聚类。聚类就是把向量分成 个类别。聚类输出一个包含 个向量的码本,这些向量称为码字、模板或原型,每个向量代表一个簇。这 个簇中的每一个都是可以作为训练金标准目标的声学单元。
现在考虑整个训练过程。声学输入经过 CNN 层后,在上下文窗口中选择一段词元进行掩码,并把这些词元对应的 CNN 输出替换为 MASK 嵌入。整个上下文窗口通过 Transformer 层,在每个时间步 ,Transformer 输出 与投影层矩阵 相乘,将其投影到类别嵌入空间。然后将所得表示与 中每个类别的嵌入进行余弦相似度比较,并使用温度参数 的 softmax,把相似度转换为概率:
如图 16.9 所示,在这个前向过程并行进行的过程中,输入波形经过 MFCC 处理,得到一个 39 维向量;通过选择码本中最相似的质心,把该向量映射到 100 个类别之一。损失函数是模型在被掩码词元集合 上赋予这些正确单元的概率之和:
因此,与掩码语言建模一样,模型训练目标是预测与被掩码帧相关联的单元。随后将该损失通过模型反向传播。

图 16.9 HuBERT 训练的第一阶段。使用由 39 维 MFCC 向量聚类得到的 100 个单元码本作为训练目标。对每个时间步 ,计算该类别的概率,并使用 log 概率作为损失。
模型初步学会映射到 MFCC 向量质心后,会进行第二阶段训练:把模型产生的表示聚类为 500 个簇,并用这些簇作为训练目标。其直觉是,初始 MFCC 簇会把模型引向语音表示;训练充分后,模型会学习到更准确、更细粒度的表示。图 16.10 展示了这一思想。

图 16.10 HuBERT 两个训练阶段的目标创建。第一阶段为全部训练数据计算 39 维 MFCC 向量,然后使用 k-means 聚类,创建 100 个声学单元。第二阶段把一部分训练数据输入第一阶段训练后的 HuBERT 模型,取中间 Transformer 层(第 6 层)的输出,再使用 k-means 聚类,创建 500 个单元。
HuBERT 完成预训练后,移除投影层和余弦层,加入一个随机初始化的线性层加 softmax 层,将表示映射到 ASR 任务的 29 个类别(对应 26 个英文字母和少数额外字符)。冻结 CNN,使用 16.5 节将介绍的 CTC 损失函数,对模型其余部分进行 ASR 微调。

图 16.11 HuBERT 预训练后的微调过程。移除投影层和余弦计算步骤,只保留随机初始化的投影/softmax 层。冻结 CNN 层,在带转写的音频数据集上微调模型的其余部分,并使用 CTC 损失(16.5 节)以字母作为输出。图中用红色表示微调时更新的参数(投影层和 Transformer 堆叠)。
16.4.3 K-means 聚类¶
本节更正式地介绍 k-means 聚类算法。K-means 是一类将向量数据分为 个簇的算法。只要希望以相同方式处理一组元素,聚类就很有用。在语音处理中,当需要把实数向量集合转换为离散符号集合时,聚类非常常用。除 HuBERT 外,第 18 章还会把它作为为 TTS 创建离散声学词元的算法再次介绍。
通常,k-means 这个名字指这一算法族中的一个简单版本:给定 个向量 ,每个向量有 个维度,即 ,以及常数 (通常 ),通过两个步骤反复迭代。
这两个步骤迭代更新 个质心向量。质心是 维空间中一组点的几何中心。
算法包括两个步骤。在分配步骤中,给定一组当前的 个质心和向量数据集,把每个向量分配给码字最近的簇(按平方欧氏距离计算)。在重新估计步骤中,通过重新计算均值向量,为每个簇重新计算码字。注意,所得均值向量不一定是数据集中的实际点。我们在两个步骤之间来回迭代。
算法如下:
初始化:对每个簇 ,随机选择一个向量 作为该簇的码字(也称模板或原型)。结果是一个码本,其中每个 个簇都有一个码字。
然后反复迭代,直到收敛:
分配:对于数据集中的每个向量 ,通过选择具有最近码字 的簇,将其分配给 个簇之一。最简单地,可以把“最近”定义为码字与 之间平方欧氏距离最小的簇。
其中 是向量的 L2 范数,即 。
重新估计:重新计算每个簇中所有向量的均值(质心),以重新估计该簇的码字。如果 是簇 中向量的集合,那么