JB的Python之旅-人工智慧篇-TensorFlow-“Hello world”

jb發表於2018-06-19

前言

通過上文介紹,主要對人工智慧等知識進行了掃盲,並且對TensorFlow基本用法有了解,記住是沒可能的,只能說掃盲,後面需要用的時候再回頭看;
上文連結
介紹完基礎知識,一般來說,都需要寫一個hello world,但對於TensorFlow來說,並非真的是輸出hello world,單純輸出hello world在上篇文章也有提及到:

import tensorflow as tf
import os
os.environ["TF_CPP_MIN_LOG_LEVEL"]='2'

hello = tf.constant("Hello,TensorFlow")
#建立了圖,裡面放入hello,TensorFlow
sess = tf.Session()
#定義了一個會話
print(sess.run(hello))
#執行圖計算
複製程式碼

如果僅僅是print,跟機器學習扯不到邊,那機器學習的hello world是什麼呢?是MNIST手寫體識別

MNIST資料集介紹

MNIST是一個入門級的計算機視覺資料集,它包含各種手寫數字圖片:

JB的Python之旅-人工智慧篇-TensorFlow-“Hello world”

MNIST資料集是含標註資訊的,以上圖片分別代表5, 0, 4和1。
MNIST資料集的官網是Yann LeCun's website

目的

訓練一個機器學習模型用於預測圖片裡面的數字

MNIST 資料下載

手動下載

直接點選上面提及到的MNIST官網:http://yann.lecun.com/exdb/mnist/

JB的Python之旅-人工智慧篇-TensorFlow-“Hello world”
把這4個內容下載即可,聽說需要翻牆,因此這裡直接提供連結給需要的同學下載:
連結:https://pan.baidu.com/s/1o9Ghq8gB6ywRuHozJBv7kg 密碼:mcab

下載完後,不需要解壓,直接放到一個目錄即可

自動下載

首先貼出github地址:https://github.com/tensorflow/tensorflow/tree/master/tensorflow/examples/tutorials/mnist

新建一個input_data.py的檔案:

# Copyright 2015 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================

