循环神经网络 LSTM RNN 回归:sin 曲线预测
摘要:本篇文章将分享循环神经网络 LSTM RNN 如何实现回归预测。
本文分享自华为云社区《[Python人工智能] 十四.循环神经网络LSTM RNN回归案例之sin曲线预测 丨【百变AI秀】》,作者:eastmount。
一.RNN 和 LSTM 回顾
1.RNN
(1) RNN 原理
循环神经网络英文是 Recurrent NeuralNetworks,简称 RNN。假设有一组数据 data0、data1、data2、data3,使用同一个神经网络预测它们,得到对应的结果。如果数据之间是有关系的,比如做菜下料的前后步骤,英文单词的顺序,如何让数据之间的关联也被神经网络学习呢?这就要用到——RNN。

假设存在 ABCD 数字,需要预测下一个数字 E,会根据前面 ABCD 顺序进行预测,这就称为记忆。预测之前,需要回顾以前的记忆有哪些,再加上这一步新的记忆点,最终输出 output,循环神经网络(RNN)就利用了这样的原理。
首先,让我们想想人类是怎么分析事物之间的关联或顺序的。人类通常记住之前发生的事情,从而帮助我们后续的行为判断,那么是否能让计算机也记住之前发生的事情呢?

在分析 data0 时,我们把分析结果存入记忆 Memory 中,然后当分析 data1 时,神经网络(NN)会产生新的记忆,但此时新的记忆和老的记忆没有关联,如上图所示。在 RNN 中,我们会简单的把老记忆调用过来分析新记忆,如果继续分析更多的数据时,NN 就会把之前的记忆全部累积起来。

RNN 结构如下图所示,按照时间点 t-1、t、t+1,每个时刻有不同的 x,每次计算会考虑上一步的 state 和这一步的 x(t),再输出 y 值。在该数学形式中,每次 RNN 运行完之后都会产生 s(t),当 RNN 要分析 x(t+1)时,此刻的 y(t+1)是由 s(t)和 s(t+1)共同创造的,s(t)可看作上一步的记忆。多个神经网络 NN 的累积就转换成了循环神经网络,其简化图如下图的左边所示。

总之,只要你的数据是有顺序的,就可以使用 RNN,比如人类说话的顺序,电话号码的顺序,图像像素排列的顺序,ABC 字母的顺序等。在前面讲解 CNN 原理时,它可以看做是一个滤波器滑动扫描整幅图像,通过卷积加深神经网络对图像的理解。

而 RNN 也有同样的扫描效果,只不过是增加了时间顺序和记忆功能。RNN 通过隐藏层周期性的连接,从而捕获序列化数据中的动态信息,提升预测结果。

(2) RNN 应用
RNN 常用于自然语言处理、机器翻译、语音识别、图像识别等领域,下面简单分享 RNN 相关应用所对应的结构。
RNN 情感分析: 当分析一个人说话情感是积极的还是消极的,就用如下图所示的 RNN 结构,它有 N 个输入,1 个输出,最后时间点的 Y 值代表最终的输出结果。

RNN 图像识别: 此时有一张图片输入 X,N 张对应的输出。

RNN 机器翻译: 输入和输出分别两个,对应的是中文和英文,如下图所示。

2.LSTM
接下来我们看一个更强大的结构,称为 LSTM。
(1) 为什么要引入 LSTM 呢?
RNN 是在有序的数据上进行学习的,RNN 会像人一样对先前的数据发生记忆,但有时候也会像老爷爷一样忘记先前所说。为了解决 RNN 的这个弊端,提出了 LTSM 技术,它的英文全称是 Longshort-term memory,长短期记忆,也是当下最流行的 RNN 之一。

假设现在有一句话,如下图所示,RNN 判断这句话是红烧排骨,这时需要学习,而“红烧排骨“在句子开头。

"红烧排骨"这个词需要经过长途跋涉才能抵达,要经过一系列得到误差,然后经过反向传递,它在每一步都会乘以一个权重 w 参数。如果乘以的权重是小于 1 的数,比如 0.9,0.9 会不断地乘以误差,最终这个值传递到初始值时,误差就消失了,这称为梯度消失或梯度离散。

反之,如果误差是一个很大的数,比如 1.1,则这个 RNN 得到的值会很大,这称为梯度爆炸。

梯度消失或梯度爆炸:在 RNN 中,如果你的 State 是一个很长的序列,假设反向传递的误差值是一个小于 1 的数,每次反向传递都会乘以这个数,0.9 的 n 次方趋向于 0,1.1 的 n 次方趋向于无穷大,这就会造成梯度消失或梯度爆炸。
这也是 RNN 没有恢复记忆的原因,为了解决 RNN 梯度下降时遇到的梯度消失或梯度爆炸问题,引入了 LSTM。
(2) LSTM
LSTM 是在普通的 RNN 上面做了一些改进,LSTM RNN 多了三个控制器,即输入、输出、忘记控制器。左边多了个条主线,例如电影的主线剧情,而原本的 RNN 体系变成了分线剧情,并且三个控制器都在分线上。

