標籤:
實現迴歸模型
為了用python實現高效的數值計算,我們通常會使用函數庫,比如NumPy,會把類似矩陣乘法這樣的複雜運算使用其他外部語言實現。不幸的是,從外部計算切換回Python的每一個操作,仍然是一個很大的開銷。如果你用GPU來進行外部計算,這樣的開銷會更大。用分布式的計算方式,也會花費更多的資源用來傳輸資料。
TensorFlow也把複雜的計算放在python之外完成,但是為了避免前面說的那些開銷,它做了進一步完善。Tensorflow不單獨地運行單一的複雜計算,而是讓我們可以先用圖描述一系列可互動的計算操作,然後全部一起在Python之外運行。(這樣類似的運行方式,可以在不少的機器學習庫中看到。)
使用TensorFlow之前,首先匯入它:
import tensorflow as tf
我們通過操作符號變數來描述這些可互動的操作單元,可以用下面的方式建立一個:
x = tf.placeholder("float", [None, 784])x不是一個特定的值,而是一個預留位置placeholder,我們在TensorFlow運行計算時輸入這個值。我們希望能夠輸入任意數量的MNIST映像,每一張圖展平成784維的向量。我們用2維的浮點數張量來表示這些圖,這個張量的形狀是[None,784 ]。(這裡的None表示此張量的第一個維度可以是任何長度的。)
我們的模型也需要權重值和偏置量,當然我們可以把它們當做是另外的輸入(使用預留位置),但TensorFlow有一個更好的方法來表示它們:Variable 。 一個Variable代表一個可修改的張量,存在在TensorFlow的用於描述互動性操作的圖中。它們可以用於計算輸入值,也可以在計算中被修改。對於各種機器學習應用,一般都會有模型參數,可以用Variable表示。
W = tf.Variable(tf.zeros([784,10]))b = tf.Variable(tf.zeros([10]))
我們賦予tf.Variable不同的初值來建立不同的Variable:在這裡,我們都用全為零的張量來初始化W和b。因為我們要學習W和b的值,它們的初值可以隨意設定。
注意,W的維度是[784,10],因為我們想要用784維的圖片向量乘以它以得到一個10維的證據值向量,每一位對應不同數字類。b的形狀是[10],所以我們可以直接把它加到輸出上面。
現在,我們可以實現我們的模型啦。只需要一行代碼!
y = tf.nn.softmax(tf.matmul(x,W) + b)
首先,我們用tf.matmul(x,W)表示x乘以W,對應之前等式裡面的,這裡x是一個2維張量擁有多個輸入。然後再加上b,把和輸入到tf.nn.softmax函數裡面。
至此,我們先用了幾行簡短的代碼來設定變數,然後只用了一行代碼來定義我們的模型。TensorFlow不僅僅可以使softmax迴歸模型計算變得特別簡單,它也用這種非常靈活的方式來描述其他各種數值計算,從機器學習模型對物理學類比模擬模型。一旦被定義好之後,我們的模型就可以在不同的裝置上運行:電腦的CPU,GPU,甚至是手機!
訓練模型
為了訓練我們的模型,我們首先需要定義一個指標來評估這個模型是好的。其實,在機器學習,我們通常定義指標來表示一個模型是壞的,這個指標稱為成本(cost)或損失(loss),然後盡量最小化這個指標。但是,這兩種方式是相同的。
一個非常常見的,非常漂亮的成本函數是“交叉熵”(cross-entropy)。交叉熵產生於資訊理論裡面的資訊壓縮編碼技術,但是它後來演變成為從博弈論到機器學習等其他領域裡的重要技術手段。它的定義如下:
y 是我們預測的機率分布, y‘ 是實際的分布(我們輸入的one-hot vector)。比較粗糙的理解是,交叉熵是用來衡量我們的預測用於描述真相的低效性。更詳細的關於交叉熵的解釋超出本教程的範疇,但是你很有必要好好理解它。
為了計算交叉熵,我們首先需要添加一個新的預留位置用於輸入正確值:
y_ = tf.placeholder("float", [None,10])
然後我們可以計算交叉熵:
cross_entropy = -tf.reduce_sum(y_*tf.log(y))
首先,用 tf.log 計算 y 的每個元素的對數。接下來,我們把 y_ 的每一個元素和 tf.log(y) 的對應元素相乘。最後,用 tf.reduce_sum 計算張量的所有元素的總和。(注意,這裡的交叉熵不僅僅用來衡量單一的一對預測和真實值,而是所有100幅圖片的交叉熵的總和。對於100個資料點的預測表現比單一資料點的表現能更好地描述我們的模型的效能。
現在我們知道我們需要我們的模型做什麼啦,用TensorFlow來訓練它是非常容易的。因為TensorFlow擁有一張描述你各個計算單元的圖,它可以自動地使用反向傳播演算法(backpropagation algorithm)來有效地確定你的變數是如何影響你想要最小化的那個成本值的。然後,TensorFlow會用你選擇的最佳化演算法來不斷地修改變數以降低成本。
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)
在這裡,我們要求TensorFlow用梯度下降演算法(gradient descent algorithm)以0.01的學習速率最小化交叉熵。梯度下降演算法(gradient descent algorithm)是一個簡單的學習過程,TensorFlow只需將每個變數一點點地往使成本不斷降低的方向移動。當然TensorFlow也提供了其他許多最佳化演算法:只要簡單地調整一行代碼就可以使用其他的演算法。
TensorFlow在這裡實際上所做的是,它會在後台給描述你的計算的那張圖裡面增加一系列新的計算操作單元用於實現反向傳播演算法和梯度下降演算法。然後,它返回給你的只是一個單一的操作,當運行這個操作時,它用梯度下降演算法訓練你的模型,微調你的變數,不斷減少成本。
現在,我們已經設定好了我們的模型。在運行計算之前,我們需要添加一個操作來初始化我們建立的變數:
init = tf.initialize_all_variables()
現在我們可以在一個Session裡面啟動我們的模型,並且初始設定變數:
sess = tf.Session()sess.run(init)
然後開始訓練模型,這裡我們讓模型循環訓練1000次!
for i in range(1000): batch_xs, batch_ys = mnist.train.next_batch(100) sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})
該迴圈的每個步驟中,我們都會隨機抓取訓練資料中的100個批處理資料點,然後我們用這些資料點作為參數替換之前的預留位置來運行train_step。
使用一小部分的隨機資料來進行訓練被稱為隨機訓練(stochastic training)- 在這裡更確切的說是隨機梯度下降訓練。在理想情況下,我們希望用我們所有的資料來進行每一步的訓練,因為這能給我們更好的訓練結果,但顯然這需要很大的計算開銷。所以,每一次訓練我們可以使用不同的資料子集,這樣做既可以減少計算開銷,又可以最大化地學習到資料集的總體特性。
評估我們的模型
那麼我們的模型效能如何呢?
首先讓我們找出那些預測正確的標籤。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"))
最後,我們計算所學習到的模型在測試資料集上面的正確率。
print sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels})
這個最終結果值應該大約是91%。
這個結果好嗎?嗯,並不太好。事實上,這個結果是很差的。這是因為我們僅僅使用了一個非常簡單的模型。不過,做一些小小的改進,我們就可以得到97%的正確率。最好的模型甚至可以獲得超過99.7%的準確率!(想瞭解更多資訊,可以看看這個關於各種模型的效能對比列表。)
比結果更重要的是,我們從這個模型中學習到的設計思想。不過,如果你仍然對這裡的結果有點失望,可以查看下一個教程,在那裡你可以學習如何用TensorFlow構建更加複雜的模型以獲得更好的效能!
TensorFlow教程03:針對機器學習初學者的MNIST實驗——迴歸的實現、訓練和模型評估