上一篇文章中总结了tf中卷积神经网络的两个中重要的函数,这篇文章里展示cnn在手写体识别中的应用
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("./MNIST_data/", one_hot=True)
learning_rate = 0.001
training_iters = 200000
batch_size = 128
n_input = 784
n_classes = 10
dropout = 0.75
x = tf.placeholder(tf.float32,[None,n_input])
y = tf.placeholder(tf.float32,[None,n_classes])
keep_prob = tf.placeholder(tf.float32)
weights = {
'wc1':tf.Variable(tf.random_normal([5,5,1,32])),#卷积核大小
'wc2':tf.Variable(tf.random_normal([5,5,32,64])),
'wd1':tf.Variable(tf.random_normal([7*7*64,1024])),
'out':tf.Variable(tf.random_normal([1024,n_classes]))
}
biases = {
'bc1':tf.Variable(tf.random_normal([32])),
'bc2':tf.Variable(tf.random_normal([64])),
'bd1':tf.Variable(tf.random_normal([1024])),
'out': tf.Variable(tf.random_normal([n_classes]))
}
def conv2d(x,W,b,strides=1):
x = tf.nn.conv2d(x,W,strides=[1,strides,strides,1],padding='SAME')
x = tf.nn.bias_add(x,b)
return tf.nn.relu(x)
def maxpool2d(x,k=2):
return tf.nn.max_pool(x,ksize=[1,k,k,1],strides=[1,k,k,1],padding='SAME')
"""
网络对输入进行卷积和池化操作两次,然后一层全连接,最后接着一个输出层
根据输入图形的形状在这个过程中的改变结合参数的设置可以了解这个网络的运行过程
输入的图像形状为【N,784】,N表示一次输入图片的个数,
先是reshape函数进行改变形状变为【N,28,28,1】,为了满足卷积函数的输入的需要,
然后conv2d函数进行卷积,和卷积核操作的结果是【N,28,28,32】,
然后进行池化,图像形状变为【N,14,14,32】,
然后又是卷积和池化操作,根据卷积核参数的设置和池化参数的设置,得到图像形状变为【N,7,7,64】
然后是全连接,全连接第一步先是对输入形状进行改变,【N,7*7*64】
全连接之后图形变为【N,1024】,
最后是输出,图形形状变为【N,10】,可以看出最后的结果是输入的N张图片的标签。
"""
def conv_net(x,weights,biases,dropout):
x = tf.reshape(x,shape=[-1,28,28,1])#-1表示那一维的值由其他维的值和总的值决定,这里代表一次输入的数量
#卷积
conv1 = conv2d(x,weights['wc1'],biases['bc1'])
#池化
conv1 = maxpool2d(conv1,k=2)
#卷积
conv2 = conv2d(conv1,weights['wc2'],biases['bc2'])
#池化
conv2 = maxpool2d(conv2,k=2)
#全连接
fc1 = tf.reshape(conv2,[-1,weights['wd1'].get_shape().as_list()[0]])#?
fc1 = tf.add(tf.matmul(fc1,weights['wd1']),biases['bd1'])
#随机失活
fc1 = tf.nn.dropout(fc1,dropout)
#输出
out = tf.add(tf.matmul(fc1,weights['out']),biases['out'])
return out
#构造模型
pred = conv_net(x,weights,biases,keep_prob)
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(pred, y))
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost)
correct_pred = tf.equal(tf.argmax(pred, 1), tf.argmax(y, 1))
accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))
init = tf.initialize_all_variables()
with tf.Session()as sess:
sess.run(init)
step = 1
while step * batch_size < training_iters:
batch_x,batch_y = mnist.train.next_batch(batch_size)
sess.run(optimizer,feed_dict={x:batch_x,y:batch_y,keep_prob:dropout})
if step % 10 == 0:
loss,acc = sess.run([cost,accuracy],feed_dict={x:batch_x,y:batch_y,keep_prob:1.})
print "Iter " + str(step*batch_size) + ", Minibatch Loss= " + \
"{:.6f}".format(loss) + ", Training Accuracy= " + \
"{:.5f}".format(acc)
step += 1
print "Optimization Finished!"
print "Testing Accuracy:", \
sess.run(accuracy, feed_dict={x: mnist.test.images[:256],
y: mnist.test.labels[:256],
keep_prob: 1.})
Testing Accuracy: 0.976562
从代码的注释中可以详细了解cnn运行过程中图像尺寸的变化,既数据的变化。
接下来是循环神经网络,循环神经网络多用于序列相关的问题,从最终的结果看,在手写数字识别这种和图像相关的问题上的效果也很好,而且之后的双向lstm感觉更是无敌。
模型主要代码为:
def RNN(x, weights, biases):
x = tf.transpose(x, [1, 0, 2])
x = tf.reshape(x, [-1, n_input])
x = tf.split(0, n_steps, x)
lstm_cell = tf.nn.rnn_cell.BasicLSTMCell(n_hidden, state_is_tuple=True)
outputs, states = tf.nn.rnn(lstm_cell, x, dtype=tf.float32)
return tf.matmul(outputs[-1], weights['out']) + biases['out']
对比双向lstm的代码:
def BiRNN(x,weights,biases):
x = tf.transpose(x,[1,0,2])
x = tf.reshape(x,[-1,n_input])
x = tf.split(0,n_steps,x)
lstm_fw_cell = tf.nn.rnn_cell.BasicLSTMCell(n_hidden,state_is_tuple=True)
lstm_bw_cell = tf.nn.rnn_cell.BasicLSTMCell(n_hidden,state_is_tuple=True)
outputs,_,_ = tf.nn.bidirectional_rnn(lstm_fw_cell,lstm_bw_cell,x,dtype=tf.float32)
return tf.matmul(outputs[-1],weights['out'])+biases['out']
其实很相似,就是BiRNN函数的运行函数和RNN不同,而且明显看到BiRNN函数多了一个cell。从RNN和BiRNN中可以看到循环神经网络的建模过程。
以RNN为例,函数的前三行是对输入x的变换,使其能够满足RNN输入的要求。那么,RNN输入的要求是什么样的呢?从RNN的结构中应该能够了解到。
这张图应该是循环神经网络比较经典的解释了,将rnn展开来看就是按照时间或者序列对同一个单元进行不断的输入和训练,所以这也说明了rnn的输入要求。
对于手写体识别的例子,图像的大小是一个784维的向量,如果同时训练n张图像,那么输入的大小就是n*784。图像本身是二维的(28*28),可以看作图像的每一行作为一次输入,循环28次,就像上图中Xt就是一行图像的像素,每次通过训练后,在循环过来和图像下一行的输入一起再进行训练。这样,rnn的输入应该是28,循环28次。这样就能够理解为什么要有前三行代码了。
batch_x, batch_y = mnist.train.next_batch(batch_size)
batch_x = batch_x.reshape((batch_size, n_steps, n_input))
这是最后运行时,x的实际值。
先将128*784的输入(128是一批图像)reshape成(128,28,28);
然后是rnn中transpose(x,[1,0,2]),将(128,28,28)转换成(28,128,28),可以理解为原来有128个28*28的矩阵,将这128个矩阵的第1行组和在一起,第2行组合在一起...就变成了28个128*28的矩阵了;
reshape(x,[-1,n_input]) 把这28个矩阵组合在一起;
split(0,n_steps,x) 函数把x分成28个块,这样每个块就是一次循环的所有输入。
双向循环神经网络最后准确率可以达到98.43%。