您的位置:首页 > 理论基础 > 计算机网络

深度学习_循环神经网络RNN与LSTM

2017-11-06 22:52 1066 查看

1. 循环神经网络RNN

1) 什么是RNN?

循环神经网络(RNN)是一种节点定向连接成环的人工神经网络。具体应用有语音识别,手写识别,翻译等.

2) 什么时候使用RNN?

FNN(前馈神经网络,如BP,CNN等)效果已经不错了,RNN还需要更大量的计算,为什么要用RNN呢?如果训练N次,每次和每次都没什么关系,那就不需要RNN,但如果每个后一次都可能和前一次训练相关,比如说翻译:一个句子里面N个词,一个词为一次训练(train instance),一个词的意思很可能依赖它的上下文,也就是其前次或后次训练,这个时候就需要RNN.

3) RNN与FNN有何不同?



如图所示,左边的是前馈神经网络,数据按黑箭头方向从输入层经过隐藏层流入输出层,向前流动,因此叫做前馈网络.右图中,隐藏层中的数据除了传向输出层,还和下次输入一起训练后续的隐藏层,不再是单向,而是包含了循环,则构成了循环神经网络.下图是将各个时间点画在同一图上,左边前馈FNN的展开图,右边是RNN的展开图.



简单地说,它只是在隐藏层处加了一个"循环",但实际上问题没这么简单.之前说过(详见:深度学习_BP神经网络),输入层向隐藏层传数据时,根据权重U计算(为简化说明省略偏置),隐藏层向输出层传数据时,根据权重V计算(之前文档用字体w1,w2表示),循环神经网络又加了参数W,用于控制隐藏层的权重.看起来好像只是多了一次矩阵乘法和加法,但实际上RNN计算要比前馈网络复杂很多.原因我们看红色箭头,它标记的是误差反向传播,也就是根据实际结果y和输出层的预测结果o计算出的误差传回网络以调整权重UVW.由于每个隐藏层都依赖前一隐藏层的结果,因此误差不只要从隐藏层传回输入层,还要一层一层传回上一隐藏层.它使用计算变得很复杂,且无法并行.

于是又有了下图中的变种,使用实际输出y,和下个x一次训练下一隐藏层,因为y中信息并不像h中那么丰富,因此可能效果会差一些.这里只是循环神经网络的几种情况,其它就不一一列举了,总的来说循环神经网络并无定式,主要指数据的流向中包含循环.



2. LSTM

经常听到LSTM神经网络如何如何,其实LSTM不是一种网络,而是一种对RNN隐藏层的改进算法(改进算法有很多,这个因为效果好,所以比较著名)

LSTM(Long short-term memory)是长短期记忆的简写.



引自:《深度学习》"花书"

如果不断用隐藏层去计算下一时间隐藏层,当计算隐藏层的特征向量大于1时,经过N次迭代后值就会越来越大,最终发生爆炸,如果小于1,最后越来越小,导致消失.换句话说,过去会给我们启发,所以不能忘记过去,但如果每时每刻都被过去影响,就像滚雪球一样,最后也会悲剧.最好是把重点记住,然后在开始新篇章的时候更多地忘记过去.

更直观的说,原来在输入层和隐藏层间是仿射变换加激活函数,现在用输入门,遗忘门输出门和状态层来代替隐藏单元的生成算法.其中每个门都有非线性变换.这几个门的关系,详见代码:

input_gate = tf.sigmoid(tf.matmul(i, ix) + tf.matmul(o, im) + ib)  #输入门
forget_gate = tf.sigmoid(tf.matmul(i, fx) + tf.matmul(o, fm) + fb)  #遗忘门
update = tf.tanh(tf.matmul(i, cx) + tf.matmul(o, cm) + cb)  #更新
state = forget_gate * state + input_gate * update # 更新状态, 遗忘门控制是否忘记旧时状态
output_gate = tf.sigmoid(tf.matmul(i, ox) +  tf.matmul(o, om) + ob) #输出门
return output_gate * tf.tanh(state), state #返回输出值

