05.23 Day 02:撰寫第一個 Neural Network 程序——阿拉伯數字辨識

內容導讀

照理講,我們應該先了解『神經網絡』(Neural Network)概念,再談如何寫程式,但是,概念介紹內容有點硬,為了提高學習興趣,避免一開始就搞一堆數學公式,造成讀者跑光光,所以,還是柿子挑軟的吃,先從簡單的開始,與 Neural Network 先培養感情,如果您是條硬漢,可以等看完下一篇後,再回頭看這一篇。其中,TensorFlow 網路聲量最高,因此,我們採用它作為程式開發的基礎,Keras 則是支援TensorFlow的更高階函數庫(Meta Framework),可以用很簡潔的程式碼完成一個 Neural Network 模型,非常適合入門學習,因此我們就從 Keras 開始學起。等一下,那 IDE 呢? 你可以用記事本、NodePad++、或者PyCharm,我則是使用 VS 2017 Community 版本,它也是一個很不錯的選擇喔,可以像 C# 一樣的除錯。另外,使用 Jupyter Notebook ,可以讓你像作筆記一樣的寫程式,總之,戲法人人會變,端看你熟悉甚麼樣的環境與工具。導入(import)要使用的函式庫,包括 NumPy(矩陣運算)、Keras、matplotlib(繪圖)。選擇損失函數(crossentropy)及優化方法(adam)及成效衡量方式(accuracy),就可以開始訓練。

入門

照理講,我們應該先了解『神經網絡』(Neural Network)概念,再談如何寫程式,但是,概念介紹內容有點硬,為了提高學習興趣,避免一開始就搞一堆數學公式,造成讀者跑光光,所以,還是柿子挑軟的吃,先從簡單的開始,與 Neural Network 先培養感情,如果您是條硬漢,可以等看完下一篇後,再回頭看這一篇。

我們就先來寫一個程序,目標是『辨識阿拉伯數字(0~9)』。

開發環境建置

首先是選擇開發環境,一般而言,Python 及 R 都有很好的支援及大量的函數庫(Library/Toolbox),而 Python 的框架較易於系統整合(Web、Mobile),因此我選擇 Python,但支援 Python 的 Neural Network 框架(Framework)也很多,參見下圖,要選擇哪一個呢?

image.png

圖. Machine Learning 框架(Framework)GitHub評比,資料來源: 。

其中,TensorFlow 網路聲量最高,因此,我們採用它作為程式開發的基礎,Keras 則是支援TensorFlow的更高階函數庫(Meta Framework),可以用很簡潔的程式碼完成一個 Neural Network 模型,非常適合入門學習,因此我們就從 Keras 開始學起。

首先我們要建構開發環境,筆者以 Windows 環境為例,依序安裝以下軟體:

安裝 : 它包含 Python 及常用的套件(Packages),例如NumPy、Pandas等矩陣運算的套件,Python V2 與 V3 不相容,我們選 V3,除非你以前曾大量使用 V2。安裝 Tensorflow:可以選擇CPU或GPU版,安裝CPU版,直接在 DOS 下,輸入 pip install tensorflow。安裝 Keras:在 DOS 下,輸入 pip install keras。

就是這麼簡單,當然,為了加速運算,你也可以安裝支援GPU版本的Tensorflow,NVidia支援CUDA的顯示卡請參考 ,相關安裝程序請參考 ,如果要在 Linux 環境開發也行,安裝內容不變, 請參考 。

以我的電腦為例,配備如下圖,GPU顯示卡為NVIDIA GeForce GTX 750(1GB memory),實際安裝 Tensorflow GPU 版本的程序如下:

image.png

下載 CUDA Toolkit 8.0,不能是 9.x,需先至nVidia官網建立帳號,再至 下載。安裝完 CUDA Toolkit 後,再下載 ,並將壓縮檔解開,複製到 CUDA Toolkit 8.0 安裝目錄下同名子目錄下。將 CUDA Toolkit 8.0 安裝目錄下bin子目錄 放到 環境變數 Path 中,在 DOS 中執行Tensorflow時,才找的到相關 Dll。安裝 Tensorflow GPU 版本,執行 pip install --ignore-installed --upgrade tensorflow-gpu在 DOS 中執行 python,接著輸入下列程式,應該就會有相關訊息出現。 import tensorflow as tf hello = tf.constant('Hello, TensorFlow!') sess = tf.Session() print(sess.run(hello))

實際測試簡單的程式,確實快很多,但是記憶體太小,遇到複雜的程式,例如後續的CNN程式,需要儲存大量矩陣時,就GG了,所以,奉勸各位,要學 Neural Network,還是要花錢買張新一點的顯示卡,才能省去執行時去泡茶、喝咖啡的時間。

如果一切順利完成,就可以開始寫程式了。等一下,那 IDE 呢? 你可以用記事本、NodePad++、或者PyCharm,我則是使用 VS 2017 Community 版本,它也是一個很不錯的選擇喔,可以像 C# 一樣的除錯。另外,使用 ,可以讓你像作筆記一樣的寫程式,總之,戲法人人會變,端看你熟悉甚麼樣的環境與工具。

程式撰寫