"""Functions for downloading and reading MNIST data."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import gzip
import os
import tempfile

import numpy
from six.moves import urllib
from six.moves import xrange  # pylint: disable=redefined-builtin
import tensorflow as tf
from tensorflow.contrib.learn.python.learn.datasets.mnist import read_data_sets
複製程式碼

使用的時候,新建一個py檔案,比如test.py,與input_data.py同一級目錄下,然後執行下面的命令來下載mnist資料集,這樣就會生成要給data資料夾:

#下載用於訓練和測試的mnist資料集的原始碼

import input_data
# 呼叫input_data
mnist = input_data.read_data_sets('data/', one_hot=True)
print(mnist)
複製程式碼

執行後看到有這幾句就行了,雖然有報錯,但是不理會,能正常下載就行了

JB的Python之旅-人工智慧篇-TensorFlow-“Hello world”

JB的Python之旅-人工智慧篇-TensorFlow-“Hello world”

檔案 內容
train-images-idx3-ubyte.gz 訓練集圖片 - 55000 張 訓練圖片, 5000 張 驗證圖片
train-labels-idx1-ubyte.gz 訓練集圖片對應的數字標籤
t10k-images-idx3-ubyte.gz 測試集圖片 - 10000 張 圖片
t10k-labels-idx1-ubyte.gz 測試集圖片對應的數字標籤

載下來的資料集被分成兩部分:
60000行的訓練資料集和10000行的測試資料集

一般來說,訓練資料集是用來訓練模型,驗證資料集可以檢驗所訓練出來的模型的正確性和是否過擬合,測試集是不可見的(相當於一個黑盒),
但我們最終的目的是使得所訓練出來的模型在測試集上的效果(這裡是準確性)達到最佳。

每一個MNIST資料單元有兩部分組成:
一張包含手寫數字的圖片和一個對應的標籤
一般把這些圖片設為“xs”,把這些標籤設為“ys”。訓練資料集和測試資料集都包含xs和ys,
比如訓練資料集的圖片是 mnist.train.images ,訓練資料集的標籤是 mnist.train.labels。

每一張圖片是一個28*28的畫素點矩陣,我們可以用一個同大小的二維整數矩陣來表示。如下:

JB的Python之旅-人工智慧篇-TensorFlow-“Hello world”

把這個陣列展開成一個向量,長度是 28x28 = 784。如何展開這個陣列(數字間的順序)不重要,只要保持各個圖片採用相同的方式展開。

在MNIST訓練資料集中,mnist.train.images 是一個形狀為 [60000, 784] 的張量,
第一個維度表示圖片的索引第二個維度表示圖片中畫素的索引,值介於0和1之間。

JB的Python之旅-人工智慧篇-TensorFlow-“Hello world”

相對應的MNIST資料集的標籤是介於0到9的數字,用來描述給定圖片裡表示的數字。
在這裡,我們用"one-hot vectors"來表述label的值。
一個one-hot向量除了某一位的數字是1以外其餘各維度數字都是0。 vector的長度為label值的數目,vector中有且只有一位為1,其他為0,表示某個數字時在vector中所對應的索引位置設定1
比如,[0,0,0,1,0,0,0,0,0,0]來表示3,[1,0,0,0,0,0,0,0,0,0,0]來表示0;
因此得出 mnist.train.labels 是一個 [60000, 10] 的數字矩陣;

JB的Python之旅-人工智慧篇-TensorFlow-“Hello world”

以上是MNIST資料集的描述及TensorFlow中表示。下面介紹Softmax Regression模型。

Softmax Regression模型

我們知道MNIST的每一張圖片都表示一個數字,從0到9。我們希望得到給定圖片代表每個數字的概率。

比如說,我們的模型可能推測一張包含9的圖片代表數字9的概率是80%,但是判斷它是8的概率是15%(因為8和9都有上半部分的小圓),判斷它是6的概率是5%,然後給予它代表其他數字的概率更小的值。

Softmax Regression是一個簡單的模型,很適合用來處理得到一個待分類物件在多個類別上的概率分佈。所以,這個模型通常是很多高階模型的最後一步。

Softmax Regression大致分為兩步:

Step 1: add up the evidence of our input being in certain classes;
Step 2: convert that evidence into probabilities.
複製程式碼

為了得到一張給定圖片屬於某個特定數字類的證據(evidence),我們對圖片畫素值進行加權求和。
如果這個畫素具有很強的證據說明這張圖片不屬於該類,那麼相應的權值為負數,相反如果這個畫素擁有有利的證據支援這張圖片屬於這個類,那麼權值是正數。
下面是一個直觀的例子,圖片中藍色表示正值,紅色表示負值(藍色區域的形狀趨向於數字形狀):

JB的Python之旅-人工智慧篇-TensorFlow-“Hello world”
我們也需要加入一個額外的偏置量(bias),因為輸入往往會帶有一些無關的干擾量。因此對於給定的輸入圖片 x 它代表的是數字 i 的證據可以表示為:

JB的Python之旅-人工智慧篇-TensorFlow-“Hello world”
其中,Wi 和 bi 分別為類別 i 的權值和偏置,j 是輸入圖片 x 的畫素索引。
然後,我們將得到的evidence值通過一個”softmax”函式轉化為概率值 y :

JB的Python之旅-人工智慧篇-TensorFlow-“Hello world”
這裡softmax函式的作用相當於是一個轉換函式,它的作用是將原始的線性函式輸出結果以某種方式轉換為我們需要的值,這裡我們需要0-9十個類別上的概率分佈。softmax函式的定義如下:
JB的Python之旅-人工智慧篇-TensorFlow-“Hello world”
展開等式右邊的子式,可以得到:
JB的Python之旅-人工智慧篇-TensorFlow-“Hello world”
這裡的softmax函式能夠得到類別上的概率值分佈,並保證所有類別上的概率值之和為1. 下面的圖示將有助於你理解softmax函式的計算過程:

JB的Python之旅-人工智慧篇-TensorFlow-“Hello world”
如果將這個過程公式化,將得到:
JB的Python之旅-人工智慧篇-TensorFlow-“Hello world”
如們將這個過程公式化,將得到:

JB的Python之旅-人工智慧篇-TensorFlow-“Hello world”
也可以簡化成:

JB的Python之旅-人工智慧篇-TensorFlow-“Hello world”

看完之後,是不是覺得不明覺厲?開頭可能還能理解,但是後面搬出公式了,是不是覺得莫名其妙了?沒錯,就是要這種感覺,那繼續吧~

實現迴歸模型

為了在Python中進行科學計算工作,我們常常使用一些獨立庫函式包,例如NumPy來實現複雜的矩陣計算。但是由於Python的執行效率並不夠快,所以常常用一些更加高效的語言來實現。但是,這樣做會帶來語言轉換(例如轉換回python操作)的開銷。

TensorFlow在這方面做了一些優化,可以對你所描述的一系列的互動計算的流程完全獨立於Python之外,從而避免了語言切換的開銷。

為了使用TensorFlow,我們需要引用該庫函式:

import tensorflow as tf
複製程式碼

利用一些符號變數來描述互動計算的過程,建立如下:

x = tf.placeholder(tf.float32, [None, 784])
複製程式碼

說明下,placeholder函式可以理解為形參,用於定義過程,在執行的時候再賦具體的值;

這裡的 x 不是一個特定的值,而是一個佔位符,即需要時指定。
我們在TensorFlow執行計算時輸入這個值。我們希望能夠輸入任意數量的MNIST影象,每一張圖展平成784維的向量。我們用2維的浮點數張量來表示這些圖,這個張量的形狀是[None,784(28*28) ]。(這裡的None表示此張量的第一個維度可以是任何長度的。)

使用Variable(變數)來表示模型中的權值和偏置,這些引數是可變的。如下:

W = tf.Variable(tf.zeros([784, 10]))
b = tf.Variable(tf.zeros([10]))
複製程式碼

說明下,zeros是建立一個所有的引數為0的tensor物件;
這裡的W和b均被初始化為0值矩陣。W的維數為784 * 10,是因為我們需要將一個784維的畫素值經過相應的權值之乘轉化為10個類別上的evidence值;b是十個類別上累加的偏置值。

現在,實現softmax regression模型僅需要一行程式碼:

y = tf.nn.softmax(tf.matmul(x,W) + b)
複製程式碼

其中,matmul函式實現了 x 和 W 的乘積,這裡 x 為二維矩陣,所以放在前面。可以看出,在TensorFlow中實現softmax regression模型是很簡單的。

訓練模型

為了訓練模型,首先需要定義一個指標來評估這個模型是好的。
其實,在機器學習,通常定義指標來表示一個模型是壞的,這個指標稱為成本(cost)或損失(loss),然後儘量最小化這個指標。但是,這兩種方式是相同的。

一個非常常見的,非常漂亮的成本函式是“交叉熵”(cross-entropy)。交叉熵產生於資訊理論裡面的資訊壓縮編碼技術,但是它後來演變成為從博弈論到機器學習等其他領域裡的重要技術手段。它的定義如下:

JB的Python之旅-人工智慧篇-TensorFlow-“Hello world”

這裡 y 是所預測的概率分佈,而 y′ 是真實的分佈(one-hot vector表示的圖片label)。
直觀上,交叉熵函式的輸出值表示了預測的概率分佈與真實的分佈的符合程度。
如果想更加深入地理解交叉熵函式,可參考這篇博文

為了實現交叉熵函式,我們需要先設定一個佔位符在存放圖片的正確label值,

y_ = tf.placeholder("float", [None,10])
複製程式碼

然後可以用−∑y′log(y),計算交叉熵:

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)
複製程式碼

在這裡,使用了一個學習率為0.01的梯度下降演算法來最小化代價函式。梯度下降是一個簡單的計算方式,即使得變數值朝著減小代價函式值的方向變化。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個隨機資料,這種操作成為批處理(batch)。
然後,每次執行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))
複製程式碼

correct_prediction是一個布林值的列表,例如 [True, False, True, True]。
可以使用tf.cast()函式將其轉換為[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}))
複製程式碼

Softmax regression模型由於模型較簡單,所以在測試集上的準確率在91%左右,這個結果並不算太好。
通過一些簡單的優化,準確率可以達到97%,目前最好的模型的準確率為99.7%。(這裡有眾多模型在MNIST資料集上的執行結果)。

原始碼

利用Softmax模型實現手寫體識別的完整程式碼如下:
完善了程式碼的註釋,以便閱讀起來更好理解,同時增加輸出訓練過程:

import input_data
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data

mnist = input_data.read_data_sets("data/", one_hot=True)
print("Download Done!")



# 設定權重weights和偏置biases作為優化變數,初始值設為0
weights = tf.Variable(tf.zeros([784, 10]))
biases = tf.Variable(tf.zeros([10]))

# 構建模型
x = tf.placeholder("float", [None, 784])
# 模型的預測值
y = tf.nn.softmax(tf.matmul(x, weights) + biases)     
# 真實值
y_real = tf.placeholder("float", [None, 10])                                        

# 預測值與真實值的交叉熵
cross_entropy = -tf.reduce_sum(y_real * tf.log(y))   
# 使用梯度下降優化器最小化交叉熵
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)     

# 比較預測值和真實值是否一致
correct_prediction = tf.equal(tf.argmax(y, 1), tf.arg_max(y_real, 1)) 
 # 統計預測正確的個數,取均值得到準確率
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float")) 

# 開始訓練
init = tf.initialize_all_variables()
sess = tf.Session()
sess.run(init)
for i in range(5000):
    # 每次隨機選取100個資料進行訓練,即所謂的隨機梯度下降
    batch_xs, batch_ys = mnist.train.next_batch(100)                                 
    # 正式執行train_step,用feed_dict的資料取代placeholder
    sess.run(train_step, feed_dict={x: batch_xs, y_real:batch_ys})                  
    if i % 100 == 0:
        # 每訓練100次後評估模型
        print("Step " + str(i) +",Training Accuracy "+ str(sess.run(accuracy, feed_dict={x: mnist.test.images, y_real: mnist.test.labels})))
print("Accuarcy on Test-dataset: ", sess.run(accuracy, feed_dict={x: mnist.test.images, y_real: mnist.test.labels}))
複製程式碼

最後執行的結果:

JB的Python之旅-人工智慧篇-TensorFlow-“Hello world”

多次執行,會發現每次都不一樣,但基本都是在91%

JB的Python之旅-人工智慧篇-TensorFlow-“Hello world”

如果把訓練次數增加到10W次,會發現成功率在98%

JB的Python之旅-人工智慧篇-TensorFlow-“Hello world”

訓練過程截圖:

JB的Python之旅-人工智慧篇-TensorFlow-“Hello world”
嗯,TensorFlow的hello world就是這樣,花了不少時間介紹Softmax模型,尤其是公式那塊看的不懂,但感覺,數學在機器學習裡面真的很重要~

小結:

本文使用Softmax Regression模型對MNIST資料集進行訓練,主要圍繞Softmax模型的原理(看不懂篇)以及怎麼在TensorFlow裡面使用這個模型,更重要的事,從這個模型學習到的設計思想~

謝謝大家~

JB的Python之旅-人工智慧篇-TensorFlow-“Hello world”

相關文章