神經網路(Neural Network , NN)

神經網路

神經網路(Neural Network,縮寫為NN)是模擬大腦的神經元(neurons)運作,使用數學模型進行模擬大腦的神經元的運作,數學模型在電腦上進行計算產生結果。神經網路由大量神經元組成,經由外部輸入資料,神經網路調整內部參數獲得學習結果。

10-1. 神經網路的神經元

神經網路模擬人類大腦的神經元運作,神經網路的神經元(neurons)又稱作unit或node,其運作圖示如下。左方輸入資料為(X1, X2, …, Xm),分別乘以對應的權重(W1, W2, …, Wm)累加起來,再加上偏移量W0所獲得的值為Z,此計算為線性轉換,通過非線性的激勵函式(activation function)轉換成神經元的輸出A。神經網路由許多個神經元所組成,又可以分好幾層,用來辨識輸入的資料,達成學習的效果。

10-2 線性可分割與非線性可分割

問題可以使用一條線劃分開來,找出這條線就可以辨識出結果,稱作線性可分割問題。假設下圖中的點,右上方的點(3,5),(4,4),(5,3)是同一組,左下方的點(2,3),(2,2),(3,2)為同一組,請找一條直線將此六個點分成指定的兩組,經由直線y = -x + 6,將此六個點分割成兩組,達成所需功能,此問題為線性可分割,表示經由一條直線可以精確分割。

下圖神經元左側為線性方程式「Z = W1*X1+ W2*X2+…+ Wm*Xm+ W0」就可以解決線性可分割問題,不需要右側的激勵函式。

若右上方的點(3,5),(4,4),(5,3),新增節點(2,1)為同一組,如下圖,則找不到一條直線,可以精確分割這七個點達成所需分割,此為非線性可分割問題。

許多問題都屬於非線性可分割,神經網路適用於非線性可分割問題,使用神經元右側的非線性的激勵函式,讓神經元有可能辨識非線性可分割問題,利用多層架構的神經元與適當的訓練,達成有效的辨識非線性可分割問題。

以下使用邏輯閘介紹線性可分割問題與非線性可分割問題,邏輯閘AND屬於線性可分割問題,邏輯閘XOR屬於非線性可分割問題。

邏輯閘AND

若兩個輸入值為1,則輸出值為1;若一個或兩個輸入值為0,則輸出為0,真值表如下。

將這些點表示在X1與X2平面上,如下圖,可以找到一條線分割這些輸出值,讓輸出值0與輸出值1能夠分開,例如:直線X1+X2-1.5=0,所以邏輯閘AND為線性可分割問題。

邏輯閘XOR

若一個輸入值為1,另一個輸入值為0,則輸出值為1,兩個輸入值都為0或兩個輸入值都為1,則輸出值為0,真值表如下。

將這些點表示在X1與X2平面上,如下圖,找不到一條線分割這些輸出值,讓輸出值0與輸出值1能夠分開,所以邏輯閘XOR為線性不可分割問題。

要如何解決非線性可分割問題,在神經網路要使用多層神經元組成,與非線性的激勵函式達成,在之後單元會介紹此範例。

10-3 神經網路

一層(single layer)神經網路可以由多個神經元組成,下圖為n個神經元所組成的一層神經網路。假設一個輸入資料有m個元素,分別為X1、X2、…、Xm,輸出n筆資料,參數W會形成m x n的二維矩陣,神經網路的訓練過程就是調整參數W來達成學習效果。如果m個輸入資料連結到n個神經元的每個神經元,加上1個偏移量的輸入,也連結到n個神經元的每個神經元,則會有(m+1)*n個連接線,稱作全連結層(Fully Connected Layer),如下圖,神經網路習慣使用全連結層。

第1個神經元的輸出Z1等於W11*X1+W21*X2+…+Wm1*Xm+W01*1,此為線性轉換,經由非線性的激勵函式f轉換成A1,也就是A1等於f(Z1);第2個神經元的輸出Z2等於W12*X1+W22*X2+…+Wm2*Xm+W02*1,此為線性轉換,經由非線性的激勵函式f轉換成A2,也就是A2等於f(Z2);依此類推,假設有n個神經元,則會執行這樣的轉換n次,輸入m個資料會產出n個輸出。

