TensorFlow實現簡單的卷積網路 | CNN | Tensorflow | 研發乾貨
來自專欄 九三智能控
更多AI資訊,關註:九三智能控
TensorflowCNN
使用TensorFlow實現一個簡單的卷積神經網路:使用的數據集為MNIST,預期可以達到99.2%左右的準確率。將使用兩個卷積層加一個全連接層構建一個簡單但是非常有代表性的卷積神經網路,可以通過這個例子掌握設計卷積神經網路的要點。
MNIST數據集下載,公眾號回復:20180329
首先載入MNIST數據集,並創建默認的Interactive?Session。
from?tensorflow.examples.tutorials.mnist?import?input_data
import?tensorflow?as?tf
mnist?=?input_data.read_data_sets("MNIST_data/",?one_hot=True)
sess?=?tf.InteractiveSession()
接下來要實現的這個卷積神經網路會有很多的權重和偏置需要創建,因此先定義好初始化函數以便重複使用。需要給權重製造一些隨機的雜訊來打破完全對稱,比如截斷的正態分布雜訊,標準差設為0.1。同時因為使用ReLU,也給偏置增加一些小的正值(0.1)用來避免死亡節點(dead?neurons)。
def?weight_variable(shape):
????initial?=?tf.truncated_normal(shape,?stddev=0.1)
????return?tf.Variable(initial)
def?bias_variable(shape):
????initial?=?tf.constant(0.1,?shape=shape)
????return?tf.Variable(initial)
卷積層、池化層也是接下來要重複使用的,因此也為他們分別定義創建函數。這裡的tf.nn.conv2d是TensorFlow中的2維卷積函數,參數中x是輸入,W是卷積的參數,比如[5,5,1,32]:前面兩個數字代表卷積核的尺寸;第三個數字代表有多少個channel。因為只有灰度單色,所以是1,如果是彩色的RGB圖片,這裡應該是3。最後一個數字代表卷積核的數量,也就是這個卷積層會提取多少類的特徵。
Strides代表卷積模板移動的步長,都是1代表會不遺漏地划過圖片的每一個點。Padding代表邊界的處理方式,這裡的SAME代表給邊界加上Padding讓卷積的輸出和輸入保持同樣(SAME)的尺寸。tf.nn.max_pool是TensorFlow中的最大池化函數,這裡使用2×2的最大池化,即將一個2×2的像素塊降為1×1的像素。最大池化會保留原始像素塊中灰度值最高的那一個像素,即保留最顯著的特徵。因為希望整體上縮小圖片尺寸,因此池化層的strides也設為橫豎兩個方向以2為步長。如果步長還是1,那麼會得到一個尺寸不變的圖片。
def?conv2d(x,?W):
????return?tf.nn.conv2d(x,?W,?strides=[1,?1,?1,?1],?padding=SAME)
def?max_pool_2x2(x):
????return?tf.nn.max_pool(x,?ksize=[1,?2,?2,?1],?strides=[1,?2,?2,?1],
??????????????????????????padding=SAME)
在正式設計卷積神經網路的結構之前,先定義輸入的placeholder,x是特徵,y_是真實的label。因為卷積神經網路會利用到空間結構信息,因此需要將1D的輸入向量轉為2D的圖片結構,即從1×784的形式轉為原始的28×28的結構。同時因為只有一個顏色通道,故最終尺寸為[-1,28,28,1],前面的-1代表樣本數量不固定,最後的1代表顏色通道數量。這裡使用的tensor變形函數是tf.reshape。
x?=?tf.placeholder(tf.float32,?[None,?784])
y_?=?tf.placeholder(tf.float32,?[None,?10])
x_image?=?tf.reshape(x,?[-1,28,28,1])
接下來定義的第一個卷積層。先使用前面寫好的函數進行參數初始化,包括weights和bias,這裡的[5,5,1,32]代表卷積核尺寸為5×5,1個顏色通道,32個不同的卷積核。然後使用conv2d函數進行卷積操作,並加上偏置,接著再使用ReLU激活函數進行非線性處理。最後,使用最大池化函數max_pool_2x2對卷積的輸出結果進行池化操作。
W_conv1?=?weight_variable([5,?5,?1,?32])
b_conv1?=?bias_variable([32])
h_conv1?=?tf.nn.relu(conv2d(x_image,?W_conv1)?+?b_conv1)
h_pool1?=?max_pool_2x2(h_conv1)
現在定義第二個卷積層,這個卷積層基本和第一個卷積層一樣,唯一的不同是,卷積核的數量變成了64,也就是說這一層的卷積會提取64種特徵。
W_conv2?=?weight_variable([5,?5,?32,?64])
b_conv2?=?bias_variable([64])
h_conv2?=?tf.nn.relu(conv2d(h_pool1,?W_conv2)?+?b_conv2)
h_pool2?=?max_pool_2x2(h_conv2)
因為前面經歷了兩次步長為2×2的最大池化,所以邊長已經只有1/4了,圖片尺寸由28×28變成了7×7。而第二個卷積層的卷積核數量為64,其輸出的tensor尺寸即為7×7×64。使用tf.reshape函數對第二個卷積層的輸出tensor進行變形,將其轉成1D的向量,然後連接一個全連接層,隱含節點為1024,並使用ReLU激活函數。
W_fc1?=?weight_variable([7?*?7?*?64,?1024])
b_fc1?=?bias_variable([1024])
h_pool2_flat?=?tf.reshape(h_pool2,?[-1,?7*7*64])
h_fc1?=?tf.nn.relu(tf.matmul(h_pool2_flat,?W_fc1)?+?b_fc1)
為了減輕過擬合,下面使用一個Dropout層,Dropout的用法第4章已經講過,是通過一個placeholder傳入keep_prob比率來控制的。在訓練時,隨機丟棄一部分節點的數據來減輕過擬合,預測時則保留全部數據來追求最好的預測性能。
keep_prob?=?tf.placeholder(tf.float32)
h_fc1_drop?=?tf.nn.dropout(h_fc1,?keep_prob)
最後將Dropout層的輸出連接一個Softmax層,得到最後的概率輸出。
W_fc2?=?weight_variable([1024,?10])
b_fc2?=?bias_variable([10])
y_conv=tf.nn.softmax(tf.matmul(h_fc1_drop,?W_fc2)?+?b_fc2)
定義損失函數為cross?entropy,和之前一樣,但是優化器使用Adam,並給予一個比較小的學習速率1e-4。
cross_entropy?=?tf.reduce_mean(-tf.reduce_sum(y_?*?tf.log(y_conv),
??????????????????????????????????????????????reduction_indices=[1]))
train_step?=?tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
再繼續定義評測準確率的操作。
correct_prediction?=?tf.equal(tf.argmax(y_conv,1),?tf.argmax(y_,1))
accuracy?=?tf.reduce_mean(tf.cast(correct_prediction,?tf.float32))
下面開始訓練過程。首先依然是初始化所有參數,設置訓練時Dropout的keep_prob比率為0.5。然後使用大小為50的mini-batch,共進行20000次訓練迭代,參與訓練的樣本數量總共為100萬。其中每100次訓練,會對準確率進行一次評測(評測時keep_prob設為1),用以實時監測模型的性能。
tf.global_variables_initializer().run()
for?i?in?range(20000):
????batch?=?mnist.train.next_batch(50)
????if?i0?==?0:
????????train_accuracy?=?accuracy.eval(feed_dict={x:batch[0],?y_:?batch[1],?
??????????????????????????????????????????????????keep_prob:?1.0})
????????print("step?%d,?training?accuracy?%g"%(i,?train_accuracy))
????train_step.run(feed_dict={x:?batch[0],?y_:?batch[1],?keep_prob:?0.5})
全部訓練完成後,在最終的測試集上進行全面的測試,得到整體的分類準確率。
print("test?accuracy?%g"%accuracy.eval(feed_dict={
????x:?mnist.test.images,?y_:?mnist.test.labels,?keep_prob:?1.0}))
最後,這個CNN模型可以得到的準確率約為99.2%,基本可以滿足對手寫數字識別準確率的要求。相比之前MLP的2%錯誤率,CNN的錯誤率下降了大約60%。這其中主要的性能提升都來自於更優秀的網路設計,即卷積網路對圖像特徵的提取和抽象能力。依靠卷積核的權值共享,CNN的參數量並沒有爆炸,降低計算量的同時也減輕了過擬合,因此整個模型的性能有較大的提升。只實現了一個簡單的卷積神經網路,沒有複雜的Trick。
引用資料
黃文堅、唐源《TensorFlow實戰》
微信群&商業合作
- 加入微信群:不定期分享資料,拓展行業人脈請在公眾號留言:「微信號+名字+研究領域/專業/學校/公司」,我們將很快與您聯繫。
- 投稿(無稿費)、商業合作請留言聯繫。
http://weixin.qq.com/r/AC91bd-EloLprZsO93oS (二維碼自動識別)
推薦閱讀:
※從TensorFlow到Theano:橫向對比七大深度學習框架
※【博客存檔】風格畫之最後一彈MRF-CNN
※初學者如何選擇神經網路環境?對比MATLAB、Torch和TensorFlow
※譯文 | 與TensorFlow的第一次接觸 第六章:並發
※淺入淺出TensorFlow 9 - 代碼框架解析
TAG:TensorFlow | 卷積神經網路CNN |