3. 程序

1) 说明

与前篇的BP网络和CNN网络一样,这次使用的仍然是MNIST手写数据识别.在练习了纯Python和Keras框架之后, 此次使用更低层的TensorFlow代码实现RNN.也顺便了解一个高级工具都封装了什么?

每个图片仍然是28x28像素,前馈网络把28x28共748个像素值作为一个输入x数据传入输入层,而RNN把每张图当成一个序列,序列有28个元素(一行为一个元素),以每行的28个点为输入x传入输入层.简单地说就是切成一行一行训练,每行与下一行有一定联系.

下图是时序图(为简化逻辑,此处只画了一个隐藏层),相对最一般的RNN,下图是一个变种:整个序列(28行)的输入对应同一个输出y(手写对应的数字).



图片.png

2) 代码

# -*- coding: utf-8 -*-

from tensorflow.examples.tutorials.mnist import input_data
import tensorflow as tf
import numpy as np

# 在MINIST_data目录下载 mnist数据
mnist = input_data.read_data_sets("MINIST_data/", one_hot=True)

# RNN学习时使用的参数
learning_rate = 0.001 # 学习率
training_iters = 100000 # 训练实例数
batch_size = 120 # 批大小
display_step = 10 # 训练10批显示一次

n_input = 28 # 每28个作为一个输入层的节点数(行中点)
n_steps = 28 # 28个连续序列(列)
n_hidden = 128 # 隐含层的节点数
n_classes = 10 # 输出的节点数,0~9个数字,这里一共有10个

x = tf.placeholder("float", [None, n_steps, n_input]) # 构建输入节点
istate = tf.placeholder("float", [None, 2 * n_hidden]) # 构建隐藏节点,一个存节点,一个存状态
y = tf.placeholder("float", [None, n_classes]) # 构建输出节点

# 随机初始化各层的权值和偏置
weights = {
'hidden': tf.Variable(tf.random_normal([n_input, n_hidden])), # 输入到隐藏
'out': tf.Variable(tf.random_normal([n_hidden, n_classes])) # 隐藏到输出
}
biases = {
'hidden': tf.Variable(tf.random_normal([n_hidden])),
'out': tf.Variable(tf.random_normal([n_classes]))
}

# 建立RNN模型,_X是批训练数据,_istate为隐藏节点
def RNN(_X, _istate, _weights, _biases):
_X = tf.transpose(_X, [1, 0, 2]) # 把batch_size,n_steps,n_input顺序变为n_steps,batch_size,n_input
_X = tf.reshape(_X, [-1, n_input]) # 再转换为n_steps*batch_size, n_input
# 两个隐藏层:第一层直接计算,第二层用LSTM
_X = tf.matmul(_X, _weights['hidden']) + _biases['hidden'] # 计算隐藏层的节点,此时X从输入节点转为隐藏节点
lstm_cell = tf.nn.rnn_cell.BasicLSTMCell(n_hidden, forget_bias=1.0,state_is_tuple=False) # 定义lstm
_X = tf.split(_X, n_steps, 0) # 序列切片,每片是一个(batch_size, n_hidden)
outputs, states = tf.nn.static_rnn(lstm_cell, _X, initial_state=_istate) # 计算lstm rnn,states存状态
return tf.matmul(outputs[-1], _weights['out']) + _biases['out'] #计算输出层

pred = RNN(x, istate, weights, biases)

cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=pred, labels=y)) # 损失函数为交叉熵
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost) # 优化方法为Adam

correct_pred = tf.equal(tf.argmax(pred, 1), tf.argmax(y, 1)) # 计算错误数
accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32)) # 计算错误率

init = tf.global_variables_initializer()