將這些W(每條線上的參數值)組成一個二維陣列W,如下。輸入資料到神經網路進行訓練,其目的就是經由不斷地調整參數陣列W內的所有參數值,讓神經網路可以產出比較正確的預估結果。

上述計算過程,使用矩陣運算表示,如下。

將多個一層神經網路可以組成多層神經網路(many layer),以下為兩層的神經網路範例。第一層的輸出(A11, A21, …, An1)為第二層的輸入,假設第二層神經網路有p個,第二層與第一層神經網路架構相同,可以使用不同的激勵函式,只是第二層輸入n個資料,產生p個結果。每一層神經網路就是找出最適合的參數W,讓整個神經網路產生更好的預測結果,神經網路可以有很多層,但是層數越多,計算複雜度越高,所需計算時間越長,但學習效果不一定比較好。

10-3-1 神經網路的運作原理

(一)建立多層的神經網路與每層所使用的激勵函式

(二)使用前向傳播算法(forward propagation)將多筆資料輸入神經網路計算預估結果

(三)加總多筆資料的預估結果與真正結果的差異,差異越大則損失值(Loss)越大

(四)使用反向傳播算法(back propagation)更新權重W,目標為降低損失值

(五)不斷重複使用前向傳播算法與反向傳播算法,直到損失值不再明顯降低

能夠準確預估的神經網路表示找到最佳權重W,使得預估結果與真正結果的差異越小越好,也就是獲得最小的損失值。

10-3-2 神經網路的運作範例

以下舉例說明二層神經網路的運作,假設第一層有3個神經元,激勵函式f使用ReLU,該函式輸入正數則回傳該正數,負數與0都回傳為0。第二層有1個神經元,激勵函式f使用sigmoid,sigmoid(x) = 1/(1+e^(-x) ),激勵函式之後會詳細介紹。

綜合上述,神經網路的運作步驟如下。

Step1)神經網路建立多層的神經網路。

Step2)初始化每一層神經網路的參數W與W0為隨機值或固定值,輸入一筆或多筆資料使用前向傳播算法(forward propagation)進行預估。

Step3)使用損失函式找出預估結果與真正結果的差異,神經網路以最小化損失函式來提高預估準確性。

Step4)接著使用反向傳播算法(back propagation)更新每一層的參數W與W0,並設定適當的學習率,如果學習率設定過大,則參數W與W0改變越大,則預測結果改變越大,容易造成無法收斂到最佳解;如果學習率設定過小,則收斂速度很慢,且可能找到局部的最佳解。

Step5)重複Step2到Step4,使用前向傳播算法與新的參數W與W0進行預估,使用損失函式計算預估結果與真正結果的差異,接著使用反向傳播算法更新每一層參數W與W0。

Step6)為了最小化損失函式,不斷重複Step5,直到獲得最小的損失值。

10-4 使用keras實作神經網路

神經網路由多層組成,分成輸入層、隱藏層、輸出層。每一層可以使用全連接層(在keras使用Dense表示全連接層),上層的每個神經元輸出都與下層的每個神經元的輸入連接起來,稱作全連接層,每一層指定激勵函式。

(1)輸入層:用於輸入資料,每次輸入一筆資料,一筆資料可以由多個數值組成,例如:(X1, X2, .., Xm),輸入層輸入個數等於輸入資料的組成個數。

(2)輸出層:用於神經網路輸出結果,根據輸出的需要,選擇適當的激勵函式,激勵函式會影響輸出結果,輸出層神經元個數等於每筆輸入資料對應的輸出個數。

(3)隱藏層:隱藏層在輸入層與輸出層之間,每個隱藏層由多個神經元組成,隱藏層連結上一層與下一層的神經元,經由隱藏層每層多個神經元可以預測複雜的問題。隱藏層層數可以一層到多層,通常使用一層到兩層的隱藏層。隱藏層神經元個數過多會過度學習,隱藏層神經元個數過少會學習不夠,需要適當的隱藏層神經元個數,通常小於兩倍的輸入層神經元個數。

神經網路模型舉例如下。

