1.RNN
-
原因很简单,无论是卷积神经网络,还是人工神经网络,他们的前提假设都是:元素之间是相互独立的,输入与输出也是独立的,比如猫和狗。
-
CNN是专门处理网格化数据(例如图像),而RNN是专门处理序列的神经网络。
-
但现实世界中,很多元素都是相互连接的,比如股票随时间的变化,
-
一个人说了:我喜欢旅游,其中最喜欢的地方是云南,以后有机会一定要去()
-
这里填空,人应该都知道是填“云南“。因为我们是根据上下文的内容推断出来的,但机会要做到这一步就相当得难了。因此,就有了现在的循环神经网络,他的本质是**:像人一样拥有记忆的能力。**因此,他的输出就依赖于当前的输入和记忆
- 其中每个圆圈可以看作是一个单元,而且每个单元做的事情也是一样的,因此可以折叠呈左半图的样子。用一句话解释RNN,就是一个单元结构重复使用。
1.1 单个RNN单元
- 循环神经网络可以看作是单元的重复,首先要实现单个时间步的计算,下图描述了RNN单元的单个时间步的操作。
Xt:表示t时刻的输入,y^t:表示t时刻的输出,at:表示t时刻的记忆
-
每个时间步隐藏层的计算:
-
输入分为两个部分,一个是当前时间步的输入Xt 和上一个时间步的输出at-1.
-
将两个输入部分分别乘以各自的权重Wax和Waa然后加上偏置b,再进行激活函数tanh的计算得到当前时间步的隐藏层输出at
-
再将输出 at乘以权重Wya ,加上偏置by,送入softmax函数,得到当前时间步的预测值y
1.2 多个RNN单元的前向传播
模型的结构如下:
- 初始化参数
- 循环:
- 前向传播计算损失
- 反向传播计算关于损失的梯度
- 修剪梯度以免梯度爆炸
- 用梯度下降更新规则更新参数。
- 返回学习后了的参数
1.3 RNN的反向传播(单个RNN单元)
1.4 RNN存在的问题1:无法运用未来的信息
1.4.1 RNN的改进:双向RNN
- 假设当前t的输出不仅仅和之前的序列有关,并且 还与之后的序列有关
1.5 RNN存在的问题2:梯度爆炸和梯度消失
-
梯度消失:反向传播时,当梯度从后往前传时,梯度不断减小,最后变为零,此时,浅层的神经网络权重得不到更新,这种现象称为“饱和”,整个网络学习速度非常慢。梯度消失导致后层的权重更新的快,而前层网络由于梯度传递不过去而得不到更新。这样在网络很深的时候,学习的速度很慢或者无法学习。
-
梯度爆炸:这是一种与梯度消失相反的情况,当进行反向传播时,梯度从后往前传时,梯度不断增大,导致权重更新太大,以致不断波动,使网络在最优点之间波动。
-
**原因:**前面层上的梯度是来自于后面层上梯度的乘乘积。当存在过多的层次时,就出现了内在本质上的不稳定场景,如梯度消失和梯度爆炸。
-
解决方案:
- 方案1:梯度剪切:这个方案主要是针对梯度爆炸提出的,其思想是设置一个梯度剪切阈值,然后更新梯度的时候,如果梯度超过这个阈值,那么就将其强制限制在这个范围之内。这可以防止梯度爆炸。
- 方案2-relu、leakrelu、elu等激活函数:思想也很简单,如果激活函数的导数为1,那么就不存在梯度消失爆炸的问题了,每层的网络都可以得到相同的更新速度,relu就这样应运而生。
- 方案3:LSTM
2 RNN的变体
2.1 LSTM (Long Short-Term Memory)长短时记忆网络
- ct :代表哪些信息应该被遗忘以及哪些信息应该被记住
- at: 代表这个lstm单元最终的输出
- 遗忘门(forget gate),所谓的“遗忘”,也就是“记忆的残缺”。它决定了上一时刻的单元状态有多少“记忆”可以保留到当前时刻;
- 输入门(input gate),它决定了当前时刻的输入有多少保存到单元状态。
- 候选门(Candidate gate),它控制着以多大比例融合“历史”信息和“当下”刺激。
2.2 BI-LSTM
2.3 GRU(Gated Recurrent Unit)
-
GRU中将LSTM中的决定哪些遗忘哪些记住的ct 以及 最终输出的at 合并成了一个 ht
-
**更新门zt:**将遗忘门 输入门 合并成了一个更新门。更新门用于控制前一时刻的状态信息被带入到当前状态中的程度,更新门的值越大说明前一时刻的状态信息带入越多。
-
重置门rt:重置门用于控制忽略前一时刻的状态信息的程度,重置门的值越小说明忽略得越多。
3 TensorFlow中的RNN
3.1 RNN单元:
3.1.1 RNN 基类
定义RNN单元和调用Call()
-
class tf.contrib.rnn.BasicRNNCell(num_units, activation=None, reuse=None, name=None)
输入参数
- num_units: int, the number of units in the RNN cell.
- activation: Nonlinearity to use. Default: tanh.
- reuse: (optional) Python boolean describing whether to reuse variables in an existing scope. If not True, and the existing scope already has the given variables, an error is raised.
- name: String, the name of the layer. Layers with the same name will share weights, but to avoid mistakes we require reuse=True in such cases.
输出:
- 一个隐层神经元数量为 num_units 的 RNN 基本单元(实例化的 cell)
常用属性:
函数返回属性:
- state:size(s) of state(s) used by this cell,等于隐层神经元数量
- output: size of outputs produced by this cell
注意: 在此函数中,state_size 永远等于 output_size - 比如我们通常是将一个batch送入模型计算,设输入数据的形状为(batch_size, input_size),那么计算时得到的隐层状态就是(batch_size, state_size),输出就是(batch_size, output_size)。
常用方法:
-
call(inputs, state): (output, next_state) = Cell.call(input, state)。
返回两个一模一样的隐层状态值.每调用一次RNNCell的call方法,就相当于在时间上“推进了一步”,这就是RNNCell的基本功能。在新版本中改成了直接调用:(Cell.(input,state))
-
zero_state(batch_size, dtype): 返回一个形状为 [batch_size, state_size] 的全零tensor.一般用来定义RNN单元的初始状态。
-
代码示例
-
import tensorflow as tf import numpy as np #state_size = 128 #cell =tf.nn.rnn_cell.BasicRNNCell(num_units=128) cell =tf.contrib.rnn.BasicRNNCell(num_units = 128) #128 print(cell.state_size) #batch_size 32 inputs = tf.placeholder(np.float32,shape=(32,128)) #通过zero_state 得到一个全0的初始状态 h0 = cell.zero_state(32,np.float32) output , h1 = cell(inputs,h0) print("h1.shape: " ,h1.shape)#32 128 print("output.shape: ",output.shape)
3.1.2 LSTM基类
class tf.contrib.rnn.BasicLSTMCell(num_units, forget_bias=1.0, state_is_tuple=True, activation=None, reuse=None, name=None)
输入参数
- num_units: int, the number of units in the RNN cell.
- forget_bias: float, Biases of the forget gate are initialized by default to 1 in order to reduce the scale of forgetting at the beginning of the training.
- state_is_tuple: If True, accepted and returned states are 2-tuples of the c_state and m_state
- activation: Nonlinearity to use. Default: tanh.
- reuse: (optional) Python boolean describing whether to reuse variables in an existing scope. If not True, and the existing scope already has the given variables, an error is raised.
- name: String, the name of the layer. Layers with the same name will share weights, but to avoid mistakes we require reuse=True in such cases.
输出:
- 一个隐层神经元数量为 num_units 的 LSTM 基本单元(实例化的 lstm_cell)
- state_size:size(s) of state(s) used by this cell,等于隐层神经元数量
- output_size: size of outputs produced by this cell.
注意:
- 在此函数中,state_size 永远等于 output_size
常用方法:
- call(inputs, state): 返回一个是 new_h,一个是 new_state(LSTMStateTuple:包含 ct 和 ht)
ct 和 ht 对应我们上面的LSTM示意图中的ct 和at(ct代表哪些信息应该被记住哪些应该被遗忘,ht代表这个cell最终的输出)
-
BasicLSTMCell 的 call 函数定义
-
返回的隐状态是
new_c
和new_h
的组合,而 output 就是单独的new_h
-
如果我们处理的是分类问题,那么我们还需要对
new_h
添加单独的Softmax 层
才能得到最后的分类概率输出
代码示例
import tensorflow as tf
lstm_cell = tf.contrib.rnn.BasicLSTMCell(num_units=128, forget_bias=1.0, state_is_tuple=True, activation=None, reuse=None, name=None)
print(lstm_cell.output_size) # 128
print(lstm_cell.state_size) # LSTMStateTuple(c=128, h=128)
inputs = tf.placeholder(shape=[32, 100], dtype=tf.float32) # 32 是 batch_size
h0 = lstm_cell.zero_state(batch_size=32, dtype=tf.float32)
print(h0)
# LSTMStateTuple(c=<tf.Tensor 'BasicLSTMCellZeroState/zeros:0' shape=(32, 128) dt