閱讀154 返回首頁    go 阿裏雲 go 技術社區[雲棲]


TensorFlow教程之完整教程 2.4 MNIST進階

本文檔為TensorFlow參考文檔,本轉載已得到TensorFlow中文社區授權。


深入MNIST

TensorFlow是一個非常強大的用來做大規模數值計算的庫。其所擅長的任務之一就是實現以及訓練深度神經網絡。

在本教程中,我們將學到構建一個TensorFlow模型的基本步驟,並將通過這些步驟為MNIST構建一個深度卷積神經網絡。

這個教程假設你已經熟悉神經網絡和MNIST數據集。

安裝

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

加載MNIST數據

為了方便起見,我們已經準備了一個腳本來自動下載和導入MNIST數據集。它會自動創建一個'MNIST_data'的目錄來存儲數據。

import input_data
mnist = input_data.read_data_sets('MNIST_data', one_hot=True)

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

運行TensorFlow的InteractiveSession

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

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

import tensorflow as tf
sess = tf.InteractiveSession()

計算圖

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

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

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

因此Python代碼的目的是用來構建這個可以在外部運行的計算圖,以及安排計算圖的哪一部分應該被運行。詳情請查看基本用法中的計算圖表一節。

構建Softmax 回歸模型

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

占位符

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

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

這裏的xy並不是特定的值,相反,他們都隻是一個占位符,可以在TensorFlow運行某一計算時根據該占位符輸入具體的值。

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

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

變量

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

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

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

Before Variables can be used within a session, they must be initialized using that session. This step takes the initial values (in this case tensors full of zeros) that have already been specified, and assigns them to each Variable. This can be done for all Variables at once.

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

sess.run(tf.initialize_all_variables())

類別預測與損失函數

現在我們可以實現我們的回歸模型了。這隻需要一行!我們把向量化後的圖片x和權重矩陣W相乘,加上偏置b,然後計算每個分類的softmax概率值。

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

可以很容易的為訓練過程指定最小化誤差用的損失函數,我們的損失函數是目標類別和預測類別之間的交叉熵。

cross_entropy = -tf.reduce_sum(y_*tf.log(y))

注意,tf.reduce_sum把minibatch裏的每張圖片的交叉熵值都加起來了。我們計算的交叉熵是指整個minibatch的。

訓練模型

我們已經定義好模型和訓練用的損失函數,那麼用TensorFlow進行訓練就很簡單了。因為TensorFlow知道整個計算圖,它可以使用自動微分法找到對於各個變量的損失的梯度值。TensorFlow有大量內置的優化算法 這個例子中,我們用最速下降法讓交叉熵下降,步長為0.01.

train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)

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

返回的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_dictx 和 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"))

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

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

構建一個多層卷積網絡

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

權重初始化

為了創建這個模型,我們需要創建大量的權重和偏置項。這個模型中的權重在初始化時應該加入少量的噪聲來打破對稱性以及避免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維對應圖片的寬、高,最後一維代表圖片的顏色通道數(因為是灰度圖所以這裏的通道數為1,如果是rgb彩色圖,則為3)。

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

We then convolve x_image with the weight tensor, add the bias, apply the ReLU function, and finally max pool. 我們把x_image和權值向量進行卷積,加上偏置項,然後應用ReLU激活函數,最後進行max pooling。

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

為了減少過擬合,我們在輸出層之前加入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神經網絡模型幾乎相同的一套代碼,隻是我們會用更加複雜的ADAM優化器來做梯度最速下降,在feed_dict中加入額外的參數keep_prob來控製dropout比例。然後每100次迭代輸出一次日誌。

cross_entropy = -tf.reduce_sum(y_*tf.log(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, "float"))
sess.run(tf.initialize_all_variables())
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快捷地搭建、訓練和評估一個複雜一點兒的深度學習模型。

最後更新:2017-08-22 15:34:01

  上一篇:go  Check Mysql Status
  下一篇:go  阿裏雲服務器優惠券分享,阿裏雲幸運券免費領取