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.2 卷积神经网络

CNN 卷积神经网络(convolutional neural network,CNN,有时简称 convnet)是一种特别适合在语音和视觉应用中提取特征的网络架构。用于语音的卷积层接收音频输入的表示(可以是原始音频或 Mel 频谱),并输出输入语音的一系列潜在表示。在 Whisper、wav2vec2.0 或 HuBERT 等 ASR 系统中,卷积层作为初始层堆叠起来,产生语音表示,再将其传给 Transformer 层。

标准前馈层是全连接的:每个输入都连接到每个输出。相比之下,卷积网络使用卷积核这一思想;卷积核是一种较小的网络,我们让它在输入上滑动。例如,在图像分类任务中,让卷积核水平和垂直地在图像上移动,以识别视觉特征,因此视觉卷积网络称为二维(2D)卷积网络。对语音而言,我们让卷积核沿信号的时间维度滑动,以提取语音特征,因此语音 CNN 是一维卷积网络。

再具体展开这个直觉。先从一个非常示意性的卷积层开始:它接收单个向量序列 x1xtx_1 \ldots x_t 作为输入,并输出长度同样为 tt 的单个向量序列 z1zt\mathbf{z}_1 \ldots \mathbf{z}_t。之后再讨论如何处理更复杂的输入和输出。

CNN 使用卷积核——一小组权重向量 w1wk\mathbf{w}_1 \ldots \mathbf{w}_k——提取特征。它通过将卷积核与输入做卷积来完成这一点。卷积核与信号的卷积有三个步骤:

  1. 将卷积核从左到右翻转。

  2. 让卷积核逐帧(沿时间)通过输入:

    • 在每一帧计算卷积核与局部输入值的点积。

  3. 输出得到的点积序列。

可以把卷积过程看作寻找信号中与卷积核相似的区域,因为两个向量相似时点积较高。卷积操作由 * 运算符表示(这是该符号不幸的重载,它也表示普通乘法)。看看如何计算 xw\mathbf{x} * \mathbf{w},即单个向量 xx 与卷积核向量 ww 的卷积。先考虑卷积核宽度为 1 的简单情况。每个输出元素 zj\mathbf{z}_j 都是卷积核与 xj\mathbf{x}_j 的乘积:

 convolution with width - 1 kernel: zj=xjw0j:1jt(16.1)\text { convolution with width - 1 kernel: } \quad \mathbf {z} _ {j} = \mathbf {x} _ {j} \mathbf {w} _ {0} \quad \forall j: 1 \leq j \leq t\tag{16.1}

图 16.2 直观展示了这一计算。

图 16.2 使用宽度为 1 的卷积核(滤波器)进行卷积的示意图。卷积核在输入上移动,每一帧的输出 zi\mathbf{z}_i 是卷积核与输入帧的点积。卷积核长度为 1 时无需考虑翻转卷积核,点积就是标量乘法。图中展示了 z3\mathbf{z}_3 作为 x3×w1\mathbf{x}_3 \times \mathbf{w}_1 的计算。

现在转向更长的卷积核。虽然我们把卷积的第一步描述为翻转卷积核,但在 ASR 系统(或 pytorch 等组件库)中,实际上会跳过这一步。从技术上说,这意味着我们使用的算法并不是卷积,而是互相关:卷积核在信号上移动,逐帧计算点积,但事先不翻转卷积核。由于卷积核参数会在训练中学习,这种差异并不重要;模型很容易学会顺序相反的参数。不过,出于历史原因,我们仍然把这一过程称为一维卷积,而不是互相关。

下面给出适用于较长卷积核的更一般方程。为了避免卷积在信号左右边缘未定义,可以在信号开头和末尾各添加 pp 个零,对输入进行填充;这样卷积核中心可以从第一个元素 x1\mathbf{x}_1 开始,x1\mathbf{x}_1 左侧也有定义值。这样还可以方便地使输出长度与输入长度相同。具体来说,令卷积核向量包含奇数个元素,长度 k=2p+1k=2p+1,这样中心元素两侧各有 pp 个元素。输出向量 z\mathbf{z} 的每个元素 zjz_j 由如下点积计算:

