前面 有篇博文講了多層感知器,也就是一般的前饋神經網絡,文章裏使用 CIFAR10 數據集得到的測試準確率是 46.98%。今天我們使用更適合處理圖像的卷積神經網絡來處理相同的數據集 - CIFAR10,來看下準確率能達到多少。
本文代碼基於 TensorFlow 的官方文檔 做了些許修改,完整代碼及結果圖片可從 這裏 下載。
這篇 文章是對本文的一個升級,增加了 TensorBoard 的實現,可以在瀏覽器中查看可視化結果,包括準確率、損失、計算圖、訓練時間和內存信息等。
更新
這裏我會列出對本文的更新。
- 2017年3月17日:增加實現 TensorBoard 的文章的鏈接。
原理
關於卷積神經網絡(Convolutional Neural Network,以下簡稱 CNN)網上有很多優秀的教程,我在這裏也不再重複造輪子,強烈推薦 斯坦福的CS321n,講的很全面。
還是和以前一樣,我在這裏簡單說下 CNN 的原理。首先來看下一個典型的 CNN - LeNet5 的結構圖,
這個 CNN 是 Yann LeCun 在 1998 年發表的論文 Gradient-Based Learning Applied to Document Recognition 中所提出的,用於字符識別。如上圖,模型輸入是一個字符圖像,然後對輸入進行卷積操作(矩陣點乘求和)得到 C1
卷積層,再進行下采樣操作(縮減維度)得到 S2
池化層或者叫下采樣層,然後重複一遍剛纔的操作,將二維數據 拉平,連着一個多層的普通神金網絡,最終得到輸出。訓練這個模型的方法還是梯度下降法,或者其變種。
數據集
數據集仍然採用 CIFAR10,包含 60000 張 32×32 的彩色RGB圖像,共有 10 類,每類有 6000 張圖像。完整數據集可以從 這裏 下載,注意選擇 Python 版本,大概是 163 MB。
下載好後解壓會看到有 5 個訓練文件和 1 個測試文件,還有一個說明文件 batches.meta
,這個文件說明了每個數字類別(0-9)具體代表哪些類別。這幾個文件都是用 cPickle
打包好的,所以載入數據也要用 cPickle
來載入。注意 Python2
和 Python3
的載入方式稍微有些不同,具體見代碼,我使用的是 Python3
。
目前在此數據集上做的實驗在沒有數據增加的情況下最低的錯誤率是 18%,數據增加的情況下最低的錯誤率是 11%,都是採用的卷積神經網絡(CNN)的結構。
數據集中的圖像和分類大致是這樣的:
代碼
由於完整代碼較長,這裏僅列出關鍵代碼,便於理解整個模型,完整代碼可從 這裏 下載。
# coding: utf-8
with tf.device('/cpu:0'):
# 模型參數
learning_rate = 1e-3
training_iters = 500
batch_size = 50
display_step = 5
n_features = 3072 # 32*32*3
n_classes = 10
n_fc1 = 384
n_fc2 = 192
# 構建模型
x = tf.placeholder(tf.float32, [None, n_features])
y = tf.placeholder(tf.float32, [None, n_classes])
w = {
'conv1': tf.Variable(tf.truncated_normal([5, 5, 3, 64], stddev=5e-2)),
'conv2': tf.Variable(tf.truncated_normal([5, 5, 64, 64], stddev=0.1)),
'fc1': tf.Variable(tf.truncated_normal([8*8*64, n_fc1], stddev=0.04)),
'fc2': tf.Variable(tf.truncated_normal([n_fc1, n_fc2], stddev=0.1)),
'fc3': tf.Variable(tf.truncated_normal([n_fc2, n_classes], stddev=1/192.0))
}
b = {
'conv1': tf.Variable(tf.constant(0.0, dtype=tf.float32, shape=[64])),
'conv2': tf.Variable(tf.constant(0.1, dtype=tf.float32, shape=[64])),
'fc1': tf.Variable(tf.constant(0.1, dtype=tf.float32, shape=[n_fc1])),
'fc2': tf.Variable(tf.constant(0.1, dtype=tf.float32, shape=[n_fc2])),
'fc3': tf.Variable(tf.constant(0.0, dtype=tf.float32, shape=[n_classes]))
}
x4d = tf.reshape(x, [-1, 32, 32, 3])
# 卷積層 1
conv1 = tf.nn.conv2d(x4d, w['conv1'], strides=[1, 1, 1, 1], padding='SAME')
conv1 = tf.nn.bias_add(conv1, b['conv1'])
conv1 = tf.nn.relu(conv1)
# 池化層 1
pool1 = tf.nn.max_pool(conv1, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1], padding='SAME')
# LRN層,Local Response Normalization
norm1 = tf.nn.lrn(pool1, 4, bias=1.0, alpha=0.001/9.0, beta=0.75)
# 卷積層 2
conv2 = tf.nn.conv2d(norm1, w['conv2'], strides=[1, 1, 1, 1], padding='SAME')
conv2 = tf.nn.bias_add(conv2, b['conv2'])
conv2 = tf.nn.relu(conv2)
# LRN層,Local Response Normalization
norm2 = tf.nn.lrn(conv2, 4, bias=1.0, alpha=0.001/9.0, beta=0.75)
# 池化層 2
pool2 = tf.nn.max_pool(norm2, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1], padding='SAME')
reshape = tf.reshape(pool2, [-1, 8*8*64])
dim = reshape.get_shape()[1].value
fc1 = tf.add(tf.matmul(reshape, w['fc1']), b['fc1'])
fc1 = tf.nn.relu(fc1)
# 全連接層 2
fc2 = tf.add(tf.matmul(fc1, w['fc2']), b['fc2'])
fc2 = tf.nn.relu(fc2)
# 全連接層 3, 即分類層
fc3 = tf.add(tf.matmul(fc2, w['fc3']), b['fc3'])
# 定義損失
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(fc3, y))
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost)
# 評估模型
correct_pred = tf.equal(tf.argmax(fc3, 1), tf.argmax(y, 1))
accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))
init = tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init)
c = []
total_batch = int(X_train.shape[0] / batch_size)
for i in range(training_iters):
avg_cost = 0
for batch in range(total_batch):
batch_x = X_train[batch*batch_size : (batch+1)*batch_size, :]
batch_y = y_train[batch*batch_size : (batch+1)*batch_size, :]
_, co = sess.run([optimizer, cost], feed_dict={x: batch_x, y: batch_y})
avg_cost += co
c.append(avg_cost)
if (i+1) % display_step == 0:
print("Iter " + str(i+1) + ", Training Loss= " + "{:.6f}".format(avg_cost))
print("Optimization Finished!")
# Test
test_acc = sess.run(accuracy, feed_dict={x: X_test, y: y_test})
print("Testing Accuracy:", test_acc)
結果
其中的縮寫仍然遵照我以前博文的習慣:
- lr:learning rate,學習率
- ti:training iters,訓練迭代次數
- bs:batch size,batch大小
- acc:測試準確率
每次結果都會不一樣,上圖是最好的結果的時候,其他結果圖的下載鏈接和上面一樣,測試準確率大約爲 60%,其實這個準確率並不高,和 TensorFlow 的官方文檔 所得到的 86% 還差一段距離,和官方文檔的差距在於我並沒有對數據進行更多的預處理,例如圖像裁剪到 24×24 的尺寸、標準化、隨機左右翻轉圖像或者隨機改變圖像亮度和對比度以擴大數據集等等,等有時間再進行進一步的實驗,與本次實驗進行對比。
在其他條件一樣的情況下,本次實驗得到的準確率可能並不是最高的,鑑於機器太垃圾,運行一次實驗需要兩三天,未能進行更多的測試。