Tensorflow- 卷積網(wǎng)絡(luò)的MNIST源碼詳盡解析

前言


鑒于國內(nèi)使用卷積網(wǎng)絡(luò)實現(xiàn)MNIST數(shù)據(jù)集識別的源碼(基于Tensorflow實現(xiàn))解析資料很少栅受,本人對最新的代碼進(jìn)行學(xué)習(xí)钩乍,現(xiàn)將筆記心得分享导饲。由于自身水平有限讯嫂,理解不當(dāng)之處請大家批評指正浇垦。本文不會對算法進(jìn)行詳細(xì)介紹炕置,主要針對代碼中所使用的一些函數(shù)定義與用法進(jìn)行解釋,并給出最終運行代碼男韧。本教程假設(shè)讀者已經(jīng)了解MNIST和卷積網(wǎng)絡(luò)讹俊,若未了解,可先看這兩篇我寫的文章:
Tensorflow- MNIST機(jī)器學(xué)習(xí)入門
Tensorflow- CNN卷積神經(jīng)網(wǎng)絡(luò)的MNIST手寫數(shù)字識別

數(shù)據(jù)集


數(shù)據(jù)集是MNIST煌抒,一個入門級的計算機(jī)視覺數(shù)據(jù)集仍劈,它包含各種手寫數(shù)字圖片:

每張圖片包含28X28個像素點,標(biāo)簽即為圖片中的數(shù)字寡壮。

問題


使用MNIST數(shù)據(jù)集進(jìn)行訓(xùn)練贩疙,識別圖片中的手寫數(shù)字(0到9,共10類)况既。

思路


使用一個簡單的CNN網(wǎng)絡(luò)結(jié)構(gòu)如下这溅,括號里邊表示tensor經(jīng)過本層后的輸出shape:

輸入層(28 * 28 * 1)-->卷積層1(28 * 28 * 32)-->pooling層1(14 * 14 * 32)-->卷積層2(14 * 14 * 64)-->池化層2(7 * 7 * 64)-->全連接層(1 * 1024)-->softmax層(10)

函數(shù)說明


在給出完整代碼前,先對幾個的主要函數(shù)中的主要參數(shù)進(jìn)行說明棒仍。也可以先運行代碼悲靴,不懂的部分在看這里的函數(shù)解釋。

tf.truncated_normal(shape, mean=0.0, stddev=1.0, dtype=tf.float32, seed=None, name=None)

隨機(jī)產(chǎn)生一個形狀為shape的服從截斷正態(tài)分布(均值為mean莫其,標(biāo)準(zhǔn)差為stddev)的tensor癞尚。截斷的方法根據(jù)官方API的定義為耸三,如果單次隨機(jī)生成的值偏離均值2倍標(biāo)準(zhǔn)差之外,就丟棄并重新隨機(jī)生成一個新的數(shù)浇揩。

tf.nn.conv2d(input, filter, strides, padding, use_cudnn_on_gpu=None, data_format=None, name=None)
  • input:一個形狀為[batch, in_height, in_width, in_channels]的tensor:

    • batch:每次batch數(shù)據(jù)的數(shù)量仪壮。
    • in_height,in_width:輸入矩陣的高和寬胳徽,如輸入層的圖片是28*28积锅,則in_height和in_width就都為28。
    • in_channels:輸入通道數(shù)量养盗。如輸入層的圖片經(jīng)過了二值化缚陷,則通道為1,如果輸入層的圖片是RGB彩色的往核,則通道為3箫爷;再如卷積層1有32個通道,則pooling層1的輸入(卷積層1的輸出)即為32通道铆铆。
  • filterfilter:一個形狀為[filter_height, filter_width, in_channels, out_channels]的tensor:

    • filter_height, filter_width:卷積核的高與寬。如卷積層1中的卷積核丹喻,filter_height, filter_width都為28薄货。
    • in_channels:輸入通道數(shù)量。
    • out_channels:輸出通道的數(shù)量碍论。如輸入數(shù)據(jù)經(jīng)過卷積層1后谅猾,通道數(shù)量從1變?yōu)?2。
  • strides:滑動窗口(卷積核)的滑動規(guī)則鳍悠,包含4個維度税娜,分別對應(yīng)input的4個維度,即每次在input tensor上滑動時的步長藏研。其中batch和in_channels維度一般都設(shè)置為1敬矩,所以形狀為[1, stride, stride, 1]