說明如下。

輸入層與隱藏層

Dense(units=4, input_shape=(2,), activation='relu')

本範例輸入層與隱藏層宣告在同一行,「input_shape=(2,)」表示輸入層每筆資料有兩個欄位,「Dense」表示隱藏層為全連接層,「units=4」表示隱藏層有4個神經元所組成,「activation='relu'」表示隱藏層的激勵函式為relu,示意圖如下。

輸出層

Dense(units=1, activation='sigmoid')

「units=1」表示輸出層有1個神經元所組成,「Dense」表示輸出層為全連接層,「activation='sigmoid'」表示輸出層的激勵函式為sigmoid,激勵函式sigmoid之後介紹。

10-4-1 使用keras實作邏輯閘XOR

(10-4-1-使用神經網路訓練XOR.ipynb)

邏輯閘XOR為非線性問題,無法使用一條直線將輸出的數值0與數值1分割開來,如下圖,輸入(0,0)與(1,1)輸出0,輸入(0,1)與(1,0)輸出1。

需要使用多層的神經網路,經過反覆的學習,才能精確預估結果。程式撰寫步驟如下。

Step1)輸入資料與正確輸出

X表示輸入資料,y表示正確輸出,如下。

X = np.array([[0.,0.],[0.,1.],[1.,0.],[1.,1.]])

y = np.array([0.,1.,1.,0.])


Step2)建立模型

輸入層與隱藏層一起宣告,如下。

model.add(Dense(units=4, input_shape=(2,), kernel_initializer='normal', activation='relu'))

(1)輸入層

「input_shape=(2,)」表示每筆資料輸入2個數值,本範例輸入(0,0)、(0,1)、(1,0)與(1,1)

(2)隱藏層

「Dense」表示全連接層,「units=4」表示使用4個神經元,「kernel_initializer = 'normal'」表示參數W初始化為常態分佈的數值,「activation = 'relu'」表示使用relu為激勵函式。

(3)輸出層

model.add(Dense(units=1, activation='sigmoid'))

本範例輸出數值0或1,只有一個數值,所以「units=1」表示使用1個神經元,輸出一個數值,輸出0或1所以使用sigmoid為激勵函式,該函式輸出值介於0到1。

Step3)設定損失函式與優化器

model.compile(loss='binary_crossentropy', optimizer=SGD(lr=0.01), metrics=['accuracy'])

預測輸出值為0或1,屬於分成兩類,因此損失函式使用binary_crossentropy,「optimizer=SGD(lr=0.01)」表示使用SGD為優化器,優化器(optimizer)用於調整參數W,讓損失(Loss)值降低的演算法策略,「lr=0.01」表示學習率為0.01,「metrics=['accuracy']」表示紀錄訓練過程的正確率。

Step4)訓練模型

「batch_size=4」表示每4筆資料更新一次神經網路中每層參數。「epochs=10000」表示反覆訓練所有輸入資料10000次,「epochs=1」表示輸入所有資料1次,「epochs=10000」表示輸入所有資料10000次。如果本範例的輸入資料有20筆資料,則每4筆資料更新一次參數,所有輸入資料執行完畢會更新5(20除以4)次參數,加上「epochs=10000」表示會更新參數總共50000次。「verbose=0」表示螢幕上不顯示任何訊息。

history = model.fit(X, y, epochs=10000, batch_size=4, verbose=0)


Step5)預測模型

使用方法predict_proba以X為輸入,預測輸出的機率。

model.predict_proba(X)

Step6)儲存模型與載入模型

程式說明

第1到2行:匯入函式庫。

第3到4行:使用函式save儲存模型的參數,使用函式load_model載入模型。

第5到7行:輸入資料X進入模型,使用模型進行預估,獲得yhat,並顯示在螢幕上。

Step7)繪製模型的正確率圖與損失圖

本範例建立模型與訓練模型程式,如下。

model.compile(loss='binary_crossentropy', optimizer=SGD(lr=0.01), metrics=['accuracy'])

history = model.fit(X, y, epochs=10000, batch_size=4, verbose=0)

