人人均可以學的人工智能:TensorFlow 入門例子

這是用 TensorFlow 來識別手寫數字的官方經典入門例子,數據都是已經處理過準備好了的,可是隻到計算準確度機率那就停了,缺乏拿實際圖片運用的例子,初學者看完以後不免發矇。因而,本文第二段用一些實際圖片來驗證咱們的模型。文中例子基於 TensorFlow 1.0.0,看過官方文檔的直接跳到後面吧。javascript

第一部分,介紹了一些處理數據的基本知識,而後採用一個簡單的模型,用一堆準確的數據去訓練它,訓練完以後拿另外一堆數據去評估一下這個模型的準確率(這也是官方例子的內容)。搞清楚這個很重要,否則看完官方例子只會以爲很厲害,可是又不知道哪厲害。html

第二部分,咱們會拿幾個圖片,告訴模型咱們認爲這個圖片是幾(固然,這個是隨便說的),而後模型告訴咱們它以爲的和咱們認爲的是否一致。java

有不少名詞和數學算法不懂不要緊,慢慢查,先跑個例子感覺一下。python

文中部分圖片來自官方文檔。git

識別手寫圖片

由於這個例子是 TensorFlow 官方的例子,不會說的太詳細,會加入了一點我的的理解,英文文檔 是最新的,中文文檔 是用 0.5 版本的 TensorFlow,在 1.0 版本跑不起來,建議中文文檔和英文文檔交叉着看,有助於理解。程序員

準備數據

這裏用來識別的手寫圖片大體是這樣的,爲了下降複雜度,每一個圖片是 28*28 大小。github

可是直接丟圖片給咱們的模型,模型是不認識的,因此必需要對圖片進行一些處理。web

若是瞭解線性代數,大概知道圖片的每一個像素點其實能夠表示爲一個二維的矩陣,對圖片作各類變換,好比翻轉啊什麼的就是對這個矩陣進行運算,因而咱們的手寫圖片大概能夠當作是這樣的:算法

這個矩陣展開成一個向量,長度是 28*28=784。咱們還須要另外一個東西用來告訴模型咱們認爲這個圖片是幾,也就是給圖片打個 label。這個 label 也不是隨便打的,這裏用一個相似有 10 個元素的數組,其中只有一個是 1,其它都是 0,哪位爲 1 表示對應的圖片是幾,例如表示數字 8 的標籤值就是 ([0,0,0,0,0,0,0,0,1,0])。數組

這些就是單張圖片的數據處理,實際上爲了高效的訓練模型,會把圖片數據和 label 數據分別打包到一塊兒,也就是 MNIST 數據集了。

MNIST數據集

MNIST 數據集是一個入門級的計算機視覺數據集,官網是Yann LeCun's website。 咱們不須要手動去下載這個數據集, 1.0 的 TensorFlow 會自動下載。

這個訓練數據集有 55000 個圖片數據,用張量的方式組織的,形狀如 [55000,784],以下圖:

還記得爲啥是 784 嗎,由於 28*28 的圖片。
label 也是如此,[55000,10]:

這個數據集裏面除了有訓練用的數據以外,還有 10000 個測試模型準確度的數據。

整個數據集大概是這樣的:

如今數據有了,來看下咱們的模型。

Softmax 迴歸模型

Softmax 中文名叫歸一化指數函數,這個模型能夠用來給不一樣的對象分配機率。好比判斷

的時候可能認爲有 80% 是 9,有 5% 認爲多是 8,由於上面都有個圈。

咱們對圖片像素值進行加權求和。好比某個像素具備很強的證聽說明這個圖不是 1,則這個像素相應的權值爲負數,相反,若是這個像素特別有利,則權值爲正數。

以下圖,紅色區域表明負數權值,藍色表明正數權值。

同時,還有一個偏置量(bias) 用來減少一些無關的干擾量。

Softmax 迴歸模型的原理大概就是這樣,更多的推導過程,能夠查閱一下官方文檔,有比較詳細的內容。

說了那麼久,終於能夠上代碼了。

訓練模型

具體引入的一些包這裏就不一一列出來,主要是兩個,一個是 tensorflow 自己,另外一個是官方例子裏面用來輸入數據用的方法。

from tensorflow.examples.tutorials.mnist import input_data
import tensorflow as tf複製代碼

以後就能夠創建咱們的模型。

# Create the model
  x = tf.placeholder(tf.float32, [None, 784])
  W = tf.Variable(tf.zeros([784, 10]))
  b = tf.Variable(tf.zeros([10]))
  y = tf.matmul(x, W) + b複製代碼