sess = tf.InteractiveSession()
sess.run(init)
step = 1
while step * batch_size < training_iters:
batch_xs, batch_ys = mnist.train.next_batch(batch_size) # 随机抽取训练数据
batch_xs = batch_xs.reshape((batch_size, n_steps, n_input))
# 用feed_dict传入数据:输入,输出,隐藏层, 数据由placeholder定义, 运行optimizer
sess.run(optimizer, feed_dict={x: batch_xs, y: batch_ys, istate: np.zeros((batch_size, 2 * n_hidden))})
if step % display_step == 0: # 每display_step次批处理显示一次, 通过run运行accuracy,cost
acc = sess.run(accuracy, feed_dict={x: batch_xs, y: batch_ys, istate: np.zeros((batch_size, 2 * n_hidden))})
loss = sess.run(cost, feed_dict={x: batch_xs, y: batch_ys, istate: np.zeros((batch_size, 2 * n_hidden))})
print("Iter " + str(step * batch_size) + ", Minibatch Loss= " + "{:.6f}".format(loss) + ", Training Accuracy= " + "{:.5f}".format(acc))
step += 1
print("Optimization Finished!")

test_len = 256
test_data = mnist.test.images[:test_len].reshape((-1, n_steps, n_input))
test_label = mnist.test.labels[:test_len]
print("Testing Accuracy:", sess.run(accuracy, feed_dict={x: test_data, y: test_label, istate: np.zeros((test_len, 2 * n_hidden))}))


3) 分析
TensorFlow涉及了更多具体计算,比如格式转换,矩阵乘法等等,不像Keras从外面基本看不到具体的步骤的动作和结果.

从代码中很容易明白为什么说TensorFlow是一个"框架",程序的前50行,具体数据还没出现,程序就指定了数据的结构和流向,在接下来的sess部分,tf 才真正开始运算,把数据切块"喂"给框架,使其运行.它和一般程序调函数,确实不太一样.

这里比较不容易理解的是数据怎么从feed_dict转入了RNN函数.在pred = RNN(x, istate, weights, biases)被运行时,其实里面并没有真正的数据在做转换和乘法,这里定义的是数据的流程.此时的x,istate里面还没有数据,而只是定义了数据形式,并告诉TensorFlow该数据需要如何处理.在后面feed_dict处理时才传入了真正的数据,并通过run()间接地调用了RNN(). sess.run()调用函数时,函数的各个参数都是从当前环境里取的.我理解这里的placeholder意思有点像C中定义的数据结构.

4. 问题与解答

1) RNN的隐藏层是一个还是多个?

隐藏层可以是一个,也可以是多个(多层循环网络),比如说可以有三个隐藏层h1,h2,h3,其中h2将结果转给下一个实例的训练,还可以是双向的(一个传向前一时间点,一个传向后一时间点,即双向循环网络),一般为了简化,例子里都有单层的.

2) RNN中反向传播梯度是怎么进行的?

对于训练序列来说,如果序列中每个时间点的输入x都有对应的输出y,总损失就是所有时间步的损失之和.如果像MNIST中整个序列对应一个结果,则使用该结果与预测的误差.具体方法还是梯度下降,只是计算隐藏层与隐藏层之间权重的方法需要按时间往前推.

5. 参考

1) TensorFlow学习笔记(8):基于MNIST数据的循环神经网络RNN

https://segmentfault.com/a/1190000008346992

2) 解读tensorflow之rnn

http://weibo.com/p/23041853dd83fd0102x6wc?sudaref=www.baidu.com&display=0&retcode=6102&sudaref=passport.weibo.com

3) TensorFlow遇到的问题汇总(持续更新中......)

http://www.cnblogs.com/hunttown/p/6866586.html

4) 利用 Keras 下的 LSTM 进行情感分析

http://blog.csdn.net/william_2015/article/details/72978387

5) 基于Theano的深度学习(Deep Learning)框架Keras学习随笔-02-Example

http://blog.csdn.net/niuwei22007/article/details/49053771

6) 循环神经网络(RNN, Recurrent Neural Networks)介绍

http://blog.csdn.net/heyongluoyao8/article/details/48636251

7) 零基础入门深度学习(6) - 长短时记忆网络(LSTM)

https://www.zybuluo.com/hanbingtao/note/581764





技术文章定时推送

请关注公众号:算法学习分享


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  lstm rnn