分享

从 RNN, LSTM, GRU 到 SRU

 DISTANCE_A 2017-10-23




1 引言


RNN(Recurrent Neural Networks) 一般翻译为“递归神经网络” 或 “循环神经网络”,这里面涉及到初学编程中经常使用的两个技巧:递归/循环。递归的典型例子就是斐波那契数列:


int Fibonacci(int x)

{

  if(x >= 3)

  {

    return Fibonacci(x - 1) + Fibonacci(x - 2);

  }

  else

  {

    return 1;

  }

}

如果需要计算 Fibonacci(n), n > 3 则必须先计算 Finonacci(n-1) 和 Fibonacci(n-2),依次类推,直到 Fibonacci(1), Fibonacci(2), ..., Fibonacci(n-2), Fibonacci(n-1) 全部计算完成,才能得到 Fibonacci(n)。


利用循环同样可以计算斐波那契数列:


const int L = 30;

int Fib[L] = {1, 1, 0};

for(int i = 2; i < l;="">

{

  Fib[i] = Fib[i-1] + Fib[i-2];

}


两种实现是等价的。


2 序列学习


上面的斐波那契数列就是一种序列(Sequence),语音信号也是一种序列,你每天手机上推送的新闻、公众号文章,互联网上的博客、视频,客厅观看的电视,收听的广播、音乐,阅读的书,银行打印的流水账……都是序列。

序列中的元素是存在相关性的,比如你阅读公众号文章,如果只看文章中一两个字,获取信息是极其有限的,只有全部读完才觉得“这篇文章很赞”或“上篇文章很水”。


前馈网络(DNN、CNN)只能接收固定维度的输入,处理序列数据时需要“截断”为若干组,各组在处理时互相独立,这显然不适合分析变长的序列数据,如语音识别时单从某个片段无法区分同一读音的两个词(不行/步行),必须联系语境上下文。RNN 则在网络结构中加入反馈机制,将前一时刻输出重新作为当前时刻输入,这也是 RNN 中 Recurrent 名字来源。RNN 结构如下图所示:


我们可以把 st 看作网络的记忆单元,它可以捕获之前时刻的信息。RNN 是面向序列学习(Sequence Learning)的一款强大工具。


序列学习模型可以干嘛?


  • 语言建模(language modeling)

输入一组单词,利用这些单词预测下一个单词。训练好的语言模型可以产生新文本。基于莎士比亚作品训练的语言模型可以产生类似莎士比亚作品的文本。


  • 机器翻译

输入为某种语言的文本,输出为另一种语言文本。德语到英语的机器翻译系统如下图所示:

  • 语音识别

输入为声波中提取的语音信号,输出一组音节片段以及相应概率密度。


  • 图像描述

RNN 同卷积神经网络结合,可以生成图像描述,输入为一张图片,输出为描述该图片的自然语言描述语句。我们前面文章《利用 TensorFlow 实现“看图说话”》介绍过该类型应用。


思考:能否用 RNN 学习斐波那契数列的规律?


3 RNN 结构改进


训练 RNN 和训练传统神经网络类似,同样使用反向传播算法,但由于网络参数被所有时刻共享,每个时刻输出计算的梯度不仅依赖当前时刻,也依赖之前时刻。例如,为了计算 t=4 时刻梯度,我们应当反向传播 3 次再将梯度累加。这种方法称为沿时间反向传播(BPTT,Backpropagation Through Time)。实际上用 BPTT 训练 RNN 会出现著名的“梯度爆炸/消失”问题。为了解决该问题,RNN 逐步发展了多个变种。其中应用最广泛的当属 LSTM(Long Short Term Memories),结构如下:


看上去有些杂乱,最核心的实际上就是中央的细胞(C),而三个标记 σ 的圆圈表示“三重门”:输入门 i,输出门 o,遗忘门 f,均使用 Sigmoid 函数将取值范围限制到 (0, 1) 之内。计算公式如下:


注意到 LSTM 结构图中蓝色箭头其实代替了 RNN 中的反馈连接。使用三个门可以控制不同时刻信息流动方向,通过控制遗忘门和输入门,选择合适的信息进入细胞,将无关信息拒之门外;通过控制输出门,选择最合适的时刻输出细胞处理后的信息。


从公式可以看出 LSTM 计算较为复杂,参数也非常多,难以训练。GRU(Gated Recurrent Units)应运而生。

在 GRU 中,大幅简化了 LSTM 结构:

  • 新增 reset gate,即图中的 r 开关;

  • 将输入门和遗忘门合并为“update gate”,即图中的 z 开关;

  • 将细胞状态 C 和隐藏状态 m 合并为 h;

  • 省掉了输出门;

计算公式如下:

通过以上改进,GRU 计算相比 LSTM 得到简化,在一些应用中也得到验证【2】,但在 GPU 上实现时,效率不高,主要是由于前后两个时刻 t-1 和 t 的计算存在依赖性,只有当  ready 时,才能计算 t 时刻的其他值等,这样无法完全发挥出 GPU 上硬件并行计算的优势。

 

最近论文【3】 SRU(Simple Recurrent Unit)则提出更激进的架构,去掉了前后时刻计算的依赖。

公式如下:

这样可以将 t = 0, 1, ..., n 时刻的计算并行,从而获得更高的加速比。

伪代码如下:

不同实现的耗时对比:

SRU 相比 cuDNN LSTM 训练提速超过 10 倍!

原始论文中只提供了 PyTorch 和 CNTK 上的 SRU 实现,github 上有开源的基于 Keras 的 SRU 实现【4】,有条件的读者可以利用它基于 TensorFlow Backend 做做试验,看看能否达到论文中的速度。


4 补充信息

RNN 家族历史悠久,使用广泛,目前仍面临很多挑战。

除了上述细胞结构的改进,网络结构有如下扩展:


  • 双向 RNN/LSTM/GRU/SRU

如果 t 时刻输出不仅取决于之前时刻的序列输入,还取决于将来时刻序列输入,那么双向 RNN 能更好地建模。典型例子:英语考试中的完形填空。

  • 深度(双向)RNN/LSTM/GRU/SRU

将单层 RNN/LSTM/GRU/SRU 堆叠,增加了网络容量,同时需要大量训练数据。


5 参考

【1】 www.wildml.com/2015/09/recurrent-neural-networks-tutorial-part-1-introduction-to-rnns/

【2】Empirical Evaluation of Gated Recurrent Neural Networks on Sequence Modeling, arXiv:1412.3555v1

【3】Training RNNs as Fast as CNNs, https:///pdf/1709.02755.pdf

【4】 https://github.com/titu1994/keras-SRU




    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多