In [1]:
import keras
keras.__version__


Using TensorFlow backend.
Out[1]:
'2.3.1'

Python Keras MNIST 手寫辨識

這是一個神經網路的範例,利用了 Python Keras 來訓練一個手寫辨識分類 Model。

我們要的問題是將手寫數字的灰度圖像(28x28 Pixel)分類為 10 類(0至9)。使用的數據集是 MNIST 典數據集,它是由國家標準技術研究所(MNIST 的 NIST)在1980年代組裝而成的,包含 60,000 張訓練圖像和 10,000 張測試圖像。您可以將「解決」MNIST 視為深度學習的 "Hello World"。

由於 Keras 已經整理了一些經典的 Play Book Data,因此我們可以很快透過以下方式取得 MNIST 資料集


In [2]:
from keras.datasets import mnist

(train_images, train_labels), (test_images, test_labels) = mnist.load_data()


Downloading data from https://s3.amazonaws.com/img-datasets/mnist.npz
11493376/11490434 [==============================] - 0s 0us/step

images 是用來訓練與測試的資料,label 則為每一筆影像資料對應的正確答案,每一張手寫圖片都是 28 x 28 的灰階 Bit Map


In [3]:
train_images.shape


Out[3]:
(60000, 28, 28)

In [4]:
len(train_labels)


Out[4]:
60000

In [5]:
train_labels


Out[5]:
array([5, 0, 4, ..., 5, 6, 8], dtype=uint8)

In [6]:
test_images.shape


Out[6]:
(10000, 28, 28)

In [7]:
len(test_labels)


Out[7]:
10000

In [8]:
test_labels


Out[8]:
array([7, 2, 1, ..., 4, 5, 6], dtype=uint8)

建立準備訓練的神經網路


In [0]:
from keras import models
from keras import layers

network = models.Sequential()
network.add(layers.Dense(512, activation='relu', input_shape=(28 * 28,)))
network.add(layers.Dense(10, activation='softmax'))

上面這裡是神經網路的核心組成方式,我們在全連接層建立了兩層,由一個有 512 個神經元的網路架構連接到 10 個神經元的輸出層。輸出層採用 softmax 表示數字 0~9 的機率分配,這 10 個數字的總和將會是 1。以下將我們建立的網路進行 compile,這裡詳細的參數以後會介紹。


In [0]:
network.compile(optimizer='rmsprop',
                loss='categorical_crossentropy',
                metrics=['accuracy'])

以下將資料正規劃成為 0~1 的數值,變成 60000, 28x28 Shape 好送進上面定義的網路輸入層。


In [0]:
fix_train_images = train_images.reshape((60000, 28 * 28)).astype('float32') / 255
fix_test_images = test_images.reshape((10000, 28 * 28)).astype('float32') / 255

由於我們使用的 categorical_crossentropy 損失函數,因此將標記資料進行格式轉換。如下:


In [0]:
from keras.utils import to_categorical

fix_train_labels = to_categorical(train_labels)
fix_test_labels = to_categorical(test_labels)

進行訓練模型,訓練中的正確率應該會在 0.989 左右


In [13]:
result = network.fit(
    fix_train_images,
    fix_train_labels,
    epochs=20,
    batch_size=128,
    validation_data=(fix_test_images, fix_test_labels))