输入控制器(write gate): 在输入 input 时设置一个 gate,gate 的作用是判断要不要写入这个 input 到我们的内存 Memory 中,它相当于一个参数,也是可以被训练的,这个参数就是用来控制要不要记住当下这个点。
输出控制器(read gate): 在输出位置的 gate,判断要不要读取现在的 Memory。
忘记控制器(forget gate): 处理位置的忘记控制器,判断要不要忘记之前的 Memory。
LSTM 工作原理为:如果分线剧情对于最终结果十分重要,输入控制器会将这个分线剧情按重要程度写入主线剧情,再进行分析;如果分线剧情改变了我们之前的想法,那么忘记控制器会将某些主线剧情忘记,然后按比例替换新剧情,所以主线剧情的更新就取决于输入和忘记控制;最后的输出会基于主线剧情和分线剧情。
通过这三个 gate 能够很好地控制我们的 RNN,基于这些控制机制,LSTM 是延缓记忆的良药,从而带来更好的结果。
二.LSTM RNN 回归案例说明
前面我们讲解了 RNN、CNN 的分类问题,这篇文章将分享一个回归问题。在 LSTM RNN 回归案例中,我们想要用蓝色的虚线预测红色的实线,由于 sin 曲线是波浪循环,所以 RNN 会用一段序列来预测另一段序列。

代码基本结构包括:
(1) 生成数据的函数 get_batch()
(2) 主体 LSTM RNN
(3) 三层神经网络,包括 input_layer、cell、output_layer,和之前分类 RNN 的结构一样。

(4) 计算误差函数 computer_cost
(5) 误差 weight 和偏置 biases
(6) 主函数建立 LSTM RNN 模型
(7) TensorBoard 可视化神经网络模型,matplotlib 可视化拟合曲线、
最后再补充下 BPTT,就开始我们的代码编写。
(1) 普通 RNN
假设我们训练含有 1000000 个数据的序列,如果全部训练的话,整个的序列都 feed 进 RNN 中,容易造成梯度消失或爆炸的问题。所以解决的方法就是截断反向传播 (TruncatedBackpropagation,BPTT) ,我们将序列截断来进行训练(num_steps)。
一般截断的反向传播是:在当前时间 t,往前反向传播 num_steps 步即可。如下图,长度为 6 的序列,截断步数是 3,Initial State 和 Final State 在 RNN Cell 中传递。

(2) TensorFlow 版本的 BPTT
但是 Tensorflow 中的实现并不是这样,它是将长度为 6 的序列分为了两部分,每一部分长度为 3,前一部分计算得到的 final state 用于下一部分计算的 initial state。如下图所示,每个 batch 进行单独的截断反向传播。此时的 batch 会保存 Final State,并作为下一个 batch 的初始化 State。

参考:深度学习(07)RNN-循环神经网络-02-Tensorflow中的实现 - 莫失莫忘Lawlite
三.代码实现
第一步,打开 Anaconda,然后选择已经搭建好的“tensorflow”环境,运行 Spyder。

第二步,导入扩展包。

第三步,编写生成数据的函数 get_batch(),它生成了 sin 曲线的序列。

此时的输出结果如下图所示,注意它只是模拟的预期曲线,还不是我们神经网络学习的结构。

第四步,编写 LSTMRNN 类,它用于定义我们的循环神经网络结构,初始化操作和所需变量。
初始化 init()函数的参数包括:
n_steps 表示 batch 中的步骤,共有 3 步。
input_size 表示传入 batch data 时,每个 input 的长度,该实例中 input_size 和 output_size 均为 1。如下图所示,假设我们 batch 长度为一个周期(0-6),每个 input 是线的 x 值,input size 表示每个时间点有多少个值,只有一个点故为 1。
output_size 表示输出的值,输出对应 input 线的 y 值,其大小值为 1。
cell_size 表示 RNN Cell 的个数,其值为 10。
batch_size 表示一次性传给神经网络的 batch 数量,设置为 50。

该部分代码如下,注意 xs 和 ys 的形状。同时,我们需要使用 Tensorboard 可视化 RNN 的结构,所以调用 tf.name_scope()设置各神经层和变量的命名空间名称,详见 第五篇文章。

第五步,接着开始编写三个函数(三层神经网络),它是 RNN 的核心结构。

这三个函数也是增加在 LSTMRNN 的 Class 中,核心代码及详细注释如下所示:

注意,上面调用了 reshape()进行形状更新,为什么要将三维变量改成二维呢?因为只有变成二维变量之后,才能计算 W*X+B。
第六步,定义计算误差函数。
这里需要注意:我们使用了 seq2seq 函数。它求出的 loss 是整个 batch 每一步的 loss,然后把每一步 loss 进行 sum 求和,变成了整个 TensorFlow 的 loss,再除以 batch size 平均,最终得到这个 batch 的总 cost,它是一个 scalar 数字。

