原文第四篇中,咱們介紹了官方的入門案例MNIST,功能是識別手寫的數字0-9。這是一個很是基礎的TensorFlow應用,地位至關於一般語言學習的"Hello World!"。
咱們先不進入TensorFlow 2.0中的MNIST代碼講解,由於TensorFlow 2.0在Keras的幫助下抽象度比較高,代碼很是簡單。但這也使得大量的工做被隱藏掉,反而讓人難以真正理解前因後果。特別是其中所使用的樣本數據也已經不一樣,而這對於學習者,是很是重要的部分。模型能夠看論文、在網上找成熟的成果,數據的收集和處理,可不會有人幫忙。
在原文中,咱們首先介紹了MNIST的數據結構,而且用一個小程序,把樣本中的數組數據轉換爲JPG圖片,來幫助讀者理解原始數據的組織方式。
這裏咱們把小程序也升級一下,直接把圖片顯示在屏幕上,再也不另外保存JPG文件。這樣圖片看起來更快更直觀。
在TensorFlow 1.x中,是使用程序input_data.py來下載和管理MNIST的樣本數據集。當前官方倉庫的master分支中已經取消了這個代碼,爲了避免去翻倉庫,你能夠在這裏下載,放置到你的工做目錄。
在TensorFlow 2.0中,會有keras.datasets類來管理大部分的演示和模型中須要使用的數據集,這個咱們後面再講。
MNIST的樣本數據來自Yann LeCun的項目網站。若是網速比較慢的話,能夠先用下載工具下載,而後放置到本身設置的數據目錄,好比工做目錄下的data文件夾,input_data檢測到已有數據的話,不會重複下載。
下面是咱們升級後顯示訓練樣本集的源碼,代碼的講解保留在註釋中。若是閱讀有疑問的,建議先去原文中看一下樣本集數據結構的圖示部分:python
#!/usr/bin/env python3 # 引入mnist數據預讀準備庫 # 2.0以後建議直接使用官方的keras.datasets.mnist.load_data # 此處爲了同之前的講解對比,沿用以前的引用文件 import input_data # tensorflow 2.0庫 import tensorflow as tf # 引入繪圖庫 import matplotlib.pyplot as plt # 這裏使用mnist數據預讀準備庫檢查給定路徑是已經有樣本數據, # 沒有的話去網上下載,並保存在指定目錄 # 已經下載了數據的話,將數據讀入內存,保存到mnist對象中 mnist = input_data.read_data_sets("data/", one_hot=True) # 樣本集的結構以下: # mnist.train 訓練數據集 # mnist.validation 驗證數據集 # mnist.test 測試數據集 # len(mnist.train.images)=55000 # len(mnist.train.images[0])=784 # len(mnist.train.labels[0])=10 def plot_image(i, imgs, labels): # 將1維的0-1的數據轉換爲標準的0-255的整數數據,2維28x28的圖片 image = tf.floor(256.0 * tf.reshape(imgs[i], [28, 28])) # 原數據爲float,轉換爲uint8字節數據 image = tf.cast(image, dtype=tf.uint8) # 標籤樣本爲10個字節的數組,爲1的元素下標就是樣本的標籤值 # 這裏使用argmax方法直接轉換爲0-9的整數 label = tf.argmax(labels[i]) plt.grid(False) plt.xticks([]) plt.yticks([]) # 繪製樣本圖 plt.imshow(image) # 顯示標籤值 plt.xlabel("{}".format(label)) def show_images(num_rows, num_cols, images, labels): num_images = num_rows*num_cols plt.figure('Train Samples', figsize=(2*num_cols, 2*num_rows)) # 循環顯示前num_rows*num_cols副樣本圖片 for i in range(num_images): plt.subplot(num_rows, num_cols, i+1) plot_image(i, images, labels) plt.show() # 顯示前4*6=24副訓練集樣本 show_images(4, 6, mnist.train.images, mnist.train.labels)
注意這個代碼只是用來把樣本集可視化。TensorFlow 2.0新特徵,在這裏只體現了取消Session和Session.run()。目的只是爲了延續原來的講解,讓圖片直接顯示而不是保存爲圖像文件,以及升級到Python3和TensorFlow 2.0的執行環境。
樣本集顯示出來效果是這樣的:
算法
原文第四篇中,咱們使用了一個並不實用的線性迴歸模型來作手寫數字識別。這樣作能夠簡化中間層,從而可以使用可視化的手段來說解機器視覺在數學上的基本原理。由於線性迴歸模型咱們在本系列第一篇中講過了,這裏就跳過,直接說使用神經網絡來解決MNIST問題。
神經網絡模型的構建在TensorFlow 1.0中是最繁瑣的工做。咱們曾經爲了講解vgg-19神經網絡的使用,首先編寫了一個複雜的輔助類,用於從字符串數組的遍歷中自動構建複雜的神經網絡模型。
而在TensorFlow 2.0中,經過高度抽象的keras,能夠很是容易的構建神經網絡模型。
爲了幫助理解,咱們先把TensorFlow 1.0中使用神經網絡解決MNIST問題的代碼原文粘貼以下:小程序
#!/usr/bin/env python # -*- coding=UTF-8 -*- import input_data mnist = input_data.read_data_sets('data/', one_hot=True) import tensorflow as tf sess = tf.InteractiveSession() #對W/b作初始化有利於防止算法陷入局部最優解, #文檔上講是爲了打破對稱性和防止0梯度及神經元節點恆爲0等問題,數學原理是相似問題 #這兩個初始化單獨定義成子程序是由於多層神經網絡會有屢次調用 def weight_variable(shape): #填充「權重」矩陣,其中的元素符合截斷正態分佈 #能夠有參數mean表示指定均值及stddev指定標準差 initial = tf.truncated_normal(shape, stddev=0.1) return tf.Variable(initial) def bias_variable(shape): #用0.1常量填充「偏移量」矩陣 initial = tf.constant(0.1, shape=shape) return tf.Variable(initial) #定義佔位符,至關於tensorFlow的運行參數, #x是輸入的圖片矩陣,y_是給定的標註標籤,有標註必定是監督學習 x = tf.placeholder("float", shape=[None, 784]) y_ = tf.placeholder("float", shape=[None, 10]) #定義輸入層神經網絡,有784個節點,1024個輸出, #輸出的數量是本身定義的,要跟第二層節點的數量吻合 W1 = weight_variable([784, 1024]) b1 = bias_variable([1024]) #使用relu算法的激活函數,後面的公式跟前一個例子相同 h1 = tf.nn.relu(tf.matmul(x, W1) + b1) #定義第二層(隱藏層)網絡,1024輸入,512輸出 W2 = weight_variable([1024, 512]) b2 = bias_variable([512]) h2 = tf.nn.relu(tf.matmul(h1, W2) + b2) #定義第三層(輸出層),512輸入,10輸出,10也是咱們但願的分類數量 W3 = weight_variable([512, 10]) b3 = bias_variable([10]) #最後一層的輸出一樣用softmax分類(也算是激活函數吧) y3=tf.nn.softmax(tf.matmul(h2, W3) + b3) #交叉熵代價函數 cross_entropy = -tf.reduce_sum(y_*tf.log(y3)) #這裏使用了更加複雜的ADAM優化器來作"梯度最速降低", #前一個例子中咱們使用的是:GradientDescentOptimizer train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy) #計算正確率以評估效果 correct_prediction = tf.equal(tf.argmax(y3,1), tf.argmax(y_,1)) accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float")) #tf初始化及全部變量初始化 sess.run(tf.global_variables_initializer()) #進行20000步的訓練 for i in range(20000): #每批數據50組 batch = mnist.train.next_batch(50) #每100步進行一次正確率計算並顯示中間結果 if i%100 == 0: train_accuracy = accuracy.eval(feed_dict={ x:batch[0], y_: batch[1]}) print "step %d, training accuracy %g"%(i, train_accuracy) #使用數據集進行訓練 train_step.run(feed_dict={x: batch[0], y_: batch[1]}) #完成模型訓練給出最終的評估結果 print "test accuracy %g"%accuracy.eval(feed_dict={ x: mnist.test.images, y_: mnist.test.labels})
總結一下上面TensorFlow 1.x版本MNIST代碼中的工做:數組
若是你理解了我總結的這幾點,請繼續看TensorFlow 2.0的實現:bash
#!/usr/bin/env python3 # 引入mnist數據預讀準備庫 # 2.0以後建議直接使用官方的keras.datasets.mnist.load_data # 此處爲了同之前的講解對比,沿用以前的引用文件 import input_data # tensorflow庫 import tensorflow as tf # tensorflow 已經內置了keras from tensorflow import keras # 引入繪圖庫 import matplotlib.pyplot as plt # 這裏使用mnist數據預讀準備庫檢查給定路徑是已經有樣本數據, # 沒有的話去網上下載,並保存在指定目錄 # 已經下載了數據的話,將數據讀入內存,保存到mnist對象中 mnist = input_data.read_data_sets("data/", one_hot=True) # 樣本集的結構以下: # mnist.train 訓練數據集 # mnist.validation 驗證數據集 # mnist.test 測試數據集 # len(mnist.train.images)=55000 # len(mnist.train.images[0])=784 # len(mnist.train.labels[0])=10 def plot_image(i, imgs, labels, predictions): # 將1維的0-1的數據轉換爲標準的0-255的整數數據,2維28x28的圖片 image = tf.floor(256.0 * tf.reshape(imgs[i], [28, 28])) # 原數據爲float,轉換爲uint8字節數據 image = tf.cast(image, dtype=tf.uint8) # 標籤樣本爲10個字節的數組,爲1的元素下標就是樣本的標籤值 # 這裏使用argmax方法直接轉換爲0-9的整數 label = tf.argmax(labels[i]) prediction = tf.argmax(predictions[i]) plt.grid(False) plt.xticks([]) plt.yticks([]) # 繪製樣本圖 plt.imshow(image) # 顯示標籤值,對比顯示預測值和實際標籤值 plt.xlabel("predict:{} label:{}".format(prediction, label)) def show_images(num_rows, num_cols, images, labels, predictions): num_images = num_rows*num_cols plt.figure('Predict Samples', figsize=(2*num_cols, 2*num_rows)) # 循環顯示前num_rows*num_cols副樣本圖片 for i in range(num_images): plt.subplot(num_rows, num_cols, i+1) plot_image(i, images, labels, predictions) plt.show() # 原文中已經說明了,當前是10個元素數組表示一個數字, # 值爲1的那一元素的索引就是表明的數字,這是分類算法決定的 # 下面是直接轉換爲0-9的正整數,用做訓練的標籤 train_labels = tf.argmax(mnist.train.labels, 1) # 定義神經網絡模型 model = keras.Sequential([ # 輸入層爲28x28共784個元素的數組,節點1024個 keras.layers.Dense(1024, activation='relu', input_shape=(784,)), keras.layers.Dense(512, activation='relu'), keras.layers.Dense(10, activation='softmax') ]) # 編譯模型 model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy']) # 使用訓練集數據訓練模型 model.fit(mnist.train.images, train_labels, epochs=3) # 測試集的標籤一樣轉成0-9數字 test_labels = tf.argmax(mnist.test.labels, 1) # 使用測試集樣本驗證識別準確率 test_loss, test_acc = model.evaluate(mnist.test.images, test_labels) print('\nTest accuracy:', test_acc) # 完整預測測試集樣本 predictions = model.predict(mnist.test.images) # 圖示結果的前4*6個樣本 show_images(4, 6, mnist.test.images, mnist.test.labels, predictions)
一般我都是直接在註釋中對程序作仔細的講解,此次例外一下,由於咱們須要從大局觀上看清楚代碼的結構。
這幾行代碼是定義神經網絡模型:網絡
# 定義神經網絡模型 model = keras.Sequential([ # 輸入層爲28x28共784個元素的數組,節點1024個 keras.layers.Dense(1024, activation='relu', input_shape=(784,)), keras.layers.Dense(512, activation='relu'), keras.layers.Dense(10, activation='softmax') ])
每一行實際就表明一層神經網絡的節點。在第一行中特別指明瞭輸入數據的形式,便可以有未知數量的樣本,每個樣本784個字節(28x28)。實際上這個輸入樣本能夠不指定形狀,在沒有指定的狀況下,Keras會自動識別訓練數據集的形狀,並自動將模型輸入匹配到訓練集形狀。只是這種習慣並不必定好,除了效率問題,當樣本集出錯的時候,模型的定義也沒法幫助開發者提早發現問題。因此建議產品化的模型,應當在模型中指定輸入數據類型。
除了第一層以外,以後的每一層都無需指定輸入樣本形狀。Keras會自動匹配相鄰兩個層的數據。這節省了開發人員大量的手工計算也不易出錯。
最後,激活函數的選擇成爲一個參數。總體代碼看上去簡潔的使人驚訝。數據結構
接着在編譯模型的代碼中,直接指定Keras中預約義的「sparse_categorical_crossentropy」損失函數和「adam」優化算法。一個函數配合幾個參數選擇就完成了這部分工做:機器學習
# 編譯模型 model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
對本來複雜的訓練循環部分,TensorFlow 2.0優化的最爲完全,只有一行代碼:函數
# 使用訓練集數據訓練模型 model.fit(mnist.train.images, train_labels, epochs=3)
使用測試集數據對模型進行評估一樣只須要一行代碼,這裏就不摘出來了,在上面完整代碼中能看到。
能夠想象,TensorFlow 2.0正式發佈後,模型搭建、訓練、評估的工做量大幅減小,會催生不少由實驗性模型創新而出現的新算法。機器學習領域會再次涌現普及化浪潮。
這一版代碼中,咱們還細微修改了樣本可視化部分的程序,將原來顯示訓練集樣本,改成顯示測試集樣本。主要是增長了一個圖片識別結果的參數。將圖片的識別結果同數據集的標註一同顯示在圖片的下面做爲對比。
程序運行的時候,控制檯輸出以下:工具
$ python3 mnist-show-predict-pic-v1.py Extracting data/train-images-idx3-ubyte.gz Extracting data/train-labels-idx1-ubyte.gz Extracting data/t10k-images-idx3-ubyte.gz Extracting data/t10k-labels-idx1-ubyte.gz Epoch 1/3 55000/55000 [==============================] - 17s 307us/sample - loss: 0.1869 - accuracy: 0.9420 Epoch 2/3 55000/55000 [==============================] - 17s 304us/sample - loss: 0.0816 - accuracy: 0.9740 Epoch 3/3 55000/55000 [==============================] - 16s 298us/sample - loss: 0.0557 - accuracy: 0.9821 10000/10000 [==============================] - 1s 98us/sample - loss: 0.0890 - accuracy: 0.9743 Test accuracy: 0.9743
最終的結果表示,模型經過3次的訓練迭代以後。使用測試集數據進行驗證,手寫體數字識別正確率爲97.43%。
程序最終會顯示測試集前24個圖片及預測結果和標註信息的對比:
(待續...)