卷積神經網路(二):應用簡單卷積網路實現MNIST數字識別
阿新 • • 發佈:2019-01-04
卷積神經網路簡單實現MNIST數字識別
本篇的主要內容:
- 一個兩層卷積層的簡單卷積網路的TensorFlow的實現
網路的結構
在這張圖裡,我把每一層的輸入以及輸出的結構都標註了,結合閱讀程式碼食用效果更佳。
具體程式碼
具體的內容,都寫在相應位置的註釋中了,可能一下子全部程式碼都貼出來不太好看,所以我非常貼心的分成了好幾個部分:
匯入包
import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data mnist = input_data.read_data_sets('MNIST_data', one_hot=True)
資料準備:
# 準備資料
trainimg = mnist.train.images
trainlabel = mnist.train.labels
testimg = mnist.test.images
testlabel = mnist.test.labels
print('MNIST ready')
定義不同層的引數與偏置:
# 定義不同層的權重 與 偏置 # 使用 字典的形式方便使用 n_input = 784 n_output = 10 weights = { 'wc1': tf.Variable(tf.random_normal([3, 3, 1, 64], stddev=0.1)), # 第一層卷積層 'wc2': tf.Variable(tf.random_normal([3, 3, 64, 128], stddev=0.1)), # 第二層卷積層 'wd1': tf.Variable(tf.random_normal([7*7*128, 1024], stddev=0.1)), #第一層全連線層 'wd2': tf.Variable(tf.random_normal([1024, n_output], stddev=0.1)) #第二層全連線層 } biases = { 'bc1': tf.Variable(tf.random_normal([64], stddev=0.1)), # 分別是上面四層的偏置 'bc2': tf.Variable(tf.random_normal([128], stddev=0.1)), 'bd1': tf.Variable(tf.random_normal([1024], stddev=0.1)), 'bd2': tf.Variable(tf.random_normal([n_output], stddev=0.1)) }
定義整個網路的結構:
# 定義神經網路結構 def conv_basic(_input, _w, _b, _keepratio): # 首先對輸入資料進行預處理 # 一開始 影象是以 784 長度的向量儲存 # tf 中要求的輸入格式是 四維 向量的形式 所以首先進行資料的預處理 # [a, b, c, d] 中 四個維度 分別是指 batch_size、 height、 width、deepth(channels) # 關於reshape函式,稍後解釋 _input_r = tf.reshape(_input, shape=[-1, 28, 28, 1]) # 建立第一層 是卷積層 conv1 # 這個建立的函式 後面詳細介紹 _conv1 = tf.nn.conv2d(_input_r, _w['wc1'], strides=[1,1,1,1], padding='SAME') # 這裡 也可以合在一起寫 因為一般卷積操作之後會跟一個啟用函式 進行去線性化 這裡使用的 ReLU()函式 _conv1 = tf.nn.relu(tf.nn.bias_add(_conv1, _b['bc1'])) # 建立第二層 是池化層 pool1 _pool1 = tf.nn.max_pool(_conv1, ksize=[1, 2, 2, 1] ,strides=[1, 2, 2, 1], padding='SAME') # 這個函式稍後詳細介紹 _pool_dr1 = tf.nn.dropout(_pool1, _keepratio) # 建立第三層 是卷積層 conv2 (接下來與前面的建立過程是一樣的) _conv2 = tf.nn.conv2d(_pool_dr1, _w['wc2'], strides=[1,1,1,1], padding='SAME') _conv2 = tf.nn.relu(tf.nn.bias_add(_conv2, _b['bc2'])) # 建立第四層 是池化層 pool2 _pool2 = tf.nn.max_pool(_conv2, ksize=[1,2,2,1], strides=[1,2,2,1], padding='SAME') _pool_dr2 = tf.nn.dropout(_pool2, _keepratio) # 向量化處理 因為接下來要建立 全連線層 就像之前一樣 全連線層要進行的處理時向量形式的 _dense1 = tf.reshape(_pool_dr2, [-1, _w['wd1'].get_shape().as_list()[0]]) # 建立第五層 是全連線層 fc1 _fc1 = tf.nn.relu(tf.add(tf.matmul(_dense1, _w['wd1']), _b['bd1'])) _fc_dr1 = tf.nn.dropout(_fc1, _keepratio) # 建立第六層 是全連線層 fc2 _out = tf.add(tf.matmul(_fc_dr1, _w['wd2']), _b['bd2']) # 對上面所有的計算 建立字典形式來返回多個內容 out = { 'input_r': _input_r, 'conv1':_conv1, 'pool1': _pool1, 'pool1_dr1':_pool_dr1, 'conv2': _conv2, 'pool2': _pool2, 'pool_dr2': _pool_dr2, 'dense1': _dense1, 'fc1' : _fc1, 'fc1_dr1': _fc_dr1, 'out': _out } return out print("CNN READY")
定義優化器以及損失函式、正確率等計算方法:
# 建立幾個佔位符 用來輸入資料
x = tf.placeholder(tf.float32, [None, n_input])
y = tf.placeholder(tf.float32, [None, n_output])
keepratio = tf.placeholder(tf.float32)
# 定義損失函式 使用的是交叉熵函式
# 定義優化器 優化方式是 隨機梯度下降
_pred = conv_basic(x, weights, biases, keepratio)['out']
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(logits=_pred, labels=y))
optm = tf.train.AdamOptimizer(learning_rate=0.001).minimize(cost)
# 定義正確率計算函式
_corr = tf.equal(tf.argmax(_pred, 1), tf.argmax(y, 1))
accr = tf.reduce_mean(tf.cast(_corr, tf.float32))
init_op = tf.global_variables_initializer()
print("GRAPH READY")
執行部分:
sess = tf.Session()
sess.run(init_op)
training_epoches = 15 # 訓練輪數
batch_size = 16 # 每一批次數量
display_step = 1 # 用於一定步數之後 輸出中間結果
for epoch in range(training_epoches):
avg_cost = 0
# total_batch = int(mnist.train.num_examples/batch_size) 本來應該是這樣定義 total_batch 的
total_batch = 100 # 個人硬體條件有限。。 所以直接指定 total_batch
batch_xs, batch_ys = None, None
for i in range(total_batch):
batch_xs, batch_ys = mnist.train.next_batch(batch_size)
sess.run(optm, feed_dict={x: batch_xs, y:batch_ys, keepratio:0.7})
avg_cost += sess.run(cost, feed_dict={x: batch_xs, y:batch_ys, keepratio:1.})/total_batch
if epoch % display_step == 0:
print('Epoch: %03d/%03d cost: %.9f'%(epoch, training_epoches, avg_cost))
#train_acc = sess.run(accr, feed_dict={x:batch_xs, y:batch_ys, keepratio:1.}) # 這裡可以輸出訓練集的正確率
test_acc = sess.run(accr, feed_dict={x:testimg, y:testlabel, keepratio:1.}) # 測試集的正確率
#print("Training accuracy: %.03f" % train_acc)
print("Test accuracy: %.03f" % test_acc)
sess.close()
print("OPTIMIZATION FINISHED")
受限於機器。。。我設定的訓練輪數只有 15 次(也很吃力),最後在 Test 資料集上的識別率大概是在 97.5% 以上 ,在中介結果來看,應該隨著訓練的增加,識別率將會繼續增加。
MNIST ready
CNN READY
GRAPH READY
Epoch: 000/015 cost: 2.028865358
Test accuracy: 0.805
Epoch: 001/015 cost: 0.471636516
Test accuracy: 0.907
Epoch: 002/015 cost: 0.289570311
Test accuracy: 0.936
Epoch: 003/015 cost: 0.197701737
Test accuracy: 0.950
Epoch: 004/015 cost: 0.200011803
Test accuracy: 0.955
Epoch: 005/015 cost: 0.173358922
Test accuracy: 0.961
Epoch: 006/015 cost: 0.129958615
Test accuracy: 0.967
Epoch: 007/015 cost: 0.119177620
Test accuracy: 0.969
Epoch: 008/015 cost: 0.099255772
Test accuracy: 0.970
Epoch: 009/015 cost: 0.102970457
Test accuracy: 0.973
Epoch: 010/015 cost: 0.093222790
Test accuracy: 0.970
Epoch: 011/015 cost: 0.100271105
Test accuracy: 0.975
Epoch: 012/015 cost: 0.081711438
Test accuracy: 0.978
Epoch: 013/015 cost: 0.074602938
Test accuracy: 0.979
Epoch: 014/015 cost: 0.090728084
Test accuracy: 0.978
OPTIMIZATION FINISHED
幾個說明點
- 輸入資料格式調整
在TensorFlow中,卷積的輸入要求是四維陣列的形式,所以我們直接獲得的mnist資料集不能直接使用,使用reshape可以得到符合規定的輸入。
標準的輸入格式:
[batch_size, height, width, deepth]
batch_size就是每一批要進行處理的資料數量
height 是輸入圖片的高度
width 是輸入圖片的寬度
deepth 也可以說是 channel,也就是通道數,例如輸入的影象是RGB格式的話,通道數就是3
- help函式
如果對 TensorFlow 的某些函式不瞭解,在IPython 或者Jupyter notebook中可以使用:
print(help(tf.nn.conv2d))
獲得函式的說明。
當然,如果使用Pycharm的話,可以直接點選函式,在右側的 Documentation 內就會出現函式的介紹。
- reshape()函式
- conv2d()函式
conv = tf.nn.conv2d() # 建立卷積層
# 定義
def conv2d(input: Any, # 輸入資料
filter: Any, # 卷積核權重矩陣
strides: Any, # 一般使用四維的輸入形式
padding: Any, # 取值有 SAME 和 VALID
use_cudnn_on_gpu: bool = True,
data_format: str = "NHWC",
dilations: List[int] = [1, 1, 1, 1],
name: Any = None) -> Any
# 定義例子
_conv1 = tf.nn.conv2d(_input_r, _w['wc1'], strides=[1,1,1,1], padding='SAME')
- dropout()函式
這個函式的作用就是在訓練過程中隨機 “殺死” 一些神經元,也就是說為模型的收斂做了一點貢獻,還可以防止過擬合,在TensorFlow中一般是會使用在全連線層上,上面的程式碼中,放在了卷積層附近,當然,對於這樣小型的網路,其實並不會有什麼區別。具體的解釋可以看這篇部落格:
談談TensorFlow的dropout
以上~