這裏的代碼都是相似佔位符,要填了數據纔有用。

  • x 是從圖片數據文件裏面讀來的,理解爲一個常量,一個輸入值,由於是 28*28 的圖片,因此這裏是 784;
  • W 表明權重,由於有 784 個點,而後有 10 個數字的權重,因此是 [784, 10],模型運算過程當中會不斷調整這個值,能夠理解爲一個變量;
  • b 表明偏置量,每一個數字的偏置量都不一樣,因此這裏是 10,模型運算過程當中也會不斷調整這個值,也是一個變量;
  • y 基於前面的數據矩陣乘積計算。

tf.zeros 表示初始化爲 0。

咱們會須要一個東西來接受正確的輸入,也就是放訓練時準確的 label。

# Define loss and optimizer
  y_ = tf.placeholder(tf.float32, [None, 10])複製代碼

咱們會用一個叫交叉熵的東西來衡量咱們的預測的「驚訝」程度。

關於交叉熵,舉個例子,咱們日常寫代碼的時候,一按編譯,一切順利,程序跑起來了,咱們就沒那麼「驚訝」,由於咱們的代碼是那麼的優秀;而若是一按編譯,整個就 Crash 了,咱們很「驚訝」,一臉蒙逼的想,這怎麼可能。

交叉熵感性的認識就是表達這個的,當輸出的值和咱們的指望是一致的時候,咱們就「驚訝」值就比較低,當輸出值不是咱們指望的時候,「驚訝」值就比較高。

cross_entropy = tf.reduce_mean(
      tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y))複製代碼

這裏就用了 TensorFlow 實現的 softmax 模型來計算交叉熵。
交叉熵,就是咱們想要儘可能優化的值,讓結果符合預期,不要讓咱們太「驚訝」。

TensorFlow 會自動使用反向傳播算法(backpropagation algorithm) 來有效的肯定變量是如何影響你想最小化的交叉熵。而後 TensorFlow 會用你選擇的優化算法來不斷地修改變量以下降交叉熵。

train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)複製代碼

這裏用了梯度降低算法(gradient descent algorithm)來優化交叉熵,這裏是以 0.5 的速度來一點點的優化交叉熵。

以後就是初始化變量,以及啓動 Session

sess = tf.InteractiveSession()
  tf.global_variables_initializer().run()複製代碼

啓動以後,開始訓練!

# Train
  for _ in range(1000):
    batch_xs, batch_ys = mnist.train.next_batch(100)
    sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})複製代碼

這裏訓練 1000 次,每次隨機找 100 個數據來練習,這裏的 feed_dict={x: batch_xs, y_: batch_ys},就是咱們前面那設置的兩個留着佔位的輸入值。

到這裏基本訓練就完成了。

評估模型

訓練完以後,咱們來評估一下模型的準確度。

# Test trained model
  correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
  accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
  print(sess.run(accuracy, feed_dict={x: mnist.test.images,
                                      y_: mnist.test.labels}))複製代碼

tf.argmax 給出某個tensor對象在某一維上的其數據最大值所在的索引值。由於咱們的 label 只有一個 1,因此 tf.argmax(_y, 1) 就是 label 的索引,也就是表示圖片是幾。把計算值和預測值 equal 一下就能夠得出模型算的是否準確。
下面的 accuracy 計算的是一個總體的精確度。

這裏填入的數據不是訓練數據,是測試數據和測試 label。

最終結果,個人是 0.9151,91.51% 的準確度。官方說這個不太好,若是用一些更好的模型,好比多層卷積網絡等,這個識別率能夠到 99% 以上。

官方的例子到這裏就結束了,雖說識別了幾萬張圖片,可是我一張像樣的圖片都沒看到,因而我決定想辦法拿這個模型真正找幾個圖片測試一下。

用模型測試

看下上面的例子,重點就是放測試數據進去這裏,若是咱們要拿圖片測,須要先把圖片變成相應格式的數據。

sess.run(accuracy, feed_dict={x: mnist.test.images,
                                      y_: mnist.test.labels})複製代碼

看下這裏 mnist 是從 tensorflow.examples.tutorials.mnist 中的 input_data 的 read_data_sets 方法中來的。

from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets(FLAGS.data_dir, one_hot=True)複製代碼

Python 就是好,有啥不懂看下源碼。源碼的在線地址在這裏

打開找 read_data_sets 方法,發現:

from tensorflow.contrib.learn.python.learn.datasets.mnist import read_data_sets複製代碼

這個文件裏面。

def read_data_sets(train_dir, fake_data=False, one_hot=False, dtype=dtypes.float32, reshape=True, validation_size=5000):
                   ...
                   ...
                   ...
    train = DataSet(train_images, train_labels, dtype=dtype, reshape=reshape)
    validation = DataSet(validation_images,
                        validation_labels,
                        dtype=dtype,
                        reshape=reshape)
    test = DataSet(test_images, test_labels, dtype=dtype, reshape=reshape)

    return base.Datasets(train=train, validation=validation, test=test)複製代碼