上面兩行程式的「metrics=['accuracy']」表示紀錄訓練過程的正確率,「history = model.fit(…)」表示訓練過程記錄在history。使用「history.history['accuracy']」讀取模型訓練過程的正確率,接著使用「plt.plot(history.history['accuracy'])」繪製正確率圖。預設會記錄模型的損失率,使用「history.history['loss']」讀取模型訓練過程的損失率,接著使用「plt.plot(history.history['loss'])」繪製損失率圖

程式說明

第1行:匯入函式庫matplotlib.pyplot。

第2行:設定繪圖所需中文字型。

第3行:繪製正確率的圖形。

第4行:設定圖片標題為「模型正確率」。

第5行:設定Y軸標籤為「正確率」。

第6行:設定X軸標籤為「Epoch」。

第7行:顯示繪圖結果到螢幕上。

第8行:繪製損失值的圖形。

第9行:設定圖片標題為「模型損失」。

第10行:設定Y軸標籤為「損失」。

第11行:設定X軸標籤為「Epoch」。

第12行:顯示繪圖結果到螢幕上。

10-5 激勵函式

激勵函式(activation function)大部分屬於非線性函式,以下介紹常用的激勵函式。

10-6 Loss函式

如何判斷神經網路獲得正確的預測結果,使用Loss函式比較預測結果與真正結果的差異,Loss函式所獲得數值越大,表示預測結果越不正確,不斷更新權重W降低Loss函式的值,Loss函式用於判定神經網路是否獲得正確的預測結果,以下介紹常用的Loss函式。

範例如下。

from tensorflow.keras.layers import Dense

from tensorflow.keras.models import Sequential

from tensorflow.keras.optimizers import SGD

model = Sequential()

model.add(Dense(units=4, input_shape=(2,), activation='relu'))

model.add(Dense(units=1, activation='sigmoid'))

model.compile(loss='mse', optimizer=SGD(lr=0.05))

Keras語法如下。

二分類的交叉熵

model.compile(loss=' binary_crossentropy ', …)

多分類的交叉熵

model.compile(loss='categorical_crossentropy', …)

範例如下。

from tensorflow.keras.layers import Dense

from tensorflow.keras.models import Sequential

from tensorflow.keras.optimizers import SGD

model = Sequential()

model.add(Dense(units=4, input_shape=(2,) , activation='relu'))

model.add(Dense(units=1, activation='sigmoid'))

model.compile(loss='binary_crossentropy', optimizer=SGD(lr=0.05))

10-7 學習率與優化器

優化器(Optimizer)可以設定學習率(Learning Rate),如果學習率設定過大,則參數W與W0改變越大,則預測結果改變越大,容易造成無法收斂到最佳解;如果學習率設定過小,則收斂速度很慢,且可能只找到局部最佳解,優化器配合適當的學習率有助於神經網路的學習。優化器用於調整參數(W與W0),讓損失(Loss)值降低的演算法策略,常見的優化器有SGD與ADAM。

SGD(隨機梯度下降法)每次執行一個批次(batch)的資料量,算出此批次資料的梯度平均值,利用此梯度平均值更新參數W,每批次的資料是從所有資料中隨機挑選,所以稱作隨機梯度下降法。

Keras語法如下。

model.compile(optimizer=SGD(lr=0.05) , …)

範例如下。

from tensorflow.keras.layers import Dense

from tensorflow.keras.models import Sequential

from tensorflow.keras.optimizers import SGD

model = Sequential()

model.add(Dense(units=4, input_shape=(2,), activation='relu'))

model.add(Dense(units=1, activation='sigmoid'))

model.compile(loss='binary_crossentropy', optimizer=SGD(lr=0.05))

(2)ADAM

ADAM結合Adagrad、RMSprop及Momentum等優化器的優點,目前最常用的優化器,適用於各種狀況,可以動態決定學習率。

Keras語法如下。

model.compile(optimizer='adam' , …)

範例如下。

from tensorflow.keras.layers import Dense

from tensorflow.keras.models import Sequential

model = Sequential()

model.add(Dense(units=4, input_shape=(2,), activation='relu'))

model.add(Dense(units=1, activation='sigmoid'))

model.compile(loss='binary_crossentropy', optimizer='adam')