tf.nn.max_pool(value, ksize, strides, padding, data_format='NHWC', name=None)
  • value:以tf.nn.conv2d()函數(shù)的參數(shù)input理解即可蠢挡。
  • ksize:滑動窗口(pool)的大小尺寸弧岳,這里注意這個大小尺寸并不僅僅指2維上的高和寬,ksize的每個維度同樣對應(yīng)input的各個維度(只是大小业踏,不是滑動步長)禽炬,同樣的,batch和in_channels維度多設(shè)置為1勤家。如pooling層1的ksize即為[1, 2, 2, 1]腹尖,即用一個2*2的窗口做pooling。
  • strides:同tf.nn.conv2d()函數(shù)的參數(shù)strides伐脖。
tf.nn.dropout(x, keep_prob, noise_shape=None, seed=None, name=None)
  • x:輸入tensor热幔。
  • keep_probx:每個元素的輸出概率乐设,輸出為原值或0。

代碼


'''
Created on 2017年10月11日

@author: zhoucheng
'''
from tensorflow.examples.tutorials.mnist import input_data
"""自動創(chuàng)建一個'MNIST_data'的目錄來存儲數(shù)據(jù)断凶,將下載的文件解壓伤提,圖像轉(zhuǎn)化為4D向量[index, y, x, depth],
        分別存儲在'train', 'validation', 'test'的Dataset中认烁。將標(biāo)簽one-hot編碼后肿男,就可以跟對連續(xù)型特征的
        歸一化方法一樣,對每一維特征進(jìn)行歸一化
"""
mnist = input_data.read_data_sets('MNIST_data', one_hot=True)
import tensorflow as tf

#Weight Initialization
def weight_variable(shape):
    '''tf.truncated_normal(shape, mean, stddev) :shape表示生成張量的維度却嗡,mean是均值舶沛,stddev是標(biāo)
                準(zhǔn)差。這個函數(shù)產(chǎn)生正太分布窗价,均值和標(biāo)準(zhǔn)差自己設(shè)定如庭。這是一個截斷的產(chǎn)生正太分布的函數(shù),就是說產(chǎn)生
                正太分布的值如果與均值的差值大于兩倍的標(biāo)準(zhǔn)差撼港,那就重新生成坪它。
    '''
    initial = tf.truncated_normal(shape = shape, stddev=0.1)
    return tf.Variable(initial)

def bias_variable(shape):
    initial = tf.constant(0.1, shape=shape)
    return tf.Variable(initial)

'''輸入圖片x是一個2維的浮點數(shù)張量。這里帝牡,分配給它的shape為[None, 784]往毡,其中784是一張展平的MNIST圖片
        的維度(28X28)。None表示其值大小不定靶溜,在這里作為第一個維度值开瞭, 用以指代batch的大小,意即x的數(shù)量不
        定罩息。輸出類別值y_也是一個2維張量嗤详,其中每一行為一個10維的one-hot向量,用于代表對應(yīng)某一MNIST圖片的類別。
'''
x = tf.placeholder(tf.float32, shape=[None, 784])
y_ = tf.placeholder(tf.float32, shape=[None, 10])