撰寫 Keras 程式,我們需要了解簡單的 Python 語法,建議快速瀏覽『Introducing Python』這本書的第二~四章就夠了,它不只有中文版,也有免費的PDF電子書喔。

以下範例主要是利用 MNIST 資料集的訓練資料,建立單一隱藏層(Hidden Layer)的 Neural Network 模型,以預測實際影像是哪一個阿拉伯數字,如下圖:

image.png

圖. 阿拉伯數字(0~9)辨識的流程

流程步驟如下:

先讀入訓練資料,本例為 60,000 筆資料,每筆資料是一個 28 * 28 的點矩陣圖形。圖形的每一點都當成一個輸入變數(X),乘以一個權重W(i,j),向隱藏層(Hidden Layer)傳導,隱藏層的每一個節點會得到輸入變數的加權總和(W * X)。再如法炮製,向輸出層傳導,輸出層的每一個節點會得到隱藏層的加權總和,將輸出層的每一個節點化為機率,就得到一個預測模型了。之後我們將新資料輸入模型,就會得到 0~9 的機率,最大的機率對應的數字就是我們的預測值了。權重(W)是唯一未知的變數,他們等於多少呢? 這就是 Neural Network 厲害的地方,它透過優化(Optimization)計算,就可以求出 W 的最佳解,構築出模型公式了。

程式很簡單,先看註解(#開頭),即可瞭解整個流程:

導入(import)要使用的函式庫,包括 NumPy(矩陣運算)、Keras、matplotlib(繪圖)。從網路載入 MNIST 資料集,請 Keras 自動分為『訓練組』及『測試組』資料,MNIST 是由 AI 大師 Yann LeCun 所建立的手寫阿拉伯數字資料集(Dataset)。建立最簡單的線性模型(Sequential),就是一層層往下執行,沒有分叉(If),也沒有迴圈(loop),這裡只設一層隱藏層(Dense)。選擇損失函數(crossentropy)及優化方法(adam)及成效衡量方式(accuracy),就可以開始訓練。執行模型評估,計算模型參數,即上圖的W(i,j)及W(j,k),模型就算完成了。接著就可以使用這個模型,預測新資料了。

# 導入函式庫
import numpy as np
from keras.models import Sequential
from keras.datasets import mnist
from keras.layers import Dense, Dropout, Activation, Flatten
from keras.utils import np_utils # 用來後續將 label 標籤轉為 one-hot-encoding
from matplotlib import pyplot as plt

# 載入 MNIST 資料庫的訓練資料,並自動分為『訓練組』及『測試組』
(X_train, y_train), (X_test, y_test) = mnist.load_data()

# 建立簡單的線性執行的模型
model = Sequential()
# Add Input layer, 隱藏層(hidden layer) 有 256個輸出變數
model.add(Dense(units=256, input_dim=784, kernel_initializer='normal', activation='relu'))
# Add output layer
model.add(Dense(units=10, kernel_initializer='normal', activation='softmax'))

# 編譯: 選擇損失函數、優化方法及成效衡量方式
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

# 將 training 的 label 進行 one-hot encoding,例如數字 7 經過 One-hot encoding 轉換後是 0000001000,即第7個值為 1
y_TrainOneHot = np_utils.to_categorical(y_train)
y_TestOneHot = np_utils.to_categorical(y_test)

# 將 training 的 input 資料轉為2維
X_train_2D = X_train.reshape(60000, 28*28).astype('float32')
X_test_2D = X_test.reshape(10000, 28*28).astype('float32')

x_Train_norm = X_train_2D/255
x_Test_norm = X_test_2D/255

# 進行訓練, 訓練過程會存在 train_history 變數中
train_history = model.fit(x=x_Train_norm, y=y_TrainOneHot, validation_split=0.2, epochs=10, batch_size=800, verbose=2)

# 顯示訓練成果(分數)
scores = model.evaluate(x_Test_norm, y_TestOneHot)
print()


print("\\t[Info] Accuracy of testing data = {:2.1f}%".format(scores[1]*100.0))

# 預測(prediction)
X = x_Test_norm[0:10,:]
predictions = model.predict_classes(X)
# get prediction result
print(predictions)

執行方法很簡單,在DOS執行Python,接著將以上程式一段段貼上即可,我們就可以觀察每段程式的用途,要看變數內容,只要輸入變數名稱即可,全部執行完,可以看到準確率有 85%,夠神奇吧,畢竟我們只寫了10多行的程式(不含註解)。

要確認預測是否正確,可以再貼上下列程式,查看影像:

# 顯示 第一筆訓練資料的圖形,確認是否正確
plt.imshow(X_test[0])
plt.show()

image.png

如果我們要看優化的過程,可以輸入以下程式,結果如下圖:

plt.plot(train_history.history['loss'])
plt.plot(train_history.history['val_loss'])
plt.title('Train History')
plt.ylabel('loss')
plt.xlabel('Epoch')
plt.legend(['loss', 'val_loss'], loc='upper left')
plt.show()

image.png

圖. 優化過程的損失函數(Loss)的變化

進行到這裡,我們已經跨出了一小步,後續我們接著抽絲剝繭,好好研究它為什麼可以這麼厲害。

相關程式請至 下載,本範例為0.py。