Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

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 时间帧的输出是诱导语音类别集合 CC 上的概率分布(取决于训练阶段,可以是 100 类或 500 类)。图 16.8 展示了组件示意图。波形文件经过 7 个 512 通道的卷积层;这些卷积层同时学习提取频谱信息,并把输入序列缩短到 20 ms 帧。随后加入位置编码,再应用 GELU 和层归一化。选定的词元随后被 MASK 词元替换;MASK 是一个在所有被掩码帧之间共享的学习嵌入。整个序列通过 Transformer 堆叠,输出再通过线性投影层 AA。将每个 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 向量进行聚类。聚类就是把向量分成 kk 个类别。聚类输出一个包含 kk 个向量的码本,这些向量称为码字、模板或原型,每个向量代表一个簇。这 kk 个簇中的每一个都是可以作为训练金标准目标的声学单元。

现在考虑整个训练过程。声学输入经过 CNN 层后,在上下文窗口中选择一段词元进行掩码,并把这些词元对应的 CNN 输出替换为 MASK 嵌入。整个上下文窗口通过 Transformer 层,在每个时间步 tt,Transformer 输出 htL\mathbf{h}_t^L 与投影层矩阵 AA 相乘,将其投影到类别嵌入空间。然后将所得表示与 CC 中每个类别的嵌入进行余弦相似度比较,并使用温度参数 au=0.1 au=0.1 的 softmax,把相似度转换为概率:

p(cX,t)=exp( sim (Aht,ec)/τ)c=1Cexp( sim (Aht,ec)/τ)(16.12)p (c | \mathbf {X}, t) = \frac {\exp (\text { sim } (\mathbf {A h} _ {t} , \mathbf {e} _ {c}) / \tau)}{\sum_ {c ^ {\prime} = 1} ^ {C} \exp (\text { sim } (\mathbf {A h} _ {t} , \mathbf {e} _ {c ^ {\prime}}) / \tau)}\tag{16.12}

如图 16.9 所示,在这个前向过程并行进行的过程中,输入波形经过 MFCC 处理,得到一个 39 维向量;通过选择码本中最相似的质心,把该向量映射到 100 个类别之一。损失函数是模型在被掩码词元集合 MM 上赋予这些正确单元的概率之和:

L=tMlogp(ztX)(16.13)L = \sum_ {t \in M} \log p (z _ {t} | \mathbf {X})\tag{16.13}

因此,与掩码语言建模一样,模型训练目标是预测与被掩码帧相关联的单元。随后将该损失通过模型反向传播。

图 16.9 HuBERT 训练的第一阶段。使用由 39 维 MFCC 向量聚类得到的 100 个单元码本作为训练目标。对每个时间步 tt,计算该类别的概率,并使用 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 是一类将向量数据分为 kk 个簇的算法。只要希望以相同方式处理一组元素,聚类就很有用。在语音处理中,当需要把实数向量集合转换为离散符号集合时,聚类非常常用。除 HuBERT 外,第 18 章还会把它作为为 TTS 创建离散声学词元的算法再次介绍。

通常,k-means 这个名字指这一算法族中的一个简单版本:给定 NN 个向量 v(1)v(N)\mathbf{v}^{(1)}\ldots\mathbf{v}^{(N)},每个向量有 dd 个维度,即 v(i)Rd\mathbf{v}^{(i)}\in\mathbb{R}^d,以及常数 kk(通常 NkN\gg k),通过两个步骤反复迭代。

这两个步骤迭代更新 kk 个质心向量。质心是 nn 维空间中一组点的几何中心。

算法包括两个步骤。在分配步骤中,给定一组当前的 kk 个质心和向量数据集,把每个向量分配给码字最近的簇(按平方欧氏距离计算)。在重新估计步骤中,通过重新计算均值向量,为每个簇重新计算码字。注意,所得均值向量不一定是数据集中的实际点。我们在两个步骤之间来回迭代。

算法如下:

初始化:对每个簇 kk,随机选择一个向量 μkRd\mu_k\in\mathbb{R}^d 作为该簇的码字(也称模板或原型)。结果是一个码本,其中每个 kk 个簇都有一个码字。

然后反复迭代,直到收敛:

  1. 分配:对于数据集中的每个向量 v(i)\mathbf{v}^{(i)},通过选择具有最近码字 μ\mu 的簇,将其分配给 kk 个簇之一。最简单地,可以把“最近”定义为码字与 v(i)\mathbf{v}^{(i)} 之间平方欧氏距离最小的簇。

cluster(i)=argmin1jkv(i)μj2(16.14)\operatorname{cluster} ^ {(i)} = \underset {1 \leq j \leq k} {\operatorname{argmin}} || \mathbf {v} ^ {(i)} - \boldsymbol {\mu} _ {j} || ^ {2}\tag{16.14}

其中 v||\mathbf{v}|| 是向量的 L2 范数,即 j=1dvj2\sqrt{\sum_{j=1}^{d}v_j^2}

  1. 重新估计:重新计算每个簇中所有向量的均值(质心),以重新估计该簇的码字。如果 SiS_i 是簇 ii 中向量的集合,那么

i:μi=1SivSiv(16.15)\begin{array}{c} \forall i: \\ \mu_ {i} = \frac {1}{| S _ {i} |} \sum_ {\mathbf {v} \in S _ {i}} \mathbf {v} \end{array}\tag{16.15}