Tensorflow之 CNN卷積神經網絡的MNIST手寫數字識別

2021-02-21 LeadAI OpenLab

點擊「閱讀原文」直接打開【北京站 | GPU CUDA 進階課程】報名連結

tensorflow中文社區對官方文檔進行了完整翻譯。鑑於官方更新不少內容,而現有的翻譯基本上都已過時。故本人對更新後文檔進行翻譯工作,紕漏之處請大家指正。(如需了解其他方面知識,可參閱以下Tensorflow系列文章)。

TensorFlow是一個非常強大的用來做大規模數值計算的庫。其所擅長的任務之一就是實現以及訓練深度神經網絡。在本教程中,通過為MNIST構建一個深度卷積神經網絡的分類器,我們將學到構建一個TensorFlow模型的基本步驟。

這個教程假設你已經熟悉神經網絡和MNIST數據集。如果你尚未了解,請查看MNIST For ML Beginners(https://www.tensorflow.org/get_started/mnist/beginners)。在學習教程之前,請確保已經安裝Install TensorFlow(https://www.tensorflow.org/install/)。

本教程第一部分為mnist_softmax.py(https://github.com/tensorflow/tensorflow/blob/r1.3/tensorflow/examples/tutorials/mnist/mnist_deep.py)做出了說明,這是一個TensorFlow模型的基本實現。而第二部分則展示了一些提高準確率的途徑。

我們將在本教程中實現:

在創建模型之前,我們會先加載MNIST數據集,然後啟動一個TensorFlow的session。

如果你打算複製、粘貼本教程的代碼,從這兩行代碼開始,這段代碼會自動下載、讀入數據集:

from tensorflow.examples.tutorials.mnist import input_datamnist = input_data.read_data_sets('MNIST_data', one_hot=True)

這裡,mnist是一個輕量級的類。它以Numpy數組的形式存儲著訓練、校驗和測試數據集。同時提供了一個函數,用於在迭代中獲得minibatch,後面我們將會用到。

2、運行TensorFlow的InteractiveSession

Tensorflow依賴於一個高效的C++後端來進行計算。與後端的這個連接叫做session。一般而言,使用TensorFlow程序的流程是先創建一個圖,然後在session中啟動它。

這裡,我們使用更加方便的InteractiveSession類。通過它,你可以更加靈活地構建你的代碼。它能讓你在運行圖的時候,插入一些computation graph(https://www.tensorflow.org/get_started/get_started#the_computational_graph),這些計算圖是由某些操作(operations)構成的。這對於工作在交互式環境中的人們來說非常便利,比如使用IPython。如果你沒有使用InteractiveSession,那麼你需要在啟動session之前構建整個計算圖,然後launching the graph(https://www.tensorflow.org/get_started/get_started#the_computational_graph)。

import tensorflow as tf
sess = tf.InteractiveSession()

為了在Python中進行高效的數值計算,我們通常會使用像NumPy一類的庫,將一些諸如矩陣乘法的耗時操作在Python環境的外部來計算,這些計算通常會通過其它語言並用更為高效的代碼來實現。

但遺憾的是,每一個操作切換回Python環境時仍需要不小的開銷。如果你想在GPU或者分布式環境中計算時,這一開銷更加糟糕,這一開銷主要可能是用來進行數據遷移。

TensorFlow也是在Python外部完成其主要工作,但是進行了改進以避免這種開銷。其並沒有採用在Python外部獨立運行某個耗時操作的方式,而是先讓我們描述一個交互操作圖,然後完全將其運行在Python外部。這與Theano或Torch的做法類似。

因此Python代碼的目的是用來構建這個可以在外部運行的計算圖,以及安排計算圖的哪一部分應該被運行。詳情請查看Getting Started With TensorFlow(https://www.tensorflow.org/get_started/get_started)中的Computation Graph(https://www.tensorflow.org/get_started/get_started#the_computational_graph)一節。

在這一節中我們將建立一個擁有一個線性層的softmax回歸模型。在下一節,我們會將其擴展為一個擁有多層卷積網絡的softmax回歸模型。

我們通過為輸入圖像和目標輸出類別創建節點,來開始構建計算圖。

x = tf.placeholder("float", shape=[None, 784])y_ = tf.placeholder("float", shape=[None, 10])

這裡的x和y並不是特定的值,相反,他們都只是一個佔位符,可以在TensorFlow運行某一計算時根據該佔位符輸入具體的值。

輸入圖片x是一個2維的浮點數張量。這裡,分配給它的shape為[None, 784],其中784是一張展平的MNIST圖片(28×28像素)的維度。None表示其值大小不定,在這裡作為第一個維度值,用以指代batch的大小,意即x的數量不定。輸出類別值y_也是一個2維張量,其中每一行為一個10維的one-hot向量,用於代表對應某一MNIST圖片的類別(0-9)。

雖然placeholder的shape參數是可選的,但有了它,TensorFlow能夠自動捕捉因數據維度不一致導致的錯誤。

我們現在為模型定義權重W和偏置b。可以將它們當作額外的輸入量,但是TensorFlow有一個更好的處理方式:變量。一個變量代表著TensorFlow計算圖中的一個值,能夠在計算過程中使用,甚至進行修改。在機器學習的應用過程中,模型參數一般用Variable來表示。

W = tf.Variable(tf.zeros([784,10]))b = tf.Variable(tf.zeros([10]))

我們在調用tf.Variable的時候傳入初始值。在這個例子裡,我們把W和b都初始化為零向量。W是一個784x10的矩陣(因為我們有784個特徵和10個輸出值)。b是一個10維的向量(因為我們有10個分類)。

變量需要通過seesion初始化後,才能在session中使用。這一初始化步驟為,為初始值指定具體值(本例當中是全為零),並將其分配給每個變量,可以一次性為所有變量完成此操作。

sess.run(tf.global_variables_initializer())

現在我們可以實現我們的回歸模型了。這只需要一行!我們把向量化後的圖片x和權重矩陣W相乘,加上偏置b。

y = tf.nn.softmax(tf.matmul(x,W) + b)

我們同樣能很容易規定損失函數。損失表明模型的預測有多糟糕;我們試著在所有樣本的訓練中最小化損失函數。在這裡,我們的損失函數是目標真實結果與應用於模型預測的softmax激活函數之間的交叉熵。在初學者教程中,我們使用了穩定的公式:

cross_entropy = tf.reduce_mean(    tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y))

注意,tf.nn.softmax_cross_entropy_with_logits將softmax應用於模型的非標準化模型預測,並對所有類別進行了求和。tf.reduce_mean對所有求和取均值。

我們已經定義好模型和訓練用的損失函數,那麼用TensorFlow進行訓練就很簡單了。因為TensorFlow知道整個計算圖,它可以使用自動微分法找到對於各個變量的損失的梯度值。TensorFlow有 built-in optimization algorithms(https://www.tensorflow.org/api_guides/python/train#optimizers)這個例子中,我們用最陡梯度下降法讓交叉熵下降,步長為0.5.

這一行代碼實際上是用來往計算圖上添加一個新操作,其中包括計算梯度,計算每個參數的步長變化,並且計算出新的參數值。

返回的train_step操作對象,在運行時會使用梯度下降來更新參數。因此,整個模型的訓練可以通過反覆地運行train_step來完成。

for i in range(1000):  batch = mnist.train.next_batch(50)  train_step.run(feed_dict={x: batch[0], y_: batch[1]})

每一步迭代,我們都會加載50個訓練樣本,然後執行一次train_step,並通過feed_dict將x 和 y_張量佔位符用訓練訓練數據替代。注意,在計算圖中,你可以用feed_dict來替代任何張量,並不僅限於替換佔位符。

那麼我們的模型性能如何呢?

首先讓我們找出那些預測正確的標籤。tf.argmax 是一個非常有用的函數,它能給出某個tensor對象在某一維上的其數據最大值所在的索引值。由於標籤向量是由0,1組成,因此最大值1所在的索引位置就是類別標籤,比如tf.argmax(y,1)返回的是模型對於任一輸入x預測到的標籤值,而 tf.argmax(y_,1) 代表正確的標籤,我們可以用 tf.equal 來檢測我們的預測是否真實標籤匹配(索引位置一樣表示匹配)。

correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))

這裡返回一個布爾數組。為了計算我們分類的準確率,我們將布爾值轉換為浮點數來代表對、錯,然後取平均值。例如:[True, False, True, True]變為[1,0,1,1],計算出平均值為0.75。

accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))

