《实战Google深度学习框架》之RNN学习笔记1

  在之前介绍的全连接神经网络或卷积神经网络中,层与层之间是全连接或部分连接的,但每层的节点间是无连接的,这样当输入数据具有依赖性且是序列模式时,预测结果就会不太准确。因此,就有了循环神经网络(RNN)的诞生。RNN的主要用途是处理和预测序列数据。从网络结构上说,RNN会记忆之前的信息,并利用之前的信息影响后面节点的输出。也就是说,循环神经网络的隐藏层之间的节点是有连接的,隐藏层的输入不仅包括输入层的输出,还包括上一时刻隐藏层的输出。
  RNN在每一个时刻会有一个输入Xt,然后根据循环神经网络的当前状态At提供一个输出ht。而循环神经网络当前的状态At是根据上一时刻的状态A(t-1)和当前的输入Xt共同决定的。因此,RNN擅长解决与时间序列相关的问题。对于一个序列数据,可以将这个序列上不同时刻的数据依次传入RNN的输入层,而输出可以对序列中下一个时刻的预测,也可以是对当前时刻信息的处理结果。RNN要求每个时刻都有一个输入,但是不一定每个时刻都要有输出。
在这里插入图片描述
  循环神经网络可以被看作同一神经网络结构在时间序列上被复制多次的结果,这个被复制多次的结构被称之为循环体。如何设置循环体的结构是循环神经网络解决实际问题的关键,循环体网络中的结构在不同时刻也是共享的。下面展示一个简单的循环神经网络前向传播的具体计算过程:
在这里插入图片描述
代码实现:

import numpy as np

#定义不同时刻的输入
X = [1,2]
#定义初始状态
state = [0,0]

#分开定义了不同输入部分的权重以方便操作
w_cell_state = np.asarray([[0.1,0.2], [0.3,0.4]])
w_cell_input = np.asarray([0.5,0.6])
b_cell = np.asarray([0.1,-0.1])

#定义用于输出的全连接层参数
w_output = np.asarray([[1], [2]])
b_output = 0.1

#按照时间顺序执行神经网络的前向传播过程
for i in range(len(X)):
    #计算循环体中的全连接层参数
    before_activation = np.dot(state,w_cell_state) + X[i]*w_cell_input + b_cell
    
    state = np.tanh(before_activation)
    #根据当前时刻状态计算最终输出。
    final_output = np.dot(state,w_output) + b_output
    
    #输出每个时刻的信息
    print('激活函数前: ',before_activation)
    print('当前状态: ',state)    
    print('最后输出: ',final_output)

输出:
激活函数前: [0.6 0.5]
当前状态: [0.53704957 0.46211716]
最后输出: [1.56128388]
激活函数前: [1.2923401 1.39225678]
当前状态: [0.85973818 0.88366641]
最后输出: [2.72707101]
  与其他神经网络类似,在定义完损失函数后,套用以前的优化框架TensorFlow就可以自动完成训练过程。最后需要指出的是,理论上循环神经网络可以支持任意长度的序列,但序列过长会导致优化时出现梯度消失问题,所以实际中会规定一个最大长度,当序列超过规定长度后会对序列进行截断。
  RNN可以更好地利用传统神经网络结构所不能建模的问题,但也带来更大的技术挑战——长期依赖。在有些问题中,模型仅需要短期的信息来解决当前问题,而有时则需要长期的信息。有用信息的间隔有大有小、长短不一,RNN的性能就会受到限制。长短时记忆网络(LSTM)就是为了解决这个问题。LSTM结构是一种特殊的循环体结构,与单一tanh循环体结构不同,LSTM是一种拥有三个“门”结构的特殊网络结构:
在这里插入图片描述
  LSTM靠一些“门”的结构让信息有选择性地影响RNN网络中每个时刻的状态。所谓“门”结构就是一个使用sigmoid神经网络和一个按位做乘法的操作。使用sigmoid作为激活函数的全连接神经网络会输出一个0到1之间的数值。当输出为1时,全部信息都可以通过,当sigmoid层输出为0时,任何结构都无法通过。其中,“遗忘门”的作用是让RNN忘记之前没有用的信息,在忘记之前的部分状态后,通过“输入门”从当前的输入补充最新的记忆。通过这两个“门”,LSTM结构就可以有效的决定哪些信息应该被遗忘,哪些应该被保留。在TensorFlow中,LSTM结构可以被很简单的实现,以下是使用LSTM结构的RNN网络的前向传播过程:

#定义一个LSTM结构,在TensorFlow中通过一句简单的命令就可以实现一个完整的LSTM结构,使用的变量也会在函数中自动被声明。
lstm = rnn_cell.BasicLSTMCell(lstm_hidden_size)

#将LSTM中的状态初始化为全0数组,batch_size给出了一个batch的大小,BasicLSTMCell类提供了zero_state函数来生成全0状态。
state = lstm.zero_state(batch_size, tf.float32)

#定义损失函数
loss = 0.0
#为了避免梯度消散问题,用num_steps规定最大序列长度。
for i in range(num_steps):
    #在第一个时刻声明LSTM结构中使用的变量,在之后的时刻都需要复用之前定义好的变量。
    if i >0: tf.get_variable_scope().reuse_variables()
    #每一步处理时间序列中的一个时刻。将当前输入和前一时刻状态state传入定义的LSTM结构可以得到当前LSTM结构的输出lstm_output
    #和更新后的状态state
    lstm_output, state = lstm(current_input, state)
    #将当前时刻LSTM结构的输出传入一个全连接层得到最后的输出。
    final_output = fully_connected(lstm_output)
    #计算当前时刻输出的损失。
    loss += calc_loss(final_output, excepted_output)
#使用类似第4章中介绍的方法训练模型

  本文仅仅是对RNN的简单介绍,更详细的介绍及数学推导请查阅相关文献。如有疏漏,还望指正。

猜你喜欢

转载自blog.csdn.net/qq_40739970/article/details/86899085
今日推荐