10-8 使用手寫數字辨識為範例

(10-8-使用神經網路進行手寫數字辨識.ipynb)

本範例使用手寫阿拉伯數字資料集(mnist),每個手寫數字由長寬各28個像素組成,將這些手寫數字分成數字0到數字9總共10類。

(一)建立模型與訓練模型

程式說明

本範例程式說明分成「資料處理」與「建立、訓練與評估模型」兩部分,分別敘述如下。

(一)資料處理

Step1)載入訓練集與測試集資料

(train_X, train_y), (test_X, test_y) = mnist.load_data()

Step2)目標值轉換成One-hot編碼

例如:目標值只有0到9,若目標值為0,One-hot編碼就會轉換成[1, 0, 0, 0, 0, 0, 0, 0, 0, 0];若目標值為1,One-hot編碼就會轉換成[0, 1, 0, 0, 0, 0, 0, 0, 0, 0];依此類推,若目標值為9,One-hot編碼就會轉換成[0, 0, 0, 0, 0, 0, 0, 0, 0, 1]。轉換成One-hot編碼是為了輸出時,顯示數字0到9的機率需要10個數值,One-hot編碼的1表示該數字機率為100%,0表示該數字機率為0%,神經網路輸出的預估值表示數字0到數字9的機率。

train_y2 = to_categorical(train_y) #轉換成One-hot編碼

test_y2 = to_categorical(test_y) #轉換成One-hot編碼

Step3)train_X的資料集大小為(60000,28,28),神經網路的輸入層為784個輸入,所以將train_X轉換成(60000, 784)用於神經網路的輸入,且轉換成浮點數,接著除以255。test_X的資料集大小為(10000,28,28),神經網路的輸入層為784個輸入,所以將test_X轉換成(10000, 784)用於神經網路的輸入,且轉換成浮點數,接著除以255。。

train_X2 = train_X.reshape(60000, 784).astype('float32')

test_X2 = test_X.reshape(10000, 784).astype('float32')

Step4)將數值0到255轉換成0到1。

train_X2 = train_X2/255 #將數值限制在0到1

test_X2 = test_X2/255 #將數值限制在0到1

(二)建立、訓練與評估模型

本範例輸入mnist資料集,輸出10個類別的其中1種,表示數字0到數字9。

Step5)建立模型

輸入層與隱藏層一起宣告,如下。

model.add(Dense(256, input_shape=(784,), kernel_initializer='normal', activation='relu'))

(1)輸入層

「input_shape=(784,)」表示每筆資料輸入784個數值,表示輸入一張圖片,本範例圖片由寬度28像素,高度28像素所組成,28乘以28等於784。

(2)隱藏層

「Dense」表示輸出層為全連接層,「256」表示隱藏層使用256個神經元,「kernel_initializer='normal'」表示參數W初始化為常態分佈的數值,「activation='relu'」表示使用relu為激勵函式。

(3)輸出層

model.add(Dense(10, kernel_initializer='normal', activation='softmax'))

本範例輸出10個數值,所以「10」表示使用10個神經元,「kernel_initializer='normal'」表示參數W初始化為常態分佈的數值,「activation='softmax'」表示使用softmax為激勵函式,該函式輸出值相加等於1,適合表示每個類別的機率。

設定損失函式與優化器

model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

預測輸出值由10個元素組成,屬於分成多類別,因此損失函式使用categorical_crossentropy。「optimizer='adam'」表示使用adam為優化器,優化器(optimizer)用於調整參數W,讓損失值降低的演算法策略。「metrics=['accuracy']」表示紀錄訓練過程的正確率。


Step6)訓練模型

「x=train_X2, y=train_y2」表示train_X2為輸入資料,train_y2為目標值。「validation_split=0.2」表示設定驗證資料的比率為0.2,表示訓練資料的80%拿來訓練模型。「batch_size=1000」表示每1000筆資料更新一次神經網路中每層參數,「epochs=10」表示反覆訓練所有輸入資料10次。「verbose=2」表示螢幕顯示訓練過程的詳細訊息。

history = model.fit(x=train_X2, y=train_y2, validation_split=0.2, epochs=10, batch_size=1000, verbose=2)


