1. 程式人生 > >keras入門實戰:手寫數字識別

keras入門實戰:手寫數字識別

如果 turn wid 寬度 initial 作用 err examples 預測

近些年由於理論知識的硬件的快速發展,使得深度學習達到了空前的火熱。深度學習已經在很多方面都成功得到了應用,尤其是在圖像識別和分類領域,機器識別圖像的能力甚至超過了人類。

本文用深度學習Python庫Keras實現深度學習入門教程mnist手寫數字識別。mnist手寫數字識別是機器學習和深度學習領域的“hello world”,MNIST數據集是手寫數字的數據集合,訓練集規模為60000,測試集為10000。

本文的內容包括:

  • 如何用Keras加載MNIST數據集
  • 對於MNIST問題如何實現一個baseline的神經網絡
  • 基於MNIST問題如何實現並評價一個卷積神經網絡(CNN)
  • 基於MNIST問題如何實現一個接近最高準確率的深度學習模型

MNIST手寫數字識別問題

MNIST問題是由Yann LeCun, Corinna Cortes 和Christopher Burges為了評估機器學習模型而設立的。問題的數據集是從一些National Institute of Standards and Technology (NIST)的文檔中得來,是計算機視覺入門級的數據集,它包含各種手寫數字圖片:
技術分享

它也包含每一張圖片對應的標簽,告訴我們這個是數字幾。比如,上面這四張圖片的標簽分別是5,0,4,1。
每張圖片是28*28像素(共784個像素)。對於一般的圖片像素通道通常是3維,即rgb,代表red、green、blue三個顏色通道,而MNIST數據集的像素通道只有一位即為灰度值,每一個像素值在0到1之間表示這個像素的灰度,0表示白色,1表示黑色。圖片的類別標簽是這個圖片的數字,取值範圍為0-9.因此MNIST問題是一個多分類的問題,類別為10。
現在好的分類結果可是使錯誤率降到1%以下。接近最好效果的錯誤率大約為0.2%,可用大規模的CNN實現。

加載MNIST數據集

Keras提供了實現深度學習所需要的絕大部分函數庫,可實現多種神經網絡模型,並可加載多種數據集來評價模型的效果。下面的代碼會自動加載數據,如果是第一次調用,數據會保存在你的hone目錄下~/.keras/datasets/mnist.pkl.gz,大約15MB。

# Plot ad hoc mnist instances
from keras.datasets import mnist
import matplotlib.pyplot as plt
# load (downloaded if needed) the MNIST dataset
(X_train, y_train), (X_test, y_test) = mnist.load_data()
# plot 4 images as gray scale
plt.subplot(221)
plt.imshow(X_train[0], cmap=plt.get_cmap(‘gray‘))
plt.subplot(222)
plt.imshow(X_train[1], cmap=plt.get_cmap(‘gray‘))
plt.subplot(223)
plt.imshow(X_train[2], cmap=plt.get_cmap(‘gray‘))
plt.subplot(224)
plt.imshow(X_train[3], cmap=plt.get_cmap(‘gray‘))
# show the plot
plt.show()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

上面的代碼加載了數據集並畫出了前4個圖片:
技術分享

多層感知機的baseline模型

在實現卷積神經網絡這種復雜的模型之前,先實現一個簡單但效果也不錯的模型:多層感知機。這種模型也叫含隱層的神經網絡。模型的效果可以使錯誤率達到1.87%。
第一步是加載所需要的庫

import numpy
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Dropout
from keras.utils import np_utils
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

設定隨機數種子,保證結果的可重現性

seed = 7
numpy.random.seed(seed)
  • 1
  • 2
  • 1
  • 2

加載數據

(X_train, y_train), (X_test, y_test) = mnist.load_data()
  • 1
  • 1

數據集是3維的向量(instance length,width,height).對於多層感知機,模型的輸入是二維的向量,因此這裏需要將數據集reshape,即將28*28的向量轉成784長度的數組。可以用numpy的reshape函數輕松實現這個過程。

num_pixels = X_train.shape[1] * X_train.shape[2]
X_train = X_train.reshape(X_train.shape[0], num_pixels).astype(‘float32‘)
X_test = X_test.reshape(X_test.shape[0], num_pixels).astype(‘float32‘)
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

