1. 程式人生 > >Tensorflow學習筆記(7)——CNN識別mnist程式設計實現

Tensorflow學習筆記(7)——CNN識別mnist程式設計實現

1.卷積神經網路構成(CNN)

卷積神經網路主要由卷積層和pooling層組成。

(1)卷積層

在CNN中的卷積層和普通神經網路的區別:
根據生物學上動物視覺上識別事物是通過區域性感知野的啟發,普通神經網路是下一層的神經元與本層神經元之間是全連結的,而卷積神經網路的下一層神經元只與本層的部分神經元之間有連結。
如下圖所示
這裡寫圖片描述
這樣能夠極大的減少連結數,即極大的減少網路的引數。
由於影象的不變形,即影象中任意區域的統計特性都是相似的特點,我們用同一個模板對影象進行卷積得到的結果即為該卷積模板對整幅影象的提取特徵。因此最終我們訓練需要的引數只有模板大小(若模板為10*10,且卷積模板只有一個時,則該層只需要100個引數)

(2)Down-pooling層

該層又被稱作池化操作、下采樣層。
由於模板(即卷積核)在影象上進行平移,由於步長(stride)不會特別大,因此卷積得到的結果仍然會有很多的冗餘,因此需要進行pooling操作,pooling操作的形式化表示如下圖所示:
這裡寫圖片描述

2.Tensorflow實現mnist手寫體識別演算法

具體分析已經寫在程式碼註釋中。
下面是訓練的程式碼:

#coding=utf-8
'''
Created on 2016-5-17
訓練了一個卷積神經網路,用以識別mnist資料庫
batch_size為50,迴圈執行共2000個batch,最終訓練集上測試正確率在90%
@author: hanchao
'''
import input_data mnist = input_data.read_data_sets('/home/hanchao/mnist', one_hot = True) import tensorflow as tf image_size = 784 #28*28 class_num = 10# 0~10 #共訓練100000次 total_step = 100000 #每隔10×batch_size步顯示一次結果 display_step = 10 #學習率 learning_rate = 0.01 #每次找出50張圖片進行訓練 batch_size = 50 #image的placeholder x = tf.placeholder(tf.float32, [None
,image_size]) #label的placeholder y = tf.placeholder(tf.float32, [None,class_num]) #dropout的placeholder dropoutP = tf.placeholder(tf.float32) #卷積層定義 def conv2d(image_input,w,b,name): return tf.nn.relu(tf.nn.bias_add(tf.nn.conv2d(image_input, w, strides=[1, 1, 1, 1], padding='SAME'),b), name=name) #下采樣層定義 def pooling(featuremaps,kernel_size,name): return tf.nn.max_pool(featuremaps, [1,kernel_size,kernel_size,1], [1,kernel_size,kernel_size,1], padding='SAME') #歸一化操作 def normlize(featuremaps,l_size,name): return tf.nn.lrn(featuremaps, 4, bias=1, alpha=0.0001, beta=0.75) #初始化引數 weights = { #[3,3,1,64]分別代表3*3*1的kernel,輸入層有1個feature maps,輸出層共64個feature maps 'wc1' : tf.Variable(tf.random_normal([3,3,1,64])), #[3,3,64,128]分別代表3*3*64的kernel,輸入層有64個feature maps,輸出層有128個feature maps 'wc2' : tf.Variable(tf.random_normal([3,3,64,128])), 'wc3' : tf.Variable(tf.random_normal([3,3,128,256])), 'wc4' : tf.Variable(tf.random_normal([2,2,256,512])), #全連線層的引數個數設定的原則好像是經過卷積層運算以後feature map的大小並沒有發生改變 #發生改變的原因都是pooling層28/2/2/2/2 = 2(7/2可能是為4) 'wd1' : tf.Variable(tf.random_normal([2*2*512,1024])), 'wd2' : tf.Variable(tf.random_normal([1024,1024])), 'out' : tf.Variable(tf.random_normal([1024,10])) } #初始化偏置項 biases = { 'bc1' : tf.Variable(tf.random_normal([64])), 'bc2' : tf.Variable(tf.random_normal([128])), 'bc3' : tf.Variable(tf.random_normal([256])), 'bc4' : tf.Variable(tf.random_normal([512])), 'bd1' : tf.Variable(tf.random_normal([1024])), 'bd2' : tf.Variable(tf.random_normal([1024])), 'out' : tf.Variable(tf.random_normal([10])) } #構建網路 def constructNet(images,weights,biases,_dropout): #首先把圖片轉為28*28*1的tensor images = tf.reshape(images,[-1,28,28,1]) #第一個卷積層conv1 conv1 = conv2d(images, weights['wc1'], biases['bc1'], 'conv1') print 'conv1: ',conv1.get_shape() #卷積層conv1對應下采樣層 pool1 = pooling(conv1, 2, 'pool1') print 'pool1: ',pool1.get_shape() #歸一化 norm1 = normlize(pool1, l_size=4, name='norm1') dropout1 = tf.nn.dropout(norm1, _dropout) #第二個卷積層 conv2 = conv2d(dropout1,weights['wc2'],biases['bc2'],'conv2') print 'conv2: ',conv2.get_shape() pool2 = pooling(conv2, 2, 'pool2') print 'pool2: ',pool2.get_shape() norm2 = normlize(pool2, 4, 'norm2') dropout2 = tf.nn.dropout(norm2,_dropout) #第三個卷積層 conv3 = conv2d(dropout2, weights['wc3'], biases['bc3'], 'conv3') print 'conv3: ',conv3.get_shape() pool3 = pooling(conv3, 2, 'pool3') print 'pool3: ',pool3.get_shape() norm3 = normlize(pool3, 4, 'norm3') dropout3 = tf.nn.dropout(norm3,_dropout) #第四個卷積層 conv4 = conv2d(dropout3,weights['wc4'],biases['bc4'],'conv4') print 'conv4: ',conv4.get_shape() pool4 = pooling(conv4, 2, 'pool4') print 'pool4: ',pool4.get_shape() norm4 = normlize(pool4, 4, 'norm4') print 'norm4: ',norm4.get_shape() #全連結層1 dense1 = tf.reshape(norm4, [-1,weights['wd1'].get_shape().as_list()[0]]) dense1 = tf.nn.relu(tf.matmul(dense1,weights['wd1']) + biases['bd1'],'fc1') #全連結層2 dense2 = tf.nn.relu(tf.matmul(dense1,weights['wd2']) + biases['bd2'],'fc2') #輸出層,最後輸出層不需要啟用函式relu操作 out = tf.matmul(dense2,weights['out']) + biases['out'] return out pred = constructNet(x, weights, biases, dropoutP) #計算loss loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(pred, y)) #定義操作,用以最小化loss(Adam是一種梯度下降演算法) optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(loss) #tf.arg_max(pred,1)是按行取最大值的下標 #tf.arg_max(y,1)是按列取最大值的下標 correct_pred = tf.equal(tf.arg_max(pred,1), tf.arg_max(y,1)) #先將correct_pred中資料格式轉換為float32型別 #求correct_pred中的平均值,因為correct_pred中除了0就是1,因此求平均值即為1的所佔比例,即正確率 correct_rate = tf.reduce_mean(tf.cast(correct_pred, tf.float32)) init = tf.initialize_all_variables() saver = tf.train.Saver() with tf.Session() as sess: sess.run(init) step = 1 while step*batch_size < total_step: batchx,batchy = mnist.train.next_batch(batch_size) sess.run(optimizer,feed_dict={x:batchx,y:batchy,dropoutP:0.75}) if(step % display_step == 0): accracy = sess.run(correct_rate,feed_dict={x:batchx,y:batchy,dropoutP:1.}) cost = sess.run(loss,feed_dict={x:batchx,y:batchy,dropoutP:1.}) print 'Step: ' + str(step*batch_size) + ' cost: ' + str(cost) + ' accracy: ' + str(accracy) #儲存當前網路的引數,以便測試時讀取訓練結果 saver.save(sess, '/home/hanchao/hanchaoNet.netmodel',step) step += 1 print 'train Finished'

下面是測試部分的程式碼:

#coding=utf-8
'''
Created on 2016-5-17
在上面訓練的網路基礎上在測試集上進行測試,提取前100個測試圖片,正確率在95%
@author: hanchao
'''
import input_data
mnist = input_data.read_data_sets('/home/hanchao/mnist', one_hot=True)

import tensorflow as tf

#構建網路部分,與訓練過程構建網路部分相同
……………
#構建網路部分,與訓練過程構建網路部分相同
#可以直接匯入train中的函式進行使用

saver = tf.train.Saver()

with tf.Session() as sess:
    #讀取上面訓練好的模型引數
    saver.restore(sess, '/home/hanchao/hanchaoNet.netmodel-1990')
    print 'Testing accary: ',sess.run(correct_rate,feed_dict={x:mnist.test.images[:100],y:mnist.test.labels[:100],dropoutP:1.})