最後,我們可以計算出在測試數據上的準確率,大概是92%。

print accuracy.eval(feed_dict={x: mnist.test.images, y_: mnist.test.labels})

在MNIST上只有92%正確率,實在太糟糕。在這個小節裡,我們用一個稍微複雜的模型:卷積神經網絡來改善效果。這會達到大概99.2%的準確率。雖然不是最高,但是還是比較讓人滿意。

下面這個在TensorBoard中建立的圖,就是我們要構建的模型:


mnist_deep.png

為了創建這個模型,我們需要創建大量的權重和偏置項。這個模型中的權重在初始化時應該加入少量的噪聲來打破對稱性以及避免0梯度。由於我們使用的是ReLU神經元,因此比較好的做法是用一個較小的正數來初始化偏置項,以避免神經元節點輸出恆為0的問題(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)

TensorFlow在卷積和池化上有很強的靈活性。我們怎麼處理邊界?步長應該設多大?在這個實例裡,我們會一直使用vanilla版本。我們的卷積使用1步長(stride size),0邊距(padding size)的模板,保證輸出和輸入是同一個大小。我們的池化用簡單傳統的2x2大小的模板做max pooling。為了代碼更簡潔,我們把這部分抽象成一個函數。

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')

現在我們可以開始實現第一層了。它由一個卷積接一個max pooling完成。卷積在每個5x5的patch中算出32個特徵。卷積的權重張量形狀是[5, 5, 1, 32],前兩個維度是patch的大小,接著是輸入的通道數目,最後是輸出的通道數目。 而對於每一個輸出通道都有一個對應的偏置量。