給定的像素的灰度值在0-255,為了使模型的訓練效果更好,通常將數值歸一化映射到0-1。

X_train = X_train / 255
X_test = X_test / 255
  • 1
  • 2
  • 1
  • 2

最後,模型的輸出是對每個類別的打分預測,對於分類結果從0-9的每個類別都有一個預測分值,表示將模型輸入預測為該類的概率大小,概率越大可信度越高。由於原始的數據標簽是0-9的整數值,通常將其表示成0ne-hot向量。如第一個訓練數據的標簽為5,one-hot表示為[0,0,0,0,0,1,0,0,0,0]。

y_train = np_utils.to_categorical(y_train)
y_test = np_utils.to_categorical(y_test)
num_classes = y_test.shape[1]
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

現在需要做得就是搭建神經網絡模型了,創建一個函數,建立含有一個隱層的神經網絡。

# define baseline model
def baseline_model():
    # create model
    model = Sequential()
    model.add(Dense(num_pixels, input_dim=num_pixels, kernel_initializer=‘normal‘, activation=‘relu‘))
    model.add(Dense(num_classes, kernel_initializer=‘normal‘, activation=‘softmax‘))
    # Compile model
    model.compile(loss=‘categorical_crossentropy‘, optimizer=‘adam‘, metrics=[‘accuracy‘])
    return model
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

模型的隱含層含有784個節點,接受的輸入長度也是784(28*28),最後用softmax函數將預測結果轉換為標簽的概率值。
將訓練數據fit到模型,設置了叠代輪數,每輪200個訓練樣本,將測試集作為驗證集,並查看訓練的效果。

# build the model
model = baseline_model()
# Fit the model
model.fit(X_train, y_train, validation_data=(X_test, y_test), epochs=10, batch_size=200, verbose=2)
# Final evaluation of the model
scores = model.evaluate(X_test, y_test, verbose=0)
print("Baseline Error: %.2f%%" % (100-scores[1]*100))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

訓練和測試結果如下:

Train on 60000 samples, validate on 10000 samples
Epoch 1/10
6s - loss: 0.2789 - acc: 0.9210 - val_loss: 0.1416 - val_acc: 0.9578
Epoch 2/10
5s - loss: 0.1117 - acc: 0.9677 - val_loss: 0.0917 - val_acc: 0.9707
Epoch 3/10
5s - loss: 0.0717 - acc: 0.9796 - val_loss: 0.0787 - val_acc: 0.9767
Epoch 4/10
6s - loss: 0.0502 - acc: 0.9859 - val_loss: 0.0741 - val_acc: 0.9767
Epoch 5/10
5s - loss: 0.0372 - acc: 0.9890 - val_loss: 0.0681 - val_acc: 0.9788
Epoch 6/10
5s - loss: 0.0269 - acc: 0.9925 - val_loss: 0.0625 - val_acc: 0.9808
Epoch 7/10
5s - loss: 0.0208 - acc: 0.9948 - val_loss: 0.0619 - val_acc: 0.9814
Epoch 8/10
6s - loss: 0.0140 - acc: 0.9970 - val_loss: 0.0639 - val_acc: 0.9799
Epoch 9/10
5s - loss: 0.0108 - acc: 0.9978 - val_loss: 0.0597 - val_acc: 0.9812
Epoch 10/10
5s - loss: 0.0080 - acc: 0.9985 - val_loss: 0.0591 - val_acc: 0.9813
Baseline Error: 1.87%

簡單的卷積神經網絡

前面介紹了如何加載訓練數據並實現一個簡單的單隱層神經網絡,並在測試集上取得了不錯的效果。現在要實現一個卷積神經網絡,想要在MNIST問題上取得更好的效果。

卷積神經網絡(CNN)是一種深度神經網絡,與單隱層的神經網絡不同的是它還包含卷積層、池化層、Dropout層等,這使得它在圖像分類的問題上有更優的效果。詳細的CNN教程可以參見斯坦福大學的cs231n課程講義,中文版鏈接

第一步依然是導入需要的函數庫

import numpy
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Dropout
from keras.layers import Flatten
from keras.layers.convolutional import Conv2D
from keras.layers.convolutional import MaxPooling2D
from keras.utils import np_utils
from keras import backend as K
K.set_image_dim_ordering(‘th‘)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

設定隨機數種子

