(挺清楚)深度学习之RNN(循环神经网络)

冷不防 2022-09-10 10:26 356阅读 0赞

原文链接:https://blog.csdn.net/qq_32241189/article/details/80461635

一 RNN概述

  1. 前面我们叙述了BP算法, CNN算法, 那么为什么还会有RNN呢?? 什么是RNN, 它到底有什么不同之处? RNN的主要应用领域有哪些呢?这些都是要讨论的问题.
  2. 1) BP算法,CNN之后, 为什么还有RNN?
  3. 细想BP算法,CNN(卷积神经网络)我们会发现, 他们的输出都是只考虑前一个输入的影响而不考虑其它时刻输入的影响, 比如简单的猫,狗,手写数字等单个物体的识别具有较好的效果. 但是, 对于一些与时间先后有关的, 比如视频的下一时刻的预测,文档前后文内容的预测等, 这些算法的表现就不尽如人意了.因此, RNN就应运而生了.
  4. 2) 什么是RNN?
  5. RNN是一种特殊的神经网络结构, 它是根据"人的认知是基于过往的经验和记忆"这一观点提出的. 它与DNN,CNN不同的是: 它不仅考虑前一时刻的输入,而且赋予了网络对前面的内容的一种**'记忆'功能**.
  6. RNN之所以称为**循环神经网路**,即一个序列当前的输出与前面的输出也有关。具体的表现形式为网络会对前面的信息进行记忆并应用于当前输出的计算中,即隐藏层之间的节点不再无连接而是有连接的,并且隐藏层的输入不仅包括输入层的输出还包括上一时刻隐藏层的输出。
  7. 3) RNN的主要**应用领域**有哪些呢?
  8. RNN的应用领域有很多, 可以说只要考虑时间先后顺序的问题都可以使用RNN来解决.这里主要说一下几个常见的应用领域:
  9. **自然语言处理(NLP)**: 主要有**视频处理**, **文本生成***,* **语言模型***, 图像处理*
  10. **机器翻译**, 机器写小说
  11. **语音识别**
  12. **图像描述生成**
  13. 文本相似度计算
  14. **音乐推荐**、**网易考拉商品推荐**、**Youtube视频推荐**等新的应用领域.

二 RNN(循环神经网络)

1) RNN模型结构

  1. 前面我们说了RNN具有时间"记忆"的功能, 那么它是怎么实现所谓的"记忆"的呢?

2018052810253376

  1. 1 RNN结构图
  2. 如图1所示, 我们可以看到RNN层级结构较之于CNN来说比较简单, 它主要有**输入层**,**Hidden Layer**, **输出层**组成.

并且会发现在Hidden Layer 有一个箭头表示数据的循环更新, 这个就是实现时间记忆功能的方法.

  1. 如果到这里你还是没有搞懂RNN到底是什么意思,那么请继续往下看!

70

  1. 2 Hidden Layer的层级展开图
  2. 如图2所示为Hidden Layer的层级展开图. t-1, t, t+1表示时间序列. X表示输入的样本. St表示样本在时间t处的的记忆,**St = f(W\*St-1 +U\*Xt)**. W表示输入的权重, U表示此刻输入的样本的权重, V表示输出的样本权重.
  3. t =1时刻, 一般初始化输入S0=0, 随机初始化W,U,V, 进行下面的公式计算:
  4. ![70 1][]
  5. 其中,fg均为激活函数. 其中f可以是tanh,relu,sigmoid等激活函数,g通常是softmax也可以是其他。
  6. 时间就向前推进,此时的状态s1作为时刻1的记忆状态将参与下一个时刻的预测活动,也就是:
  7. ![70 2][]
  8. 以此类推, 可以得到最终的输出值为:
  9. ![70 3][]
  10. **注意**: 1. 这里的**W,U,V**在每个时刻都是相等的(**权重共享**).
  11. 2. 隐藏状态可以理解为: S=f(现有的输入+过去记忆总结)