Step7)輸入測試資料到模型進行預測

輸入測試資料集test_X2到模型,比較預估結果與目標結果test_y2的差異來評估模型。

score = model.evaluate(test_X2, test_y2)

執行結果

執行結果如下,可以觀察到訓練過程中正確率不斷提高,損失不斷降低,但在第6次訓練後(Epoch = 6)後其實正確率差異不大了。

Model: "sequential_1"

_________________________________________________________________

Layer (type) Output Shape Param #

=================================================================

dense_2 (Dense) (None, 256) 200960

_________________________________________________________________

dense_3 (Dense) (None, 10) 2570

=================================================================

Total params: 203,530

Trainable params: 203,530

Non-trainable params: 0

None

Epoch 1/10

48/48 - 0s - loss: 0.8793 - accuracy: 0.7918 - val_loss: 0.3477 - val_accuracy: 0.9027

Epoch 2/10

48/48 - 0s - loss: 0.3147 - accuracy: 0.9121 - val_loss: 0.2566 - val_accuracy: 0.9302

Epoch 3/10

48/48 - 0s - loss: 0.2468 - accuracy: 0.9314 - val_loss: 0.2188 - val_accuracy: 0.9397

Epoch 4/10

48/48 - 0s - loss: 0.2052 - accuracy: 0.9429 - val_loss: 0.1870 - val_accuracy: 0.9498

Epoch 5/10

48/48 - 0s - loss: 0.1745 - accuracy: 0.9506 - val_loss: 0.1675 - val_accuracy: 0.9533

Epoch 6/10

48/48 - 0s - loss: 0.1526 - accuracy: 0.9574 - val_loss: 0.1516 - val_accuracy: 0.9591

Epoch 7/10

48/48 - 0s - loss: 0.1350 - accuracy: 0.9622 - val_loss: 0.1389 - val_accuracy: 0.9622

Epoch 8/10

48/48 - 0s - loss: 0.1200 - accuracy: 0.9665 - val_loss: 0.1320 - val_accuracy: 0.9625

Epoch 9/10

48/48 - 0s - loss: 0.1086 - accuracy: 0.9697 - val_loss: 0.1233 - val_accuracy: 0.9660

Epoch 10/10

48/48 - 0s - loss: 0.0982 - accuracy: 0.9728 - val_loss: 0.1152 - val_accuracy: 0.9675

313/313 [==============================] - 0s 1ms/step - loss: 0.1117 - accuracy: 0.9660

[0.11170680820941925, 0.9660000205039978]

正確率為 96.60000205039978 %

延伸應用 -- 繪製訓練模型的正確率圖與損失圖

繪製模型訓練過程的正確率圖與損失圖

程式說明

第1行:匯入函式庫。

第2行:設定繪圖所需中文字型。

第3行:繪製訓練集的正確率。

第4行:繪製測試集的正確率。

第5行:設定圖片標題為「模型正確率」。

第6行:設定Y軸標籤為「正確率」。

第7行:設定X軸標籤為「Epoch」。

第8行:設定圖說在左上方,標示為「Train」與「Test」。

第9行:顯示繪圖結果到螢幕上。

第10行:繪製訓練集的損失值。

第11行:繪製測試集的損失值。

第12行:設定圖片標題為「模型損失」。

第13行:設定Y軸標籤為「損失」。

第14行:設定X軸標籤為「Epoch」。

第15行:設定圖說在左上方,標示為「Train」與「Test」。

第16行:顯示繪圖結果到螢幕上。

機器學習的重要概念 — 過適與乏適

機器學習模型對於訓練資料有很好的預測結果,但對於測試資料預測結果不佳,稱作Overfitting(過適或擬合過度),表示模型過度適應訓練資料,無法在測試資料獲得好的預測結果,無法適用於所有(一般)資料。本範例的正確率圖如下圖,在Epoch等於6以後,發現訓練資料正確率提升,而測試資料正確率並未明顯提升,就有一點過適現象,但本範例其實不明顯。