能夠看到,最後返回的都是是一個對象,而咱們用的 feeddict={x: mnist.test.images, y: mnist.test.labels} 就是從這來的,是一個 DataSet 對象。這個對象也在這個文件裏面。

class DataSet(object):

  def __init__(self, images, labels, fake_data=False, one_hot=False, dtype=dtypes.float32, reshape=True):
    """Construct a DataSet. one_hot arg is used only if fake_data is true. `dtype` can be either `uint8` to leave the input as `[0, 255]`, or `float32` to rescale into `[0, 1]`. """
    ...
    ...
    ...複製代碼

這個對象很長,我就只挑重點了,主要看構造方法。必定要傳入的有 images 和 labels。其實這裏已經比較明朗了,咱們只要把單張圖片弄成 mnist 格式,分別傳入到這個 DataSet 裏面,就能夠獲得咱們要的數據。

網上查了下還真有,代碼地址,對應的文章:www.jianshu.com/p/419557758…,文章講的有點不清楚,須要針對 TensorFlow 1.0 版本以及實際目錄狀況作點修改。

直接上我修改後的代碼:

from PIL import Image
from numpy import *

def GetImage(filelist):
    width=28
    height=28
    value=zeros([1,width,height,1])
    value[0,0,0,0]=-1
    label=zeros([1,10])
    label[0,0]=-1

    for filename in filelist:
        img=array(Image.open(filename).convert("L"))
        width,height=shape(img);
        index=0
        tmp_value=zeros([1,width,height,1])
        for i in range(width):
            for j in range(height):
                tmp_value[0,i,j,0]=img[i,j]
                index+=1

        if(value[0,0,0,0]==-1):
            value=tmp_value
        else:
            value=concatenate((value,tmp_value))

        tmp_label=zeros([1,10])
        index=int(filename.strip().split('/')[2][0])
        print "input:",index
        tmp_label[0,index]=1
        if(label[0,0]==-1):
            label=tmp_label
        else:
            label=concatenate((label,tmp_label))

    return array(value),array(label)複製代碼

這裏讀取圖片依賴 PIL 這個庫,因爲 PIL 比較少維護了,能夠用它的一個分支 Pillow 來代替。另外依賴 numpy 這個科學計算庫,沒裝的要裝一下。

這裏就是把圖片讀取,並按 mnist 格式化,label 是取圖片文件名的第一個字,因此圖片要用數字開頭命名。

若是懶得 PS 畫或者手寫的畫,能夠把測試數據集的數據給轉回圖片,實測成功,參考這篇文章:如何用python解析mnist圖片

新建一個文件夾叫 test_num,裏面圖片以下,這裏命名就是 label 值,能夠看到 label 和圖片是對應的:

開始測試:

print("Start Test Images")

  dir_name = "./test_num"
  files = glob2.glob(dir_name + "/*.png")
  cnt = len(files)
  for i in range(cnt):
    print(files[i])
    test_img, test_label = GetImage([files[i]])

    testDataSet = DataSet(test_img, test_label, dtype=tf.float32)

    res = accuracy.eval({x: testDataSet.images, y_: testDataSet.labels})

    print("output: ",  res)
    print("----------")複製代碼

這裏用了 glob2 這個庫來遍歷以及過濾文件,須要安裝,常規的遍歷會把 Mac 上的 .DS_Store 文件也會遍歷進去。

能夠看到咱們打的 label 和模型算出來的是相符的。

而後咱們能夠打亂文件名,把 9 說成 8,把 0 也說成 8:

能夠看到,咱們的 label 和模型算出來的是不相符的。

恭喜,到着你就完成了一次簡單的人工智能之旅。

總結

從這個例子中咱們能夠大體知道 TensorFlow 的運行模式:

例子中是每次都要走一遍訓練流程,其實是能夠用 tf.train.Saver() 來保存訓練好的模型的。這個入門例子完成以後能對 TensorFlow 有個感性認識。

TensorFlow 沒有那麼神祕,沒有咱們想的那麼複雜,也沒有咱們想的那麼簡單,而且還有不少數學知識要補充呢。

另外,這方面我也是個小白,文中如有錯誤之處,歡迎斧正。

demo 代碼地址:
github.com/bob-chen/te…

碎碎念

記錄一些所思所想,寫寫科技與人文,寫寫生活狀態,寫寫讀書心得,主要是扯淡和感悟。
歡迎關注,交流。

微信公衆號:程序員的詩和遠方

公衆號ID : MonkeyCoder-Life

參考

github.com/wlmnzf/tens…

blog.csdn.net/u014046170/…

www.jianshu.com/p/419557758…

zhuanlan.zhihu.com/p/22410917?…

stackoverflow.com/questions/3…

相關文章
相關標籤/搜索