seed = 7
numpy.random.seed(seed)
  • 1
  • 2
  • 1
  • 2

將數據集reshape,CNN的輸入是4維的張量(可看做多維的向量),第一維是樣本規模,第二維是像素通道,第三維和第四維是長度和寬度。並將數值歸一化和類別標簽向量化。

# load data
(X_train, y_train), (X_test, y_test) = mnist.load_data()
# reshape to be [samples][pixels][width][height]
X_train = X_train.reshape(X_train.shape[0], 1, 28, 28).astype(‘float32‘)
X_test = X_test.reshape(X_test.shape[0], 1, 28, 28).astype(‘float32‘)

X_train = X_train / 255
X_test = X_test / 255
# one hot encode outputs
y_train = np_utils.to_categorical(y_train)
y_test = np_utils.to_categorical(y_test)
num_classes = y_test.shape[1]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

接下來構造CNN。

  1. 第一層是卷積層。該層有32個feature map,或者叫濾波器,作為模型的輸入層,接受[pixels][width][height]大小的輸入數據。feature map的大小是5*5,其輸出接一個‘relu’激活函數。
  2. 下一層是pooling層,使用了MaxPooling,大小為2*2。
  3. 下一層是Dropout層,該層的作用相當於對參數進行正則化來防止模型過擬合。
  4. 接下來是全連接層,有128個神經元,激活函數采用‘relu’。
  5. 最後一層是輸出層,有10個神經元,每個神經元對應一個類別,輸出值表示樣本屬於該類別的概率大小。
def baseline_model():
    # create model
    model = Sequential()
    model.add(Conv2D(32, (5, 5), input_shape=(1, 28, 28), activation=‘relu‘))
    model.add(MaxPooling2D(pool_size=(2, 2)))
    model.add(Dropout(0.2))
    model.add(Flatten())
    model.add(Dense(128, activation=‘relu‘))
    model.add(Dense(num_classes, activation=‘softmax‘))
    # Compile model
    model.compile(loss=‘categorical_crossentropy‘, optimizer=‘adam‘, metrics=[‘accuracy‘])
    return model
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

接著開始訓練模型

# build the model
model = baseline_model()
# Fit the model
model.fit(X_train, y_train, validation_data=(X_test, y_test), epochs=10, batch_size=200, verbose=2)
# Final evaluation of the model
scores = model.evaluate(X_test, y_test, verbose=0)
print("Baseline Error: %.2f%%" % (100-scores[1]*100))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

訓練和測試結果如下:

Train on 60000 samples, validate on 10000 samples
Epoch 1/10
137s - loss: 0.2329 - acc: 0.9340 - val_loss: 0.0820 - val_acc: 0.9742
Epoch 2/10
140s - loss: 0.0736 - acc: 0.9781 - val_loss: 0.0466 - val_acc: 0.9842
Epoch 3/10
138s - loss: 0.0531 - acc: 0.9839 - val_loss: 0.0432 - val_acc: 0.9860
Epoch 4/10
145s - loss: 0.0404 - acc: 0.9876 - val_loss: 0.0389 - val_acc: 0.9872
Epoch 5/10
135s - loss: 0.0335 - acc: 0.9893 - val_loss: 0.0341 - val_acc: 0.9886
Epoch 6/10
133s - loss: 0.0275 - acc: 0.9915 - val_loss: 0.0308 - val_acc: 0.9893
Epoch 7/10
133s - loss: 0.0233 - acc: 0.9926 - val_loss: 0.0363 - val_acc: 0.9880
Epoch 8/10
137s - loss: 0.0204 - acc: 0.9937 - val_loss: 0.0320 - val_acc: 0.9889
Epoch 9/10
139s - loss: 0.0167 - acc: 0.9945 - val_loss: 0.0294 - val_acc: 0.9893
Epoch 10/10
139s - loss: 0.0143 - acc: 0.9957 - val_loss: 0.0310 - val_acc: 0.9907
Baseline Error: 0.93%

可以看出相對於單隱層神經網絡,CNN的效果有很大提升,error rate 從1.87%降到了0.93%。

上面實現了一個只含有一層卷積層和pooling層的CNN,為了實現更好的分類效果,可以添加多層的Convolution2D和MaxPooling2D,CNN會自動提取特征,學習到更好的分類效果。

keras入門實戰:手寫數字識別