W_conv1 = weight_variable([5, 5, 1, 32])

b_conv1 = bias_variable([32])

為了用這一層,我們把x變成一個4d向量,其第2、第3維對應圖片的寬、高,最後一維代表圖片的顏色通道數

x_image = tf.reshape(x, [-1,28,28,1])

我們把x_image和權值向量進行卷積,加上偏置項,然後應用ReLU激活函數,最後進行池化(max pooling)。max_pool_2x2 函數將圖像大小變成了14x14。

h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
h_pool1 = max_pool_2x2(h_conv1)

為了構建一個更深的網絡,我們會把幾個類似的層堆疊起來。第二層中,每個5x5的patch會得到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)

現在,圖片尺寸減小到7x7,我們加入一個有1024個神經元的全連接層,用於處理整個圖片。我們把池化層輸出的張量reshape成向量,乘上權重矩陣,加上偏置,然後對其使用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。我們用一個placeholder來代表一個神經元的輸出在dropout中保持不變的概率。這樣我們可以在訓練過程中啟用dropout,在測試過程中關閉dropout。 TensorFlow的tf.nn.dropout操作除了可以屏蔽神經元的輸出外,還會自動處理神經元輸出值的scale。所以用dropout的時候可以不用考慮scale。

keep_prob = tf.placeholder("float") h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

最後,我們添加一個softmax層,就像前面的單層softmax regression一樣。

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)

這個模型的效果如何呢?為了進行訓練和評估,我們使用與之前簡單的單層SoftMax神經網絡模型幾乎相同的一套代碼。

不同之處在於:

我們使用tf.Session,而不是tf.InteractiveSession。這樣可以更好地把創建圖形(設計模型)和評估圖形(模型擬合)區分開。它通常是為了更明了的代碼。tf.Session是在一個代碼塊(block)中創建的,因此一旦代碼塊退出,它就會自動銷毀。請注意,它進行了20,000次的訓練迭代,可能需要一段時間(可能長達半小時),這取決於您的處理器。

cross_entropy = tf.reduce_mean(    
tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y_conv))
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))
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(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}))

以上代碼,在最終測試集上的準確率大概是99.2%。