后面的文章我们会详细写一篇机器翻译相关的内容,并使用 seq2seq 模型。
Seq2Seq 模型是输出的长度不确定时采用的模型,这种情况一般是在机器翻译的任务中出现,将一句中文翻译成英文,那么这句英文的长度有可能会比中文短,也有可能会比中文长,所以输出的长度就不确定了。如下图所,输入的中文长度为 4,输出的英文长度为 2。

在网络结构中,输入一个中文序列,然后输出它对应的中文翻译,输出的部分的结果预测后面,根据上面的例子,也就是先输出“machine”,将"machine"作为下一次的输入,接着输出"learning",这样就能输出任意长的序列。
机器翻译、人机对话、聊天机器人等等,这些都是应用在当今社会都或多或少的运用到了我们这里所说的 Seq2Seq。
第七步,定义 msr_error 计算函数、误差计算函数和偏置计算函数。

写到这里,整个 Class 就定义完成。
第八步,接下来定义主函数,进行训练和预测操作,这里先尝试 TensorBoard 可视化展现。

四.完整代码及可视化展示
该阶段的完整代码如下,我们先尝试运行下代码:





此时会在 Python 文件目录下新建一个“logs”文件夹和 events 的文件,如下图所示。

接下来尝试打开它。首先调出 Anaconda Prompt,并激活 TensorFlow,接着去到 events 文件的目录,调用命令“tensorboard--logdir=logs 运行即可,如下图所示。注意,这里只需要指引到文件夹,它就会自动索引到你的文件。


此时访问网址“http://localhost:6006/”,选择“Graphs”,运行之后如下图所示,我们的神经网络就出现了。

神经网络结构如下图所示,包括输入层、LSTM 层、输出层、cost 误差计算、train 训练等。

详细结构如下图所示:

通常我们会将 train 部分放置一边,选中“train”然后鼠标右键点击“Remove from maingraph”。核心结构如下,in_hidden 是接受输入的第一层,之后是 LSTM_cell,最后是输出层 out_hidden。

in_hidden: 包括了权重 Weights 和 biases,计算公式 Wx_plus_b。同时,它包括了 reshape 操作,2_2D 和 2_3D。

out_hidden: 包括了权重 weights、偏置 biases、计算公式 Wx_plus_b、二维数据 2_2D,并且输出结果为 cost。

cost: 计算误差。

中间是 LSTM_cell: 包括 RNN 循环神经网络,初始化 initial_state,之后会被 state 更新替换。

注意版本问题,读者可以结合自己的 TensorFlow 版本进行适当修改运行。作者版本版本信息为:Python3.6、Anaconda3、Win10、Tensorflow1.15.0。
如果您报错 AttributeError: module ‘tensorflow._api.v1.nn’ has no attribute ‘seq2seq’,这是 TensorFlow 版本升级,方法调用更改。解决方式:

如果您报错 TypeError:msr_error() got an unexpected keyword argument ‘labels’,msr_error() 函数得到一个意外的关键参数 ‘lables’。其解决方式:定义 msr_error() 函数时,使用 labels,logits 指定,将

改为:

如果您报错 ValueError: Variable in_hidden/weights already exists, disallowed. Did you meanto set reuse=True or reuse=tf.AUTO_REUSE in VarScope? ,则重新启动 kernel 即可运行。

五.预测及曲线拟合
最后,我们在主函数中编写 RNN 训练学习和预测的代码。
首先我们来测试 cost 学习的结果。代码如下,if 判断中 cell_init_state 为前面已初始化的 state,之后更新 state(model.cell_init_state:state ),其实就是将 Final State 换成下一个 batch 的 Initial State,从而符合我们定义的结构。


每隔 20 步输出结果,如下所示,误差从最初的 33 到最后的 0.335,神经网络在不断学习,误差在不断减小。

接下来增加 matplotlib 可视化的 sin 曲线动态拟合过程,最终完整代码如下所示:







写道这里,这篇文章终于写完了。文章非常长,但希望对您有所帮助。LSTM RNN 通过一组数据预测另一组数据。预测效果如下图所示,红色的实线表示需要预测的线,蓝色的虚线表示 RNN 学习的线,它们在不断地逼近,蓝线学到了红线的规律,最终将蓝线基本拟合到红线上。

六.总结
本文介绍完了,更多 TensorFlow 深度学习文章会继续分享,接下来我们会分享监督学习、GAN、机器翻译、文本识别、图像识别、语音识别等内容。如果读者有什么想学习的,也可以私聊我,我去学习并应用到你的领域。
最后,希望这篇基础性文章对您有所帮助,如果文章中存在错误或不足之处,还请海涵~作为人工智能的菜鸟,我希望自己能不断进步并深入,后续将它应用于图像识别、网络安全、对抗样本等领域,指导大家撰写简单的学术论文,一起加油!
代码下载地址(欢迎大家关注点赞):
https://github.com/eastmountyxz/AI-for-TensorFlow
https://github.com/eastmountyxz/AI-for-Keras
版权声明: 本文为 InfoQ 作者【华为云开发者社区】的原创文章。
原文链接:【http://xie.infoq.cn/article/4c0cafab990f4e44ae3003d75】。文章转载请联系作者。
评论