#Convolution and Pooling
'''TensorFlow在卷積和池化上有很強(qiáng)的靈活性瓷炮。我們怎么處理邊界葱色?步長應(yīng)該設(shè)多大?在這個實例里娘香,我們會一直
        使用vanilla版本冬筒。我們的卷積使用1步長(stride size),0邊距(padding size)的模板茅主,通過填充“零”保
        證輸出和輸入是同一個大小舞痰。我們的池化用簡單傳統(tǒng)的2x2大小的模板做max pooling。為了代碼更簡潔诀姚,我們把
        這部分抽象成一個函數(shù)响牛。
'''
def conv2d(x, W):
    return tf.nn.conv2d(input = x, filter = 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')
    
#First Convolutional Layer

'''現(xiàn)在我們可以開始實現(xiàn)第一層了。它由一個卷積接一個max pooling完成。卷積在每個5x5的patch中算出32個特征
    (32個卷積核呀打,可以學(xué)習(xí)32種特征)矢赁。卷積的權(quán)重張量形狀是[5, 5, 1, 32],前兩個維度是patch的大小贬丛,接著
        是輸入的通道數(shù)目撩银,最后是輸出的通道數(shù)目。 而對于每一個輸出通道都有一個對應(yīng)的偏置量豺憔。
'''
W_conv1 = weight_variable([5, 5, 1, 32])
b_conv1 = bias_variable([32])

'''為了用這一層额获,我們把x變成一個4d向量,其第2恭应、第3維對應(yīng)圖片的寬抄邀、高,最后一維代表圖片的顏色通道數(shù)(因為
        是灰度圖所以這里的通道數(shù)為1昼榛,如果是rgb彩色圖境肾,則為3)。將784X1的向量形式轉(zhuǎn)化成28X28的矩陣形式進(jìn)行卷積運算
'''
x_image = tf.reshape(x, [-1, 28, 28, 1])

'''我們把x_image和權(quán)值向量進(jìn)行卷積胆屿,加上偏置項奥喻,然后應(yīng)用ReLU激活函數(shù),最后進(jìn)行max pooling非迹。同理环鲤,可以
        建立結(jié)構(gòu)不變,輸入32個通道彻秆,輸出64個通道的第二層卷積層楔绞。
'''
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)#32 map:14X14
h_pool1 = max_pool_2x2(h_conv1)

#Second Convolutional Layer
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)#64 map:7X7
h_pool2 = max_pool_2x2(h_conv2)

'''現(xiàn)在结闸,圖片尺寸減小到7x7唇兑,我們加入一個有1024個神經(jīng)元的全連接層,用于處理整個圖片桦锄。我們把池化層輸出的張
        量reshape成向量扎附,乘上權(quán)重矩陣,加上偏置结耀,然后對其使用ReLU留夜。
'''
#Densely Connected Layer
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)#matrix 1X1024

'''為了減少過擬合,我們在輸出層之前加入dropout图甜。我們用一個placeholder來代表一個神經(jīng)元的輸出在dropout中
        保持不變的概率碍粥。這樣我們可以在訓(xùn)練過程中啟用dropout,在測試過程中關(guān)閉dropout黑毅。 TensorFlow的
    tf.nn.dropout操作除了可以屏蔽神經(jīng)元的輸出外嚼摩,還會自動處理神經(jīng)元輸出值的scale。所以用dropout的時候
        可以不用考慮scale。
'''

#dropout
keep_prob = tf.placeholder(tf.float32)
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)#matrix 1X1024

'''最后枕面,我們添加一個softmax層愿卒,把向量化后的圖片x和權(quán)重矩陣W相乘,加上偏置b潮秘,然后計算每個分類的softmax概率值琼开。
'''
#Readout Layer
W_fc2 = weight_variable([1024, 10])
b_fc2 = bias_variable([10])

y_conv = tf.matmul(h_fc1_drop, W_fc2) + b_fc2#matrix 1X10

'''可以很容易的為訓(xùn)練過程指定最小化誤差用的損失函數(shù),我們的損失函數(shù)是目標(biāo)類別和預(yù)測類別之間的交叉熵枕荞。
'''
#loss function
"""
   softmax_cross_entropy_with_logits返回1X10的向量柜候,代表每個維度預(yù)測越準(zhǔn)確,結(jié)果的值越新虿(別忘了前面還有負(fù)號)改橘,
   最后reduce_mean求一個平均,得到我們想要的loss
"""
cross_entropy = tf.reduce_mean( #a number
    tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y_conv))