2) RNN的反向传播

  1. 前面我们介绍了RNN的前向传播的方式, 那么RNN的权重参数W,U,V都是怎么更新的呢?
  2. 每一次的输出值Ot都会产生一个误差值Et, 则总的误差可以表示为:![70 4][].
  3. 则**损失函数**可以使用**交叉熵损失函数**也可以使用**平方误差损失函数**.
  4. 由于每一步的输出不仅仅依赖当前步的网络,并且还需要前若干步网络的状态,那么这种BP改版的算法叫做Backpropagation Through Time(**BPTT**) , 也就是将输出端的误差值反向传递,运用**梯度下降法**进行更新.([不熟悉BP的可以参考这里][BP])
  5. 也就是要求参数的梯度:
  6. ![70 5][]
  7. 首先我们求解**W的更新方法**, 由前面的W的更新可以看出它是每个时刻的偏差的偏导数之和.
  8. 在这里我们以 t = 3时刻为例, 根据链式求导法则可以得到t = 3时刻的偏导数为:
  9. ![70 6][]
  10. 此时, 根据公式![70 7][]我们会发现, S3除了和W有关之外, 还和前一时刻S2有关.
  11. 对于S3直接展开得到下面的式子:
  12. ![70 8][]
  13. 对于S2直接展开得到下面的式子:
  14. ![70 9][]
  15. 对于S1直接展开得到下面的式子:
  16. ![70 10][]
  17. 将上述三个式子合并得到:
  18. ![70 11][]
  19. 这样就得到了公式:
  20. ![70 12][]
  21. 这里要说明的是:![70 13][]表示的是S3W直接求导, 不考虑S2的影响.(也就是例如y = f(x)\*g(x)对x求导一样)
  22. 其次是对**U的更新方法**. 由于参数U求解和W求解类似,这里就不在赘述了,最终得到的具体的公式如下:
  23. ![70 14][]![70 15][]
  24. 最后,给出**V的更新公式**(V只和输出O有关):
  25. ![70 16][]

三 RNN的一些改进算法

  1. 前面我们介绍了RNN的算法, 它处理时间序列的问题的效果很好, 但是仍然存在着一些问题, 其中较为严重的是容易出现**梯度消失**或者**梯度爆炸**的问题(***BP算法**和**长时间依赖****造成的*). 注意: 这里的梯度消失和BP的不一样,这里主要指由于时间过长而造成记忆值较小的现象.
  2. 因此, 就出现了一系列的改进的算法, 这里介绍主要的两种算法: **LSTM **和 **GRU**.
  3. LSTM GRU对于梯度消失或者梯度爆炸的问题处理方法主要是:
  4. 对于**梯度消失**: 由于它们都有特殊的方式存储”记忆”,那么以前梯度比较大的”记忆”不会像简单的RNN一样马上被抹除,因此可以一定程度上克服梯度消失问题。
  5. 对于**梯度爆炸**:用来克服梯度爆炸的问题就是**gradient clipping**,也就是当你计算的梯度超过阈值c或者小于阈值-c的时候,便把此时的梯度设置成c或-c

1) LSTM算法(Long Short Term Memory, 长短期记忆网络 ) —- 重要的目前使用最多的时间序列算法

  1. ![70 17][]
  2. 3 LSTM算法结构图
  3. 如图3LSTM算法的结构图.
  4. RNN不同的是: RNN中![70 18][],就是个简单的线性求和的过程. LSTM可以通过“**门**”结构来去除或者增加“**细胞状态**”的信息,实现了对重要内容的保留和对不重要内容的去除. 通过**Sigmoid**层输出一个01之间的概率值,描述每个部分有多少量可以通过,0表示“不允许任务变量通过”,1表示“运行所有变量通过 ”.
  5. 用于遗忘的门叫做"**遗忘门**", 用于信息增加的叫做"**信息增加门**",最后是用于输出的"**输出门**". 这里就不展开介绍了.
  6. 此外,LSTM算法的还有一些变种.
  7. 如图4所示, 它增加“**peephole connections**”层 , 让门层也接受细胞状态的输入.
  8. ![70 19][]
  9. 4 LSTM算法的一个变种
  10. 如图5所示为LSTM的另外一种变种算法.它是通过耦合忘记门和更新输入门(第一个和第二个门);也就是不再单独的考虑忘记什么、增加什么信息,而是一起进行考虑。
  11. ![70 20][]
  12. 5 LSTM算法的一个变种

