上一篇文章中總結了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%。