'''我們已經(jīng)定義好模型和訓(xùn)練用的損失函數(shù)玉控,那么用TensorFlow進(jìn)行訓(xùn)練就很簡單了飞主。因為TensorFlow知道整個計算圖,它
        可以使用自動微分法找到對于各個變量的損失的梯度值高诺。TensorFlow有大量內(nèi)置的優(yōu)化算法 這個例子中碌识,我們用最速下降
        法讓交叉熵下降,步長為0.0001.這一行代碼實際上是用來往計算圖上添加一個新操作虱而,其中包括計算梯度筏餐,計算每個參數(shù)
        的步長變化,并且計算出新的參數(shù)值牡拇。
        返  回的train_step操作對象魁瞪,在運行時會使用梯度下降來更新參數(shù)。整個模型的訓(xùn)練可以通過反復(fù)地運行train_step來完成惠呼。
'''

#Train the Model 
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)

'''首先讓我們找出那些預(yù)測正確的標(biāo)簽导俘。tf.argmax 是一個非常有用的函數(shù),它能給出某個tensor對象在某一維上的其數(shù)據(jù)最
        大值所在的索引值剔蹋。由于標(biāo)簽向量是由0,1組成旅薄,因此最大值1所在的索引位置就是類別標(biāo)簽,比如tf.argmax(y,1)返回的是
        模型對于任一輸入x預(yù)測到的標(biāo)簽值泣崩,而 tf.argmax(y_,1) 代表正確的標(biāo)簽少梁,我們可以用 tf.equal 來檢測我們的預(yù)測是否
        真實標(biāo)簽匹配(索引位置一樣表示匹配)。
    tf.argmax(input, axis=None, name=None, dimension=None)
            此函數(shù)是對矩陣按行或列計算最大值

參數(shù)
input:輸入Tensor
axis:0表示按列矫付,1表示按行
        這里返回一個布爾數(shù)組凯沪。為了計算我們分類的準(zhǔn)確率,我們通過tf.cast將布爾值轉(zhuǎn)換為浮點數(shù)來代表對买优、錯妨马,然后取平均值樟遣。
        例如:[True, False, True, True]變?yōu)閇1,0,1,1],計算出平均值為0.75身笤。
'''

#Evaluate the Model
correct_prediction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y_, 1))#a list of boolean
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))#bool->float,然後求均值

with tf.Session() as sess:
  sess.run(tf.global_variables_initializer())
  for i in range(20000):
    batch = mnist.train.next_batch(50)
    if i % 100 == 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({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}))

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末豹悬,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子液荸,更是在濱河造成了極大的恐慌瞻佛,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件娇钱,死亡現(xiàn)場離奇詭異伤柄,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)文搂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進(jìn)店門适刀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人煤蹭,你說我怎么就攤上這事笔喉。” “怎么了硝皂?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵常挚,是天一觀的道長。 經(jīng)常有香客問我稽物,道長奄毡,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任贝或,我火速辦了婚禮吼过,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘咪奖。我一直安慰自己盗忱,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布赡艰。 她就那樣靜靜地躺著售淡,像睡著了一般斤葱。 火紅的嫁衣襯著肌膚如雪慷垮。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天揍堕,我揣著相機(jī)與錄音料身,去河邊找鬼。 笑死衩茸,一個胖子當(dāng)著我的面吹牛芹血,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼幔烛,長吁一口氣:“原來是場噩夢啊……” “哼啃擦!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起饿悬,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤令蛉,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后狡恬,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體珠叔,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年弟劲,在試婚紗的時候發(fā)現(xiàn)自己被綠了祷安。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡兔乞,死狀恐怖汇鞭,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情庸追,我是刑警寧澤虱咧,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站锚国,受9級特大地震影響腕巡,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜血筑,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一绘沉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧豺总,春花似錦车伞、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至表伦,卻和暖如春谦去,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蹦哼。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工鳄哭, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人纲熏。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓妆丘,卻偏偏與公主長得像锄俄,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子勺拣,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,037評論 2 355

推薦閱讀更多精彩內(nèi)容