CNN in Acoustic Model

ASR这块的CNN&RNN大约从13年开始就已经被陆续玩烂了。从CLDNN之后,最近几年貌似在深度和端到端上更加引人关注,例如DeepRNN(Highway, Residual等等)声学建模,DeepCNN的前端增强,前段时间看了一下Interspeech的论文,发现声学模型这块RNN和E2E明显成为两大热门专题。我之前看过KWS和解码的一些东西,没有过多的花精力在声学模型上。因此,也一直想多搭搭模型,跑跑实验,测测WER。

我想将声学模型这块任务从kaldi迁移到pytorch/tensorflow上,理由是自己可以多了解一些流程上的具体工作。kaldi的强大在于,它从前端的特征提取,数据准备,语料对齐,到声学模型的训练,后期的解码,提供了整套的服务。但是问题是,很多人跑完脚本之后,并不知道我这个模型的输入构建,以及训练流程具体是怎么进行的,除非细致的看一下代码。我个人认为这对学习者而言不是什么好事。因此考虑切换工具,自己把控数据格式,模型搭建和训练流程。

这里先说一下CNN。

CNN概述

卷积这个操作在信号领域本身就是含有特殊意义的,传统的方法可以人工设计一个滤波器(高通/低通/带通之类的)对输入信号进行信息/特征提取或者过滤,它不像仿射变换,很难理解它在实际问题中到底做了什么。比如在图像中,高通滤波可以检测出图片的边缘信息,这就可以理解成一种图像特征(边缘特征)。检测出这种特征的滤波器当然不止一种,边缘检测这块就有Roberts,Sobel,Prewitt等等算子(具体可以参见冈萨雷斯的数字图像处理,本科的东西有点忘了)。 CNN的卷积层就可以理解为一组(假设为$N$)这样的滤波器,分别对输入信号进行卷积(根据输入的维度,可以是一维/二维的),得到$N$组特征。CNN的训练就是要训出这样的一组滤波器。 使得在这组滤波特征之上,配合激活,池化,以及feed后续网络层的操作使得模型分类/回归效果最佳。

实际网络输入都是离散化(经过采样)的值,因此所谓的卷积均为离散形式。记得在数字信号里面,解释卷积是这么回事:

信号$A$和$B$卷积,将$B$翻转,从左往右的逐步移动,每移动一次,计算对应位置的乘积之和,直到两者完全不相交为止

这个时域上的翻转比较难理解,但是如果$B$是卷积核,又是对称的,那么解释卷积就变成了模板操作,即将卷积核/模板$B$在$A$上移动,在$A$被$B$覆盖的区域内(感受野)两者对应相乘。所以实际操作中并不考虑这个翻转操作。也可以这么理解,CNN训练之前,各个滤波器/卷积核/模板都是未知的,卷积的时候不做翻转,那么最终训出的结果就是翻转之后的结果,道理是一样的。另外需要注意的是,信号里面的卷积默认做padding的,即超出被卷信号$A$的时长部分按照0处理,因此卷积之后的结果一定比原来信号时长长,我记得好像有个结论$M+N-1$之类的。但是CNN里面的卷积(以二维为例),如果设置padding大小,那么卷出来的结果是越来越小的。

因此卷积这一步有如下概念:

  1. 卷积核大小
  2. 卷积核移动步长,即每次在特定方向上移动多少步
  3. 是否要做边缘padding,是否要边缘补充0

类似的,池化操作也有以上概念。池化层相当于一组特征的滤波器/卷积核,它对输入进行特定的变换,比如取最大(maxpool)或者取平均(avgpool)等等。池化层的作用在于减少信息冗余量,提高特征鲁棒性。CNN设计者认为卷积层之后的结果在空间上是具有variation的,对相邻区域的特征进行将采样,可以减少这种variation带来的不稳定性。另外池化层需要放在激活层之后。

在设计CNN的时候,需要关注输入输出信号之间的大小关系,定义在某方向上的卷积核大小为$N_k$,移动步长为$N_s$,padding长度为$N_p$,该方向上输入信号长度为$N_i$,那么对应方向上输出长度$N_o$由下式得出:

如果要保证输入输出信号大小不发生变化,那么保证$N_s = 1$,$N_p = (N_k - 1) / 2$。

CNN这块还有一个信道(channel)的概念,比如语音信号中的delta,图像的RGB等等。假设输入为3个信道,那么$N$个卷积核产生的特征个数是$3N$还是$N$?我的理解还是$N$,只不过卷积需要在三个信道上进行,并将对应的结果相加,表示如下:

