Deep RNN in Acoustic Model

话说图像分类那边喜欢把网络往深处设计,语音这边也跟风走势,DeepRNN和DeepCNN接连出现在各种实验论文中。不过在网络成功走向深处之前,它的难点也是疑问在于,不加修正的加深网络层数,并不能获得相应的结果提升。于是各方大佬就进行了各种尝试和改良。截止目前,成功尝试包括以下三种:

  • Highway Network
  • Residual Network,可以看做HN的特殊情况,ResNet在图像分类上用的很成功
  • Recurrent Highway Network,中了ICML2017

语音这边也基本是拿大佬们成功的实践往声学模型上尝试,也得到了一些识别率上的提升。

再谈RNN

这里主要以pytorch为参考,说明一下RNN的输入输出关系以及内在设计思路,从而加深对RNN的理解。对于BaseRNN,GRU,LSTM,我们可以用一个笼统的式子表示其内在变换:

$s$表示state,即RNN内在的状态值,这个状态的解释如下:

  • 对于BaseRNN和GRU而言,状态表示RNN之前某时刻的输出,即$h_t$,原式可写为:$y, h_T = \mathcal{H}(x, h_0)$
  • 对于LSTM而言,由于cell存储了主要信息,状态表示之前某时刻下的输出和cell值,原式可写为:$y, h_T, c_T = \mathcal{H}(x, h_0, c_0)$

$x, y$分别表示$t$时刻的输入和对应的输出。$\mathcal{H}$用来描述不同RNN对输入和状态的一系列操作。如果是LSTM的话,首先计算$i, f, g, o$各个门的值,之后更新cell值$c_t$,给出输出值$h_t$;GRU则计算$r, z$以及候选$\hat{h}_t$,给出$h_t$。

pytorch里面,考虑到批训练准则(min-batch SGD),输入$x$的维度被设计为$[T, N, I]$,分别表示time_steps,batch_size, input_size,其中time_steps的存在由RNN的BPTT训练算法决定。与此对应的输出维度为$[T, N, O]$。$s_t$则记录下了每一个batch对应的每一层的状态值。如果不做sequence训练的话,那么一般是不需要使用$T$时刻的状态值$s_T$的,只需要拿到$y$将其返回给后续网络做输入即可。但是在类似CharRNN的任务中,仍旧需要将$T$时刻的状态传递下去,作为初始的$s_0$输入,这时候状态这个量就起到相应的作用了。

以上这个过程是一个高度封装的结果,因为它既包含了时间轴上的展开,也包含了层间的传递,深入一点,即如何做时间展开(定义在Recurrent中),层与层之间如何堆叠传递(定义在StackedRNN中,实际上是在每一层中依次做的时间展开),可以参考pytorch的源码,RNN这部分代码主要分布在如下几个地方:

  • pytorch/torch/nn/_functions/thnn/rnn.py:实际定义了RNN的各种cell和层次封装
  • pytorch/torch/nn/_functions/backends:1和3之间的grue
  • pytorch/torch/nn/_functions/modules/rnn.py:最外层的封装,核心调用backends.RNN

Deep RNN

定义$h_l^t$表示DeepRNN中第$l$层$t$时刻的输出,$s_l^t$为第$l$层$t$时刻的状态,那么依据StackRNN的前一层的输入作为后层输入的关系,有:

从上式可以看出,$l - 1$层的输入信息是无法直接到达下一层的。网络走向Deep的方案之一就是通过创建一条旁路(skip connection),允许输入通过,直接汇入输出中,这就是所谓的Highway Network(高速路网络,HWN)和Residual Network(残差网络, ResNet)的设计思路。通用的表示如下:

HWN的形式更加通用一些,它允许网络动态的学习,权衡两者的权值$w_i, w_o$,即用一层仿射变换定义权值:

也可以减少一部分学习参数,将$C$用$1 - T$来表示。ResNet可以看做HWN的一种特殊形式,即$C = T = 1$:

强制网络的输出学习$\mathcal{F} - h_l^t$,其中$\mathcal{F}$表示我们期望的结果。Interspeech2017上谷歌有一篇文章(相关文献[5])对比了两种跳转方案的优劣,发现HWN的学习能力强大一些。

LSTM的情况可能要特殊一点,因为除了和RNN&GRU共有的$h$之外,自己还有一个细胞状态$c$。我个人觉得如果按照上面的框架只考虑输出状态也是可行的,毕竟LSTM的输出也是读取了cell的结果。HW-LSTM和Res-LSTM的设计和应用可以参见相关文献[4],[6]。

在[4]中,LSTM的cell之间被加入了一个称为gated connection的东西$d$,将其用于修正LSTM中$c$的计算逻辑,即联通相邻层的cell。标准的LSTM,$c_t$计算逻辑如下:

其中

上面的公式加上上标$l$是为了和HW-LSTM做对比。标准的LSTM仅仅允许$l$层的输出作为下一层的输入,不允许层间cell的信息流动。HW-LSTM添加了这种流动路径,将其修改为:

从上面的公式可以看出,$d_t^l$决定了下一层cell信息流动到当前层的量,值有下式给出:

文章中的示意图如下,红色区域即为添加的用来允许cell之间信息流动的connection:

HW-LSTM

[6]中所谓Res-LSTM的设计就没有考虑cell之间的信息流动,仅仅在输出门上加了shortcut路径。不考虑Project层的话,输出$h_t$由$o_t \odot \tanh(c_t)$变为$h_t = o_t \odot (\tanh(c_t) + x_t)$。如果考虑Project层,那么考虑到project之后的维度和LSTM输入的维度未必保持一致,因此需要给$x_t$乘一个变换矩阵$\mathbf{W}_H$,即:

以上主要是通过HWN和ResNet使得RNN变得Deeper,ICML2017上出现了一种新的结构,循环高速网络,针对RNN提出的加深方案。

Recurrent Highway Networks(简称RHN吧)中定义了一种新层:RHN层,并将该层的深度称为Recurrent Depth(简称RD)。对于RD为$L$的RHN层而言,它由一层RNN层和$L - 1$层HW层构成,若$L = 1$,那么所谓的RHN就和传统的RNN一致了,论文中配的单层RHN示意图如下:

RHN-structure

图中的$C,T,H$均表示激活变换(先进行仿射变换,再输入激活函数),输入有箭头的指向决定。可以看出,实际只有第一层输入了上一时刻的历史信息,但是每一层都有HW的逻辑在里面,最后一层的输出作为下一时刻第一层的输入。可以这么理解,传统的RNN保留当前层的输出作为自己的状态值,RHN允许RNN层的输入继续传递若干个HW层的输出作为状态值保留。

对于$l(l > 1)$层而言,输出$s_l^t$可以表达为:

$l = 1$时:

以上所说只是一层RHN层,只是这一层里面有着$L$层的HW层。增大网络深度可以从增大RD和增加RHN层层数两个角度入手,在相关文献[5]中发现,堆叠RHN层比增大RD在近似参数量下更加有效(堆叠RHN引入了更多的Recurrent成分)。

相关文献

[1]. Deep residual learning for image recognition
[2]. Highway Networks
[3]. Recurrent highway networks
[4]. Highway long short-term memory RNNS for distant speech recognition
[5]. Highway-LSTM and Recurrent Highway Networks for Speech Recognition
[6]. Residual LSTM: Design of a Deep Recurrent Architecture for Distant Speech Recognition