目前為止,我們已經學會了用TensorFlow快捷地搭建、訓練和評估一個相對複雜的深度學習模型。

對於這個小的卷積網絡,Dropout的存在與否對性能幾乎沒有影響。Dropout對減少過擬合一般十分有效,但但在訓練非常大的神經網絡時,它才是最有用的。

原文地址:Deep MNIST for Experts(https://www.tensorflow.org/get_started/mnist/pros) 翻譯:周乘

原文連結:http://www.jianshu.com/p/ec1de9d8cb7f

點擊「閱讀原文」直接打開【北京站 | GPU CUDA 進階課程】報名連結

相關焦點

  • 深度學習筆記13:Tensorflow實戰之手寫mnist手寫數字識別
    作者:魯偉一個數據科學踐行者的學習日記。
  • 基於TensorFlow卷積神經網絡與MNIST數據集設計手寫數字識別算法
    隨著數位化的普及和信息技術的發展,在編號處理、數量讀取、價格統計等場合,手寫數字識別系統的應用需求越來越強烈,如何將數字方便、快速地輸入到計算機中已成為關係到計算機技術普及的關鍵問題。數字識別技術的研究不僅可以解決當下面臨的數字識別問題,同時在圖像識別、機器學習等方面也有鋪墊作用。
  • 詳解與實戰TensorFlow MNIST手寫體數字識別(softmax and cnn)
    更多人關注的公眾號:機器學習算法與Python精研編輯|魚大來源|githubMNIST是一套手寫體數字的圖像數據集
  • 圖像識別 | 從手寫字識別項目入門Tensorflow
    圖像處理是深度學習裡面的一個重要的應用,深度學習的研究者使用的最熱門的框架就要屬Tensorflow了,因此我們藉由深度學習圖像處理中最基本也是最簡單的手寫字體識別項目,帶大家入門深度學習圖像處理,了解如何使用Tensorflow搭建神經網絡。
  • Python人工智慧 | 七.TensorFlow實現分類學習及MNIST手寫體識別案例
    2.MNISTMNIST是手寫體識別數據集,它是非常經典的一個神經網絡示例。MNIST圖片數據集包含了大量的數字手寫體圖片,如下圖所示,我麼可以嘗試用它進行分類實驗。二.tensorflow實現MNIST分類第一步,導入擴展包。import tensorflow as tffrom tensorflow.examples.tutorials.mnist import input_data第二步,下載數據集。
  • BP神經網絡+TensorFlow做圖片識別
    作者:zzZ_CMing博客:http://blog.csdn.net/zzz_cming作者好文推薦:CNN卷積神經網絡原理講解
  • 人工智慧TensorFlow(十五)CNN 結構(代碼實現)
    MNIST  通過上期的分享,我們了解了手寫數字識別的基本原理以及CNN卷積神經網絡的基本原理,本期我們結合MNIST數據集,來用代碼來實現CNN。(手寫數字識別是TensorFlow人工智慧最基礎的案例,這個跟學習程式語言的hello Word一樣)
  • CNN卷積神經網絡實例(基於pytorch)
    1.關於卷積神經網絡卷積神經網絡(Convolutional Neural Network,CNN) 是一種具有局部連接、權重共享等特徵的深層前饋神經網絡
  • 100天搞定機器學習|day39 Tensorflow Keras手寫數字識別
    TensorFlow 最初由Google大腦小組(隸屬於Google機器智能研究機構)的研究員和工程師們開發出來,用於機器學習和深度神經網絡方面的研究,但這個系統的通用性使其也可廣泛用於其他計算領域。1、安裝庫tensorflow有些教程會推薦安裝nightly,它適用於在一個全新的環境下進行TensorFlow的安裝,默認會把需要依賴的庫也一起裝上。
  • 如何從Tensorflow中創建CNN,並在GPU上運行該模型(附代碼)
    在本教程中,您將學習卷積神經網絡(CNN)的架構,如何在Tensorflow中創建CNN,並為圖像標籤提供預測。
  • 卷積神經網絡及Python代碼實現
    blogId=97卷積神經網絡(Convolutional Neural Network,CNN)是將二維離散卷積運算和人工神經網絡相結合的一種深度神經網絡。它的特點是可以自動提取特徵。有關卷積神經網絡的數學原理和訓練過程請見我的另一篇文章《機器學習教程 十五-細解卷積神經網絡》。
  • 連載 | Tensorflow教程一:30行代碼搞定手寫識別
    為了讓大家在一開始就看到一個美好的場景,而不是停留在漫長的基礎知識積累上,參考網上的一些教程,我們直接一開始就直接展示用tensorflow實現MNIST手寫識別的例子。然後基礎知識我們再慢慢講。線性回歸不過癮,我們直接一步到位,開始進行手寫識別。
  • 使用tensorflow layers相關API快速構建卷積神經網絡
    微信公眾號:OpenCV學堂關注獲取更多計算機視覺與深度學習知識覺得文章對你有用,請戳底部廣告支持Layers API介紹tf.layers包中包含了CNN卷積神經網絡的大多數層類型,當前封裝支持的層包括:卷積層均值池化層最大池化層扁平層密集層dropout層BN層轉置卷積層我們將基於卷積層、池化層、扁平層與密集層構建一個簡單網絡模型,實現手寫數字識別mnist數據集的訓練。
  • 機器學習進階筆記| 利用TensorFlow實現智能數字識別
    Mnist數字識別項目介紹2. 準備工作申請一臺UCloud雲主機配置Tensorflow環境3. 編寫第一個Tensorflow項目——Mnist數字識別Mnist數字識別介紹人工智慧技術已經逐漸滲透到大眾生活的方方面面,從大名鼎鼎的AlphaGo,再到貼近實際生活的廣告展示、新聞智能推薦等,人工智慧技術被廣泛運用於各行各業。
  • 使用Python+Tensorflow的CNN技術快速識別驗證碼
    目前,在圖像識別和視覺分析研究中,卷積神經網絡(CNN)技術的使用越來越多。Tensorflow 是由 Google 團隊開發的神經網絡模塊,短短幾年間, 就已經有很多次版本的更新。最近我也在自學Tensorflow,想通過卷積神經網絡快速識別整塊驗證碼(不分割字符)。期間也碰到許多問題,諸如軟體安裝,Tensorflow版本差異等。
  • 機器之心GitHub項目:從零開始用TensorFlow搭建卷積神經網絡
    所有的代碼和運行結果都已上傳至 Github,機器之心希望通過我們的試驗提供精確的代碼和運行經驗,我們將持續試驗這一類高質量的教程和代碼。機器之心項目地址:https://github.com/jiqizhixin/ML-Tutorial-Experiment本文的重點是實現,並不會從理論和概念上詳細解釋深度神經網絡、卷積神經網絡、最優化方法等基本內容。
  • 機器學習|卷積神經網絡(CNN) 手寫體識別 (MNIST)入門
    所以文檔後面介紹的都是關於監督學習,因為手寫體識別需要有一些訓練集告訴我這些圖像實際上應該是什麼數字,不過監督學習的方法也有很多,主要有分類和回歸兩大類:分類 (Classification): 例如手寫體識別,這類問題的特點在於最後的結果是離散的,最後分類的數字只能是 0, 1, 2, 3 而不會是 1.414, 1.732 這樣的小數。
  • TensorFlow 實現流行的機器學習算法的教程匯集
    MNIST 是一個手寫數字的資料庫,查看這個筆記了解關於該數據集的描述: https://github.com/aymericdamien/TensorFlow-Examples/blob/master/notebooks/0_Prerequisite/mnist_dataset_intro.ipynb官方網站: http://yann.lecun.com/exdb
  • 深度學習筆記14:CNN經典論文研讀之Le-Net5及其Tensorflow實現
    作者:魯偉一個數據科學踐行者的學習日記。
  • CNN卷積神經網絡原理講解+圖片識別應用(附源碼)
    而機器識圖的方式正好和繡十字繡的方式相反,現在有了一幅圖片,機器通過識別圖片中每個格子(像素點)上的顏色,將每個格子裡的顏色都用數字類型存儲,得到一張很大的數字矩陣,圖片信息也就存儲在這張數字矩陣中。