我們終於要開始做生命中第一個神經網路...
再來是我們標準數據分析動作!
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
Keras 很貼心的幫我們準備好 MNIST 數據庫, 我們可以這樣讀進來 (第一次要花點時間)。
from keras.datasets import mnist
Using TensorFlow backend.
(x_train, y_train), (x_test, y_test) = mnist.load_data()
我們來看看訓練資料是不是 6 萬筆、測試資料是不是有 1 筆。
len(x_train)
60000
len(x_test)
10000
特別要注意的是, 萬一在讀的過程中失敗, 你需要找到下載的部份數據集刪去, 然後在一個網路通𣈱的地方再下載一次。
每筆輸入 (x) 就是一個手寫的 0-9 中一個數字的圖檔, 大小為 28x28。而輸出 (y) 當然就是「正確答案」。我們來看看編號 87 的訓練資料。
n = 87
X = x_train[n]
X
array([[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 125, 225, 254, 254, 255, 254, 170, 48, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 101, 250, 253, 253, 253, 253, 253, 253, 253, 250, 161, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 94, 246, 247, 253, 253, 196, 227, 116, 56, 253, 253, 253, 234, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 152, 253, 253, 180, 19, 9, 15, 0, 4, 55, 253, 253, 166, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 41, 238, 253, 253, 125, 0, 0, 0, 21, 189, 232, 253, 253, 117, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 219, 253, 220, 165, 34, 92, 21, 52, 228, 253, 253, 241, 82, 13, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 38, 241, 170, 25, 20, 12, 75, 39, 59, 253, 253, 253, 110, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 112, 253, 236, 67, 0, 0, 0, 0, 100, 253, 253, 221, 16, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 23, 239, 253, 235, 202, 135, 99, 173, 240, 253, 253, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 54, 200, 253, 253, 253, 253, 253, 253, 253, 241, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 112, 244, 253, 237, 142, 253, 253, 111, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, 71, 51, 159, 253, 188, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 150, 236, 212, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 99, 253, 243, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 237, 253, 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 219, 253, 195, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 171, 253, 207, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 105, 253, 198, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 60, 242, 253, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 235, 253, 206, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=uint8)
X.shape
(28, 28)
因為是圖檔, 當然可以顯示出來!
plt.imshow(X, cmap = 'Greys')
<matplotlib.image.AxesImage at 0x7fb70a894b00>
y_train[n]
9
我們現在要用標準神經網路學學手寫辨識。原來的每筆數據是個 28x28 的矩陣 (array), 但標準神經網路只吃「平平的」, 也就是每次要 28x28=784 長的向量。因此我們要用 reshape
調校一下。
x_train.shape
(60000, 28, 28)
x_test.shape
(10000, 28, 28)
x_train = x_train.reshape(60000, 784)/255
x_test = x_test.reshape(10000, 784)/255
我們可能會想, 我們想學的函數是這樣的型式:
$$\hat{f} \colon \mathbb{R}^{784} \to \mathbb{R}$$其實這樣不太好! 為什麼呢? 比如說我們的輸入 x 是一張 0 的圖, 因為我們訓練的神經網路總會有點誤差, 所以可能會得到:
$$\hat{f}(x) = 0.5$$那這意思是有可能是 0, 也有可能是 1 嗎!!?? 可是 0 和 1 根本不像啊。換句話說分類的問題這樣做其實不合理!
於是我們會做 "1-hot enconding", 也就是
等等。因為分類問題基本上都要做這件事, Keras 其實已幫我們準備好套件!
from keras.utils import np_utils
y_train = np_utils.to_categorical(y_train, 10)
y_test = np_utils.to_categorical(y_test, 10)
我們來看看剛剛是 9 的 87 號數據的答案。
y_train[n]
array([0., 0., 0., 0., 0., 0., 0., 0., 0., 1.], dtype=float32)
和我們想的一樣! 至此我們可以打造我們的神經網路了。
我們決定了我們的函數是
$$\hat{f} \colon \mathbb{R}^{784} \to \mathbb{R}^{10}$$這個樣子。而我們又說第一次要用標準神網路試試, 所以我們只需要再決定要幾個隱藏層、每層要幾個神經元, 用哪個激發函數就可以了。
假如我們要這麼做:
使用 3 個 hidden layers
Hidden layer 1 用 88 個神經元
Hidden layer 2 用 168 個神經元
Hidden layer 2 用 888 個神經元
Activation Function 唯一指名 relu
於是從 Keras 把相關套件讀進來。
from keras.models import Sequential
from keras.layers import Dense, Activation
from keras.optimizers import SGD
和以前做迴歸或機器學習一樣, 我們就打開個「函數學習機」。標準一層一層傳遞的神經網路叫 Sequential
, 於是我們打開一個空的神經網路。
model = Sequential()
我們每次用 add
去加一層, 從第一個隱藏層開始。而第一個隱藏層因為 Keras 當然猜不到輸入有幾個 features, 所以我們要告訴它。
model.add(Dense(88, input_dim=784, activation='relu'))
第二層 hidden layer 因為前面輸出是 4, 現在輸入是 2, 就不用再說了! 這裡的 2 只告訴 Keras, 我們第二層是用 2 個神經元!
model.add(Dense(168, activation='relu'))
model.add(Dense(888, activation='relu'))
輸出有 10 個數字, 所以輸出層的神經元是 10 個! 而如果我們的網路輸出是
$$(y_1, y_2, \ldots, y_{10})$$我們還希望
$$\sum_{i=1}^{10} y_i = 1$$這可能嗎, 結果是很容易, 就用 softmax
當激發函數就可以!!
model.add(Dense(10, activation='softmax'))
至此我們的第一個神經網路就建好了!
和之前比較不一樣的是我們還要做 compile
才正式把我們的神經網路建好。你可以發現我們還需要做幾件事:
mse
為了一邊訓練一邊看到結果, 我們加設
metrics=['accuracy']
本行基本上和我們的神經網路功能沒有什麼關係。
model.compile(loss='mse', optimizer=SGD(lr=0.087), metrics=['accuracy'])
我們可以檢視我們神經網路的架構, 可以確認一下是不是和我們想像的一樣。
model.summary()
_________________________________________________________________ Layer (type) Output Shape Param # ================================================================= dense_1 (Dense) (None, 88) 69080 _________________________________________________________________ dense_2 (Dense) (None, 168) 14952 _________________________________________________________________ dense_3 (Dense) (None, 888) 150072 _________________________________________________________________ dense_4 (Dense) (None, 10) 8890 ================================================================= Total params: 242,994 Trainable params: 242,994 Non-trainable params: 0 _________________________________________________________________
很快算算參數數目和我們想像是否是一樣的!
784*10 + 10
7850
恭喜! 我們完成了第一個神經網路。現在要訓練的時候, 你會發現不是像以前沒頭沒腦把訓練資料送進去就好。這裡我們還有兩件事要決定:
batch_size
), 我們就 100 筆調一次參數好了epochs
), 我們訓練個 20 次試試於是最精彩的就來了。你要有等待的心理準備...
model.fit(x_train, y_train, batch_size=100, epochs=20)
Epoch 1/20 60000/60000 [==============================] - 2s 41us/step - loss: 0.0891 - acc: 0.2547 Epoch 2/20 60000/60000 [==============================] - 1s 19us/step - loss: 0.0824 - acc: 0.3999 Epoch 3/20 60000/60000 [==============================] - 1s 19us/step - loss: 0.0567 - acc: 0.6485 Epoch 4/20 60000/60000 [==============================] - 1s 18us/step - loss: 0.0309 - acc: 0.8185 Epoch 5/20 60000/60000 [==============================] - 1s 18us/step - loss: 0.0216 - acc: 0.8698 Epoch 6/20 60000/60000 [==============================] - 1s 19us/step - loss: 0.0181 - acc: 0.8874 Epoch 7/20 60000/60000 [==============================] - 1s 19us/step - loss: 0.0163 - acc: 0.8963 Epoch 8/20 60000/60000 [==============================] - 1s 18us/step - loss: 0.0152 - acc: 0.9029 Epoch 9/20 60000/60000 [==============================] - 1s 19us/step - loss: 0.0143 - acc: 0.9077 Epoch 10/20 60000/60000 [==============================] - 1s 18us/step - loss: 0.0137 - acc: 0.9120 Epoch 11/20 60000/60000 [==============================] - 1s 18us/step - loss: 0.0132 - acc: 0.9153 Epoch 12/20 60000/60000 [==============================] - 1s 18us/step - loss: 0.0127 - acc: 0.9186 Epoch 13/20 60000/60000 [==============================] - 1s 19us/step - loss: 0.0123 - acc: 0.9210 Epoch 14/20 60000/60000 [==============================] - 1s 18us/step - loss: 0.0119 - acc: 0.9237 Epoch 15/20 60000/60000 [==============================] - 1s 18us/step - loss: 0.0115 - acc: 0.9263 Epoch 16/20 60000/60000 [==============================] - 1s 18us/step - loss: 0.0112 - acc: 0.9285 Epoch 17/20 60000/60000 [==============================] - 1s 18us/step - loss: 0.0109 - acc: 0.9305 Epoch 18/20 60000/60000 [==============================] - 1s 19us/step - loss: 0.0107 - acc: 0.9319 Epoch 19/20 60000/60000 [==============================] - 1s 19us/step - loss: 0.0104 - acc: 0.9341 Epoch 20/20 60000/60000 [==============================] - 1s 18us/step - loss: 0.0102 - acc: 0.9354
<keras.callbacks.History at 0x7fb70a8ba2b0>
我們來用比較炫的方式來看看可愛的神經網路學習成果。對指令有問題可以參考我們之前的 MOOC 影片教學。
from ipywidgets import interact_manual
我們 "predict" 放的是我們神經網路的學習結果。這裡用 predict_classes
會讓我們 Keras 選 10 個輸出機率最大的那類。
predict = model.predict_classes(x_test)
predict
array([7, 0, 1, ..., 9, 6, 6])
不要忘了我們的 x_test
每筆資料已經換成 784 維的向量, 我們要整型回 28x28 的矩陣才能當成圖形顯示出來!
def test(測試編號):
plt.imshow(x_test[測試編號].reshape(28,28), cmap='Greys')
print('神經網路判斷為:', predict[測試編號])
test(87)
神經網路判斷為: 3
interact_manual(test, 測試編號=(0, 9999))
interactive(children=(IntSlider(value=4999, description='測試編號', max=9999), Button(description='Run Interact', …
<function __main__.test(測試編號)>
到底測試資料總的狀況如何呢? 我們可以給我們神經網路「考一下試」。
score = model.evaluate(x_test, y_test)
10000/10000 [==============================] - 0s 24us/step
print('loss:', score[0])
print('正確率', score[1])
loss: 0.08840728223323822 正確率 0.217
如果對訓練成果滿意, 我們當然不想每次都再訓練一次! 我們可以把神經網路的架構和訓練好的參數都存起來, 以供日後使用!
之前還沒裝 pyh5 要在終端機 (Anaconda Prompt) 下安裝:
conda install h5py
model_json = model.to_json()
open('stupid_model.json', 'w').write(model_json)
model.save_weights('stupid_model_weights.h5')