2) GRU算法

  1. GRU2014年提出的一种LSTM改进算法. **它将忘记门和输入门合并成为一个单一的更新门, 同时合并了数据单元状态和隐藏状态, 使得模型结构比之于LSTM更为简单.**
  2. ![70 21][]
  3. 其各个部分满足关系式如下:
  4. ![70 22][]

四 基于Tensorflow的基本操作和总结

  1. 使用tensorflow的基本操作如下:
  2. # _*_coding:utf-8_*_
  3. import tensorflow as tf
  4. import numpy as np
  5. '''
  6. TensorFlow中的RNN的API主要包括以下两个路径:
  7. 1) tf.nn.rnn_cell(主要定义RNN的几种常见的cell)
  8. 2) tf.nn(RNN中的辅助操作)
  9. '''
  10. # 一 RNN中的cell
  11. # 基类(最顶级的父类): tf.nn.rnn_cell.RNNCell()
  12. # 最基础的RNN的实现: tf.nn.rnn_cell.BasicRNNCell()
  13. # 简单的LSTM cell实现: tf.nn.rnn_cell.BasicLSTMCell()
  14. # 最常用的LSTM实现: tf.nn.rnn_cell.LSTMCell()
  15. # RGU cell实现: tf.nn.rnn_cell.GRUCell()
  16. # 多层RNN结构网络的实现: tf.nn.rnn_cell.MultiRNNCell()
  17. # 创建cell
  18. # cell = tf.nn.rnn_cell.BasicRNNCell(num_units=128)
  19. # print(cell.state_size)
  20. # print(cell.output_size)
  21. # shape=[4, 64]表示每次输入4个样本, 每个样本有64个特征
  22. # inputs = tf.placeholder(dtype=tf.float32, shape=[4, 64])
  23. # 给定RNN的初始状态
  24. # s0 = cell.zero_state(4, tf.float32)
  25. # print(s0.get_shape())
  26. # 对于t=1时刻传入输入和state0,获取结果值
  27. # output, s1 = cell.call(inputs, s0)
  28. # print(output.get_shape())
  29. # print(s1.get_shape())
  30. # 定义LSTM cell
  31. lstm_cell = tf.nn.rnn_cell.LSTMCell(num_units= 128)
  32. # shape=[4, 64]表示每次输入4个样本, 每个样本有64个特征
  33. inputs = tf.placeholder(tf.float32, shape=[ 4, 48])
  34. # 给定初始状态
  35. s0 = lstm_cell.zero_state( 4, tf.float32)
  36. # 对于t=1时刻传入输入和state0,获取结果值
  37. output, s1 = lstm_cell.call(inputs, s0)
  38. print(output.get_shape())
  39. print(s1.h.get_shape())
  40. print(s1.c.get_shape())
  41. 当然, 你可能会发现使用cell.call()每次只能调用一个得到一个状态, 如有多个状态需要多次重复调用较为麻烦, 那么我们怎么解决的呢? 可以参照后面的基于RNN的**手写数字识别**和**单词预测**的实例查找解决方法.
  42. 本文主要介绍了一种时间序列的RNN神经网络及其基础上衍生出来的变种算法LSTMGRU算法, 也对RNN算法的使用场景作了介绍.
  43. 当然, 由于篇幅限制, 这里对于双向RNNs和多层的RNNs没有介绍. 另外, 对于LSTM的参数更新算法在这里也没有介绍, 后续补上吧!
  44. *最后, 如果你发现了任何问题, 欢迎一起探讨, 共同进步!!*

发表评论

表情:
评论列表 (有 0 条评论,356人围观)

还没有评论,来说两句吧...

相关阅读

    相关 循环神经网络RNN

    对于人类而言,以前见过的事物会在脑海中留下记忆,虽然随后记忆会慢慢消失,但是每当经过提醒,人们往往可以重拾记忆。在神经网络中也是一样,之前介绍的CNN模型都是与时间序列无关的模