RNN、LSTM应用于MNIST数字图片识别

Source

1、RNN和LSTM介绍
两者在语音识别文本识别,机器翻译等方面有很好的应用,因为这些数据存在着前后联系,输入长度不确定。这正是前馈神经网络的缺点:每次网络的输出只依赖当前的输入,没有考虑不同时刻输入的相互影响;输入和输出的维度都是固定的,没有考虑到序列结构数据长度的不固定性。

我们通常所说的RNN实际上有两种,一种是Recurrent Neural Networks,即循环神经网络,一种是Recursive Neural Networks,即递归神经网络。循环神经网络是首先被提出的,它是一种时间上进行线性递归的神经网络,也就是我们通常所说的RNN。下面我们所的都是循环神经网络。
网络结构如下:
在这里插入图片描述
其中xt代表t时刻的输入,ot代表t时刻的输出。我们可以看到下一时刻t+1的输入不但有xt+1,还有上一个隐藏层ht的状态,用于记忆状态,并且每个时刻参数都是一样的。

然而因为要考虑时间状态,在bp求导时,计算的梯度也不只是当前时刻,还有之前的时刻,隐藏层一多,系数连乘就会导致梯度消失和爆炸的问题,并且,前面时刻对后面的影响就变小了,就失去了记忆能力,然而生物神经网络中,我们对很早以前印象深刻的事也是不会忘记的,并且影响我们今后的生活。为了解决这些问题,我们就提出了LSTM。

长短期记忆(Long short-term memory, LSTM)就是要解决这两个问题,通过引入若干门来解决,相比RNN多了一个状态cell state。
在这里插入图片描述
遗忘门:三个输入经过sigmoid函数主要完成对上一状态信息Ct-1进行选择性遗忘;
输入门:ht-1,xt经过sigmoid函数再tanh激活,完成对新信息选择性记忆,不重要的信息少记一些;
输出门:主要决定输出多少状态信息Ct,这个Ct也决定了输出ht。

2、实战演练

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


# In[2]:

#载入数据集
mnist = input_data.read_data_sets("MNIST_data/",one_hot=True)

# 输入图片是28*28
n_inputs = 28 #输入一行,一行有28个数据
max_time = 28 #一共28行
lstm_size = 100 #隐层单元
n_classes = 10 # 10个分类
batch_size = 50 #每批次50个样本
n_batch = mnist.train.num_examples // batch_size #计算一共有多少个批次

#这里的none表示第一个维度可以是任意的长度
x = tf.placeholder(tf.float32,[None,784])
#正确的标签
y = tf.placeholder(tf.float32,[None,10])

#初始化权值
weights = tf.Variable(tf.truncated_normal([lstm_size, n_classes], stddev=0.1))
#初始化偏置值
biases = tf.Variable(tf.constant(0.1, shape=[n_classes]))


#定义RNN网络
def RNN(X,weights,biases):
    # inputs=[batch_size, max_time, n_inputs]
    inputs = tf.reshape(X,[-1,max_time,n_inputs])
    #定义LSTM基本CELL
    lstm_cell = tf.contrib.rnn.core_rnn_cell.BasicLSTMCell(lstm_size)
    # final_state[0]是cell state
    # final_state[1]是hidden_state
    outputs,final_state = tf.nn.dynamic_rnn(lstm_cell,inputs,dtype=tf.float32)
    results = tf.nn.softmax(tf.matmul(final_state[1],weights) + biases)
    return results
    
    
#计算RNN的返回结果
prediction= RNN(x, weights, biases)  
#损失函数
cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=prediction,labels=y))
#使用AdamOptimizer进行优化
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
#结果存放在一个布尔型列表中
correct_prediction = tf.equal(tf.argmax(y,1),tf.argmax(prediction,1))#argmax返回一维张量中最大的值所在的位置
#求准确率
accuracy = tf.reduce_mean(tf.cast(correct_prediction,tf.float32))#把correct_prediction变为float32类型
#初始化
init = tf.global_variables_initializer()

with tf.Session() as sess:
    sess.run(init)
    for epoch in range(6):
        for batch in range(n_batch):
            batch_xs,batch_ys =  mnist.train.next_batch(batch_size)
            sess.run(train_step,feed_dict={
    
      x:batch_xs,y:batch_ys})
        
        acc = sess.run(accuracy,feed_dict={
    
      x:mnist.test.images,y:mnist.test.labels})
        print ("Iter " + str(epoch) + ", Testing Accuracy= " + str(acc))

其中我们要知道输入是一行一行输入的,28*28,所以相当于有28个时刻,RNN函数里面最后输出的第一维是状态,第二维是输出值。
但是效果比不上cnn,因为图片不是时间序列特性的数据,发挥不出它的优势,而且因为加上了门函数判断,计算量也大大增加,所以这里只是一个尝试,并不特别适合这个例子。