其中$J$表示输入信道个数,$I,O$表示输入输出,$W_{ij}$表示输入信道$i$和输出信道$j$之间的卷积核。

补充,Github上关于卷积的一个可视化

CNN in ASR

首先需要说明,CNN在语音中被设计来降低频域的variation(时域的variation是用HMM来建模)的,因此很多声学模型中的CNN仅仅在输入特征的频率轴上进行卷积(已经有实验验证,在频率轴上卷积的效用更大一些。频率轴卷积是什么意思后面给出自己的理解)。故输入CNN的特征不可以是MFCC(因为其中做了iFFT,破坏了频谱的局部相关性),只能是谱特征以及其衍生特征比如FBANK等等。通过语谱图可以知道,同一个音素的发音在各个频率的分布是有规律的,但是不同的人,不同的性别在频率轴上的共振峰会有shift。通过卷积和池化操作可以降低这种shift带来的影响,相当于提取了一个更加鲁棒的特征。

其次,就是卷积这个操作的参数量很少,对于$N$个卷积核,一层仅仅会产生$N \times K_x \times K_y$个参数,因此在移动终端设备上的应用前景很大(模型复杂度低,计算量少,不同卷积之间可以并行等等)。CNN常见的使用方式是通过卷积层提取鲁棒特征,作为后续的全连接层的输入。

图像自然就是二维的,语音的基本单位为帧,一帧特征对应的只是向量,输入CNN的话,取一个时间窗$T$内的特征构成二维的频谱图$F_{B \times T}$。我对进行频率轴上的卷积的理解如下:

即卷积核的长度和时间窗保持一致,宽度自定(也就是指定的filter_size),移动仅仅在频率轴上进行。这样实际上卷积的结果是一个向量。而如果在时间轴也进行卷积,那么卷积核的大小均可以自定,卷积产生一个矩阵。

但是在文献[4]中提出了两种组织方式,这里以40维FBANK特征来说明:

  1. 组织成$N$个$T \times F$的矩阵,故进行二维卷积。$N$为feature maps的个数,取决于是否进行delta
  2. 组织成$N \times T$个向量,故进行一维卷积。每个向量长度为$F$,$N \times T$为feature maps的个数,取决于是否delta以及上下文的宽度。

ASR这边的文献中有几个常见的概念,我的理解如下:

  • feature maps:$N$个卷积核卷积输入信号得到$N$组卷积特征,每一组滤波特征称为一个feature maps,等于卷积层的输出信道数目,可以理解为信道。
  • feature bands:以40维FABNK特征为例子,每一维上的特征序列称为一个feature band。如果连续多帧,一个feature band对应一个特征向量,单帧仅仅为一个数值。
  • LWS/FWS(local/full weight sharing):默认一个卷积核一次卷积操作中保持不变,称为FWS,如果在不同的频率值上允许卷积核变化,称为LWS。

沿着频率轴的FWS卷积过程可以参考下图理解,图选自相关文献[2]:

CNN在文献[4]中总结的比较全面,[4]和[1],[2]的第一作者都是一个人,因而可以看做他工作的总结。

实验

目前在TIMIT语料上简单跑了几组实验,与很多文献不同的是,我的建模单元采用对齐模型(SAT)的1923个pdf,而不是$61 \times 3 = 183$个。评估方法依旧是标准test集合的PER。主要和传统的DNN比较结果(RNN的结果还没有出来)。模型用pytorch训练,解码使用kaldi内置的latgen-faster-mapped命令,将模型后验转为lattice进行PER的打分计算。其中CNN部分的频轴卷积是按照我自己的理解实现的。实验结果如下:

MODEL PER
DNN(3X1024) + BN 25.3%
DNN(4X512) + BN 24.1%
DNN(4X512) + BN + Dropout 23.8%
DNN(4X1024) + BN + Dropout 23.7%
CNN(K10,P6) + DNN(2X512) 23.1%
CNN(K10,P6) + DNN(2X512) + BN + Dropout 22.7%

从实验记录中可以看出:

  1. BN和Dropout的正面作用。
  2. DNN中深度比宽度更有助于提高识别率。
  3. CNN的声学建模的鲁棒性(参数量最少,但是获得了最低的错误率)。

相关文献

[1]. Applying convolutional neural networks concepts to hybrid NN-HMM model for speech recognition
[2]. Exploring convolutional neural network structures and optimization techniques for speech recognition.
[3]. Deep convolutional neural networks for LVCSR
[4]. Convolutional neural networks for speech recognition