zj=i=ppxj+iwi+p(16.2)\mathbf {z} _ {j} = \sum_ {i = - p} ^ {p} \mathbf {x} _ {j + i} \mathbf {w} _ {i + p}\tag{16.2}

图 16.3 展示了卷积 xwx*w 的计算:卷积核宽度为 3,并在 xx 开头和末尾各填充 1 帧、值为 0 的内容。

图 16.3 使用宽度为 3、填充为 1 的卷积核(滤波器)进行卷积的示意图。信号开头和末尾各加入一个零值。已经翻转的卷积核在输入上移动,每一帧的输出 zi\mathbf{z}_i 是卷积核与窗口内输入的点积。图中展示了 z3\mathbf{z}_3 的计算。

注意,卷积核的大小 kk(感受野)相对于信号通常设计得很小。例如,在 Whisper 的卷积层中,卷积核宽度为 3 帧,即卷积核是长度为 3 的向量(说卷积核感受野为 3)。这意味着卷积核与 3 帧语音进行比较。Whisper 每 10 ms 有一帧,每帧表示 25 ms 的语音信息。因此,每个卷积核从约 45 ms 的语音中提取信息 (10+10+12.5+12.5)(10+10+12.5+12.5)。这足以提取共振峰过渡、塞音闭塞或送气等各种语音特征。

到这里,我们描述的是简化的卷积:输入是单个向量 xx,输出是单个向量 zz,两者都表示随时间变化的信号。在实践中,卷积层的输入通常来自对数 Mel 频谱,因此包含多个(例如 128 个)通道,每个通道对应一个对数 Mel 滤波器的输出。卷积核会为每个输入通道设置独立向量。我们说卷积核深度为 128,即卷积核的形状为 [128, 3]。

为了得到卷积核的输出,要对所有输入通道求和。也就是说,对每个输入通道 xc\mathbf{x}^c,把卷积核 ww 与其做卷积,得到一个输出 zc\mathbf{z}^c,然后把所有结果相加:

z=c=1Cixcw(16.3)\mathbf {z} = \sum_ {c = 1} ^ {C _ {i}} \mathbf {x} ^ {c} * \mathbf {w}\tag{16.3}

因此,帧 jj 的输出 zjz_j 整合了所有输入通道的信息。

最后,卷积层的输出也比每帧一个标量值组成的向量更复杂。给定输入帧,卷积层的输出需要是一个嵌入,即该帧的潜在表示。和所有神经模型一样,潜在表示应具有模型维度。以 Whisper 为例,模型维度为 1280,因此卷积层需要为模型的 1280 个维度各设置一个输出通道。为此,实际会为每个模型维度学习一个独立卷积核:学习 1280 个卷积核,每个卷积核的深度是输入通道数(例如 128),滤波器宽度为 3。这样,每帧的嵌入表示就包含 1280 个独立计算的输入信号特征。图 16.4 展示了示意图。

图 16.4 具有 128 个输入通道和 1024 个输出通道的卷积网络示意图。在时间点 ii,1024 个卷积核中的一个(每个深度为 128、宽度为 3)分别与 128 个对数 Mel 频谱输入向量进行点积,然后求和,产生时间 ii 输出嵌入某个维度的单个值。

一维卷积层还可以有步长。步长是每一步在输入上移动卷积核的距离。上图中的步长为 1,即先将卷积核放在 x1x_1 上,再放在 x2x_2x3x_3 等位置。步长为 2 时,会先把卷积核放在 x1\mathbf{x}_1 上,再放在 x3\mathbf{x}_3x5\mathbf{x}_5 等位置。步长越长,输出序列越短;步长为 2 意味着输出序列 zz 的长度是输入序列 xx 的一半。步长大于 1 的卷积层通常用于缩短输入序列。这一方面有助于减少内存和计算带宽,另一方面也有助于解决声学帧嵌入(10 ms)与字母或词之间的长度不匹配:字母或词覆盖的信号长度要大得多。

最后,在实践中,卷积层之后还可以接一个输出非线性函数,例如 ReLU 层。