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(手写对应的数字).
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
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