Train on 60000 samples, validate on 10000 samples
Epoch 1/20
60000/60000 [==============================] - 4s 63us/step - loss: 0.2536 - accuracy: 0.9264 - val_loss: 0.1373 - val_accuracy: 0.9574
Epoch 2/20
60000/60000 [==============================] - 2s 36us/step - loss: 0.1035 - accuracy: 0.9682 - val_loss: 0.0968 - val_accuracy: 0.9688
Epoch 3/20
60000/60000 [==============================] - 2s 36us/step - loss: 0.0677 - accuracy: 0.9797 - val_loss: 0.0817 - val_accuracy: 0.9750
Epoch 4/20
60000/60000 [==============================] - 2s 36us/step - loss: 0.0488 - accuracy: 0.9852 - val_loss: 0.0690 - val_accuracy: 0.9797
Epoch 5/20
60000/60000 [==============================] - 2s 34us/step - loss: 0.0376 - accuracy: 0.9886 - val_loss: 0.0673 - val_accuracy: 0.9789
Epoch 6/20
60000/60000 [==============================] - 2s 36us/step - loss: 0.0289 - accuracy: 0.9913 - val_loss: 0.0658 - val_accuracy: 0.9793
Epoch 7/20
60000/60000 [==============================] - 2s 35us/step - loss: 0.0226 - accuracy: 0.9936 - val_loss: 0.0630 - val_accuracy: 0.9822
Epoch 8/20
60000/60000 [==============================] - 2s 35us/step - loss: 0.0169 - accuracy: 0.9953 - val_loss: 0.0641 - val_accuracy: 0.9823
Epoch 9/20
60000/60000 [==============================] - 2s 37us/step - loss: 0.0138 - accuracy: 0.9961 - val_loss: 0.0695 - val_accuracy: 0.9820
Epoch 10/20
60000/60000 [==============================] - 2s 35us/step - loss: 0.0099 - accuracy: 0.9973 - val_loss: 0.0698 - val_accuracy: 0.9815
Epoch 11/20
60000/60000 [==============================] - 2s 35us/step - loss: 0.0081 - accuracy: 0.9977 - val_loss: 0.0744 - val_accuracy: 0.9807
Epoch 12/20
60000/60000 [==============================] - 2s 36us/step - loss: 0.0062 - accuracy: 0.9983 - val_loss: 0.0732 - val_accuracy: 0.9815
Epoch 13/20
60000/60000 [==============================] - 2s 37us/step - loss: 0.0049 - accuracy: 0.9987 - val_loss: 0.0704 - val_accuracy: 0.9829
Epoch 14/20
60000/60000 [==============================] - 2s 36us/step - loss: 0.0042 - accuracy: 0.9990 - val_loss: 0.0785 - val_accuracy: 0.9815
Epoch 15/20
60000/60000 [==============================] - 2s 36us/step - loss: 0.0030 - accuracy: 0.9991 - val_loss: 0.0785 - val_accuracy: 0.9830
Epoch 16/20
60000/60000 [==============================] - 2s 36us/step - loss: 0.0027 - accuracy: 0.9992 - val_loss: 0.0797 - val_accuracy: 0.9829
Epoch 17/20
60000/60000 [==============================] - 2s 35us/step - loss: 0.0018 - accuracy: 0.9995 - val_loss: 0.0809 - val_accuracy: 0.9831
Epoch 18/20
60000/60000 [==============================] - 2s 36us/step - loss: 0.0018 - accuracy: 0.9995 - val_loss: 0.0830 - val_accuracy: 0.9826
Epoch 19/20
60000/60000 [==============================] - 2s 36us/step - loss: 0.0011 - accuracy: 0.9997 - val_loss: 0.0895 - val_accuracy: 0.9826
Epoch 20/20
60000/60000 [==============================] - 2s 35us/step - loss: 8.4929e-04 - accuracy: 0.9998 - val_loss: 0.0853 - val_accuracy: 0.9835

將訓練後的模型輸入測試資料進行評比,一般說這樣的正確率應該會在 0.977% 左右


In [14]:
test_loss, test_acc = network.evaluate(fix_test_images, fix_test_labels)
print('test_loss:', test_loss)
print('test_acc:', test_acc)


10000/10000 [==============================] - 1s 77us/step
test_loss: 0.08534976623827886
test_acc: 0.9835000038146973

為什麽訓練時的正確率會高於驗證測試呢?在這樣數據中,由於模型訓練時對訓練資料造成些微的過度擬合 (Over Fitting) 。一般來說這樣的情況是正常的,未來我們可以透過參數的調整或其他方法提高正確性。

透過圖表協助分析訓練過程

由於訓練 Model 時會進行好幾次的 Epoch,每一次 Epoch 都是對訓練資料集進行一輪完整的訓練,妥善觀察每一次 Epoch 的數據是很重要地。我們可以透過 matplotlib 函式庫繪製圖表,幫我們進行分析。

以下方式可以繪製訓練過程 Loss Function 對應的損失分數。Validation loss 不一定會跟隨 Training loss 一起降低,當 Model Over Fitting Train Data 時,就會發生 Validation loss 上升的情況。


In [15]:
history_dict = result.history

loss_values = history_dict['loss']
val_loss_values = history_dict['val_loss']
epochs = range(1, len(loss_values) + 1)

import matplotlib.pyplot as plt
plt.plot(epochs, loss_values, 'bo', label='Training loss')
plt.plot(epochs, val_loss_values, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()

plt.show()


以下程式可以繪製訓練過程的正確率變化。訓練的過程中,當 Accuracy 後期並有沒太大的變化,表示 Model 很快就在假設空間裡進行不錯的收斂。


In [16]:
plt.clf()
acc = history_dict['accuracy']
val_acc = history_dict['val_accuracy']

plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.legend()

plt.show()