下圖為典型的過適,訓練資料的正確率到達90%以上,而測試資料正確率仍在80%以下,表示機器學習已經完全適應訓練資料,但輸入非訓練資料時無法正確預估,此時就要降低神經元個數,隨機丟棄(Dropout)某些參數來降低學習效果,或使用其他機器學習模型等改善過適問題。

為了防止過適,可以新增Dropout功能,隨機讓某些神經元沒有作用來減少學習,範例程式如下,其中「Dropout(0.2)」表示有20%的神經元沒有作用。

model = Sequential()

model.add(Dense(256, input_shape=(784,), kernel_initializer='normal', activation='relu'))

model.add(Dropout(0.2))

model.add(Dense(10, kernel_initializer='normal', activation='softmax'))

model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

機器學習模型對於訓練資料與測試資料預測結果都不好,稱作Underfitting(乏適或擬合不足),表示模型訓練不足。下圖為典型的乏適,不管訓練資料與測試資料輸入模型正確率都未達80%,此時需要增加每層的神經元個數,增加神經網路層數,或使用其他機器學習模型等改善乏適問題。

延伸應用 -- 儲存、載入與預測模型

儲存數字辨識範例程式所建立的模型參數,載入儲存的模型參數到新的模型,利用此新模型進行預測。

程式說明

第1到3行:匯入所需函式庫。

第4行:設定繪圖所需中文字型。

第5行:使用save儲存模型參數到minst_model.ann。

第6行:使用load_model載入模型minst_model.ann。

第8到19行:自訂函式draw_digit顯示從index開始的5個手寫數字,digits為手寫資料集,y為目標值,pred_y為預測值。

第9到19行:使用for迴圈,迴圈變數i由0到4每次遞增1,使用函式gcf新增繪圖區域(第10行),設定fig的大小為寬12吋高1吋(第11行),將分割為五個區域(第12行),將陣列digits第index個的手寫數字,使用imshow顯示手寫數字到螢幕上(第13行)。設定圖片標題為「原始為」與目標值(第14行),圖片標題串接「預估為」與預估值(第15行),設定標題字型為10(第16行)。清空X軸刻度(第17行),清空Y軸刻度(第18行),使用函式show顯示五張手寫數字(第19行)。

第21行:使用函式predict以test_X2為輸入,輸出結果到pred_y。

第22行:使用函式argmax以pred_y為輸入,將one-hot編碼還原到數值0到9。

第23到24行:使用for迴圈,其迴圈變數i由0到19每次遞增5,使用函式draw_digit顯示前20個數字,一列五個手寫數字。

延伸應用 -- 找出混淆矩陣

建立目標值與預測值的混淆矩陣。

程式說明

第1行:匯入函式庫。

第2行:使用函式predict_classes輸入test_X2,輸出pred_y。

第3行:比較test_ypred_y建立混淆矩陣cm

4行:顯示混淆矩陣cm到螢幕。

延伸應用 -- 只顯示辨識錯誤的圖片

只顯示測試集經由模型產生的預測值與目標值不同的手寫數字。

程式說明

第1到2行:匯入所需函式庫。

第3行:設定繪圖所需中文字型。

第5到15行:自訂函式draw_digit顯示索引值為index的手寫數字,digits為手寫資料集,y為目標值,pred_y為預測值。使用函式gcf新增繪圖區域(第6行),設定fig的大小為寬1吋高1吋(第7行),分割為只有一個區域(第8行),使用imshow顯示陣列digits第index個手寫數字到螢幕上 (第9行)。設定圖片標題為「原始為」與目標值(第10行),圖片標題串接「預估為」與預估值(第11行),設定標題字型為10(第12行)。清空X軸刻度(第13行),清空Y軸刻度(第14行),使用函式show顯示手寫數字到螢幕上(第15行)。

第17行:比較test_y與pred_y的不同之處,產生布林陣列到陣列diff,1表示兩數字不同,0表示兩數字相同。

第18行:顯示「辨識錯誤個數」,加總陣列diff的數值。

第19行:找出陣列diff元素為True的索引值到陣列diff_idx。

第20行:印出陣列diff_idx。

第21到22行:使用for迴圈取出陣列diff_idx前10個元素為索引值,使用函式draw_digit顯示該索引值的手寫數字圖片到螢幕上。