一、前言
本篇将以RNN(Recurrent Neural Network)(循环神经网络)的网络结构来对我们熟知的MNIST数据集做分类模型训练,MNIST数据集在前面的文章中有简要的介绍,这里不做赘述,仅在下面的代码中有相应展现。
二、介绍
RNN:循环神经网络是一类用于处理序列数据的神经网络,就像卷积网络是专门用于处理网格化数据的神经网络一样,属于具有不同网络结构但有重叠功能的神经网络。卷积神经网络可以很容易的拓展(应用其局部感知能力)到具有很大宽度和高度的图像,以及处理大小可变的图像,循环神经网络可以拓展到更长的序列,大多数循环神经网络也能处理可变长度的序列。循环神经网络其循环的要义就是输出的每一项是前一项的函数,并且输出的每一项对先前的输出应用相同的更新规则而产生,因而其参数共享机制是基于时间序列上的参数共享。所以RNN更加适合做对于前后有依赖关系的结构的输入来做模型训练,例如:情感分析、关键字提取、语音识别、机器翻译和股票分析等细分领域。
单向RNN:从字面意思理解,单向的意思就是下文的输出仅与之前的序列有关。
双向RNN:该结构的含义为,当前的输出不仅和之前的序列有关,并且还与之后的序列有关。
三、任务目标
1.正确的构建RNN单层和双向单隐层网络结构
2.打印模型训练过程中准确率的迭代过程
3.使用tensorboard可视化界面进一步观察网络结构
四、具体实现过程
1.导包
import tensorflow as tf
import numpy as np
from tensorflow.examples.tutorials.mnist import input_data
2.获取数据集
def get_date():
mnist=input_data.read_data_sets('MNIST/',one_hot=True)
return mnist,mnist.train.images,mnist.train.labels,mnist.test.images,mnist.test.labels
3.定义参数初始化方式
def get_variable(name,shape,dtype=tf.float32,initializer=tf.random_normal_initializer(mean=0,stddev=0.1)):
return tf.get_variable(name=name,shape=shape,dtype=dtype,initializer=initializer)
4.定义网络参数
def set_params():
training_epochs=10000
testing_epochs=156
#维度数目
num_units=128
#批次数量
batch_size=64
#时刻数目
time_num=28
#数据输入维度数目
input_dimssion=28
learning_rate=0.001
class_num=10
return training_epochs,testing_epochs,num_units,batch_size,time_num,input_dimssion,learning_rate,class_num
5.定义网络结构
def set_network(X,Y,batch_size,num_units,class_num):
#num_units:指定Cell中各个神经网络层次中的神经元数目
RNN_Cell=tf.nn.rnn_cell.BasicRNNCell(num_units=num_units)
#给定初始状态
init_state=RNN_Cell.zero_state(batch_size=batch_size,dtype=tf.float32)
#传入输入和state获取结果值
output,state=tf.nn.dynamic_rnn(RNN_Cell,inputs=X,initial_state=init_state)
#拿到结果
output=output[:,-1,:]
network=tf.nn.bias_add(tf.matmul(output,get_variable(name='w',shape=[num_units,class_num])),get_variable('b',[class_num]))
predict=tf.nn.softmax(network)
return predict
5.定义双向RNN网络结构(2)
def set_network(X,Y,batch_size,num_units,class_num):
time_num=28
#将X按列划分为序列
X=tf.unstack(X,time_num,1)
#定义正反向RNN结构
RNN_fw_Cell=tf.nn.rnn_cell.BasicRNNCell(num_units)
RNN_bw_Cell=tf.nn.rnn_cell.BasicRNNCell(num_units)
output,_,_=tf.nn.static_bidirectional_rnn(RNN_fw_Cell,RNN_bw_Cell,inputs=X,dtype=tf.float32)
output=output[:][-1][:]
network = tf.nn.bias_add(tf.matmul(output, get_variable(name='w', shape=[num_units*2, class_num])),
get_variable('b', [class_num]))
predict = tf.nn.softmax(network)
return predict
6.模型训练
def train():
# 获取数据
mnist, train_images, train_labels, test_images, test_labels = get_date()
# 获取网络参数
training_epochs, testing_epochs,num_units,batch_size,time_num, input_dimssion, learning_rate,class_num = set_params()
#定义占位符
X=tf.placeholder(tf.float32,shape=[None,784])
X_=tf.reshape(X,shape=[-1,time_num,input_dimssion])
Y=tf.placeholder(tf.float32,shape=[None,class_num])
#batch_size=X.get_shape()[0]
#train_images=tf.reshape(shape=[-1,time_num,input_dimssion])
predict=set_network(X_,Y,batch_size=batch_size,num_units=num_units,class_num=class_num)
loss=tf.reduce_sum(tf.nn.softmax_cross_entropy_with_logits(logits=predict,labels=Y))
train=tf.train.GradientDescentOptimizer(learning_rate=learning_rate).minimize(loss)
#计算正确率
equal=tf.equal(tf.arg_max(predict,1),tf.arg_max(Y,1))
acc=tf.reduce_mean(tf.cast(equal,tf.float32))
init=tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init)
for epoch in range(training_epochs):
batch_x,batch_y=mnist.train.next_batch(batch_size)
feed_dict={X:batch_x,Y:batch_y}
sess.run(train,feed_dict=feed_dict)
loss_=sess.run(loss,feed_dict=feed_dict)
acc_=sess.run(acc,feed_dict=feed_dict)
print("epoch:{},loss:{},acc:{}".format(epoch+1,loss_,acc_))
avg_acc=0.0
avg_loss=0.0
for epoch in range(testing_epochs):
batch_x,batch_y=mnist.test.next_batch(batch_size)
feed_dict_test={X:batch_x,Y:batch_y}
loss_test=sess.run(loss,feed_dict=feed_dict_test)
acc_test=sess.run(acc,feed_dict=feed_dict_test)
avg_acc+=acc_test
avg_loss+=loss_test
print("*"*20,"testing","*"*20)
print("epoch:{},loss:{},acc:{}".format(epoch+1,loss_test,acc_test))
avg_loss=avg_loss/testing_epochs
avg_acc=avg_acc/testing_epochs
print("avg_loss:{},avg_acc:{}".format(avg_loss,avg_acc))
if __name__=='__main__':
train()
7.添加tensorboard显示代码
分别在文中相应位置添加如下代码段
#将损失以标量的形式显示
tf.summary.scalar('loss',loss)
#合并所有summary
merged_summary_op=tf.summary.merge_all()
#创建summary_writer,用于写文件
summary_writer=tf.summary.FileWriter('log/mnist_with_summaries',sess.graph)
#生成summary
summary_str=sess.run(merged_summary_op,feed_dict=feed_dict)
#将summary写入文件
summary_writer.add_summary(summary_str,epoch)
运行结果如下图所示
7.1该图为loss标量结果的变化图
7.2该图为网络的结构图示
从图中可以看出BasicRNNCellZeroState传入到整个网络结构中去,占位符进行分时输入,GradientDescent进行权值的更新,还进行了损失函数的计算和正确率的计算等等。
7.3测试集最终结果图
五、分析与总结
1.RNN从网络结构上来讲是处理前后有依赖关系的序列,比如一句话可以分为多个词向量,每一个词向量的前后必然是有联系的,例如:隔壁家小孩说“我想吃饭”这个是正确无疑的逻辑,那么倒过来说“饭吃想我”那么就不对了,或者仅改变任意一个字的位置也会显得非常奇怪。通过这个逻辑来看,图片是有先后关系的么,仔细分析一下,显然是有的,不过这个逻辑的连结没有文字序列那么紧密,但是仅仅以mnist数据集来看的话,这个连结的稳定性还是比较强的,因为它不像那种抽象派的画,数字图片的结构是固定的,就像我们写数字的时候也不是一下子就可以写好的,也就是需要进行分时(按笔画)书写才能得以完成的。这样来看的话RNN同CNN一样也是有局部感知能力的,只是相对来说要弱于CNN的效果。
2.代码中将一副784个像素值的图片划分为28个时序分别进行输入,拿到输出结果进行全连接、softmax和argmax得到分类结果。
3.LSTM代码的书写与上面类似,仅仅修改个别代码而已,LSTM的结构这里就不赘述了,以后有机会再稍微的提一下。