計算機視覺是Deep Learning中非常廣泛的應用之一,在這之中有一個非常著名的數據集——MNIST數據集,通常會作為在計算機視覺方面的第一個示例,來實踐在DeepLearning中的圖像識別的算法。在這裡將會通過Keras實現卷積神經網絡來處理這個數據集,進步加深對卷積審計網絡的理解。
13.1 問題描述MNIST數據集是由YannLeCun、Corinna Cortes和ChristopherBurges開發的用於評估手寫數字分類問題的機器學習模型的數據集。該數據集是由美國國家標準與技術研究所(NIST)提供的許多掃描文件數據集構成的,這是數據集的名稱來MNIST或者NIST的來源。
數字圖像是從各種掃描文件中取出的,並且大小統一,這使得它成為評估模型的優秀數據集之一。因為數據集的大小統一,只需要很少的數據清理或準備工作,就可以直接用於模型的訓練,讓開發人員可以專注於模型的構建上。每個圖像是一個28×28像素(總共784像素點)。在這個項目中將數據集預先分割為訓練數據集和評估數據集,其中使用60,000個圖像來訓練模型,並且使用單獨的10,000張圖像來評估得到模型的準確度。
這是一個數字識別任務。因此,預測結果是將圖片中的手寫數字識別為有10個數字(0到9)。使用預測準確度來報告結果,優異的結果能夠達到99%以上的預測準確度。目前,大型卷積神經網絡可以實現約0.2%的預測誤差的高準確度。
13.2 導入數據在這裡將會使用Keras提供的數據集,因此數據導入過程非常簡單,導入的數據也不需要進行進一步的預處理,可以直接用於神經網絡模型的訓練。導入的數據直接被分割成訓練數據集和評估數據集。同時,為了確保每次執行模型生產相同的模型,數據導入之後會設定隨機種子,並查看最初的4張手寫數字的圖片。代碼如下:
from keras.datasets import mnist
from matplotlib import pyplot as plt
import numpy as np
from keras.models import Sequential
from keras.layers import Dense
from keras.utils import np_utils
# 從Keras導入Mnist數據集
(X_train, y_train), (X_validation, y_validation) =mnist.load_data()
# 顯示4張手寫數字的圖片
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'))
plt.show()
# 設定隨機種子
seed = 7
np.random.seed(seed)
在第一次導入數據時會從網上下載數據到本地,Keras默認使用Https協議來下載文件,需要注意的是,如果出現「SSL:CERTIFCATE_VERIFY_FAILED」的問題,可以暫時全局取消證書驗證,來下載數據,具體的方法就不在這裡贅述了。執行代碼後,可以看到最初的4張手寫數字的圖片(圖13-1)。
圖13-1
13.3 多層感知器模型在採用卷積神經網絡來處理手寫數字識別的這個問題之前,先採用多層網絡感知器(MLP)來簡單的實現一下這個問題,作為比較的基準,看一下卷積神經網絡對圖像識別這個問題的改進。也許卷積神經網絡在這個簡單的手寫數字識別問題中的準確度的提升不會很大,但是這會加深對卷積神經網路的理解。
數據導入後,圖像的信息會被保存到,每位有0-255之間的數字構成的28x28的矩陣中,因此多層網絡感知器(MLP)的輸入層的神經元個數是784(28x28),同樣構建一個包含784個神經元的隱藏層。輸入層和隱藏層的激活函數都採用relu。因為在這個數據集中的手寫數字包含0-9這10個數字,也就是說數據集被分成10個類別,因此,輸出層包含10個神經元,激活函數採用softmax。
因為輸入數據是0-255之間的整數,在輸入神經網絡之前對數據進行歸一元處理,並對輸出結果進行one-hot編碼。然後,通過訓練數據集訓練模型,通過驗證數據集來評估模型的準確度。代碼如下:
from keras.datasets import mnist
from matplotlib import pyplot as plt
import numpy as np
from keras.models import Sequential
from keras.layers import Dense
from keras.utils import np_utils
# 從Keras導入Mnist數據集
(X_train, y_train), (X_validation, y_validation) =mnist.load_data()
# 顯示4張手寫數字的圖片
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'))
plt.show()
# 設定隨機種子
seed = 7
np.random.seed(seed)
num_pixels = X_train.shape[1] * X_train.shape[2]
print(num_pixels)
X_train = X_train.reshape(X_train.shape[0],num_pixels).astype('float32')
X_validation = X_validation.reshape(X_validation.shape[0],num_pixels).astype('float32')
# 格式化數據到0-1之前
X_train = X_train / 255
X_validation = X_validation / 255
# one-hot編碼
y_train = np_utils.to_categorical(y_train)
y_validation = np_utils.to_categorical(y_validation)
num_classes = y_validation.shape[1]
print(num_classes)
# 定義基準MLP模型
def create_model():
# 創建模型
model = Sequential()
model.add(Dense(units=num_pixels, input_dim=num_pixels,kernel_initializer='normal', activation='relu'))
model.add(Dense(units=num_classes, kernel_initializer='normal',activation='softmax'))
# 編譯模型
model.compile(loss='categorical_crossentropy', optimizer='adam',metrics=['accuracy'])
return model
model = create_model()
model.fit(X_train, y_train, epochs=10, batch_size=200)
score = model.evaluate(X_validation, y_validation)
print('MLP: %.2f%%' % (score[1] * 100))
執行上述代碼,可以看到多層感知器(MLP)的準確度是:
MLP: 98.17%
13.4 簡單卷積神經網絡
已經完成了如何加載MNIST數據集並在其上訓練一個簡單的多層感知器模型,現在是開發更複雜的卷積神經網絡模型的時候了。 Keras提供了可以很簡單就可以創建卷積神經網絡的API。接下來將使用MNIST數據集創建一個簡單的卷積神經網路,演示如何在Keras中實現卷積神經網絡,包括卷積層,池化層和全聯接層。
數據的導入和準備與多層感知器(MLP)中的類似,就不在重複。在這裡主要介紹一下,這個簡單的卷積神經網路如何設計:
I. 第一個隱藏層是一個稱為Conv2D的卷積層。該層使用5x5的感受野,輸出具有32個特徵圖,輸入的數據期待具有input_shape參數所描述的特徵,並採用relu作為激活函數。
II. 接下來,定義一個採用最大值MaxPooling2D的池化層。並配置它在縱向和橫向兩個方向的採樣因子(pool_size)為2x2,這表示圖片在兩個維度均變成原來的一半。
III. 下一層是使用名為Dropout的Dropout的正則化層,並配置為隨機排除層中的20%的神經元,以減少過度擬合。
IV. 接下來是將多維數據轉換為一維數據的Flatten 層。它的輸出便於標準的全連接的層處理。
V. 接下來是具有128個神經元的全連接的層,採用relu作為激活函數。
VI. 最後,輸出層有10個神經元,在MNIST數據集的輸出具有10個分類,因此採用softmax激活函數,輸出每張圖片在每個分類上的得分。
卷積神經網絡的模型定義完成了,在對模型進行訓練前,依然需要編譯模型,在這裡採用categorical_crossentropy損失函數,和adam優化器來編譯模型。採用10個epochs和batch_size為100來訓練模型。完整代碼如下:
from keras.datasets import mnist
import numpy asnp
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 importbackend
backend.set_image_data_format('channels_first')
# 設定隨機種子
seed = 7
np.random.seed(seed)
# 從Keras導入Mnist數據集
(X_train, y_train), (X_validation, y_validation) = mnist.load_data()
X_train = X_train.reshape(X_train.shape[0], 1, 28, 28).astype('float32')
X_validation = X_validation.reshape(X_validation.shape[0],1, 28, 28).astype('float32')
# 格式化數據到0-1之前
X_train = X_train / 255
X_validation = X_validation / 255
# one-hot編碼
y_train = np_utils.to_categorical(y_train)
y_validation = np_utils.to_categorical(y_validation)
# 創建模型
def 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(units=128, activation='relu'))
model.add(Dense(units=10, activation='softmax'))
# 編譯模型
model.compile(loss='categorical_crossentropy',optimizer='adam',metrics=['accuracy'])
return model
model = create_model()
model.fit(X_train, y_train, epochs=10, batch_size=200, verbose=2)
score = model.evaluate(X_validation, y_validation, verbose=0)
print('CNN_Small:%.2f%%' % (score[1] * 100))
執行代碼得到模型的準確度使99.05%。在訓練時verbose設置為2,進輸入每個epoch的最終結果,忽略在每一個epoch的詳細內容。輸出結果如下:
Epoch 1/10
161s - loss: 0.2310 - acc: 0.9344
Epoch 2/10
151s - loss: 0.0737 - acc: 0.9780
Epoch 3/10
144s - loss: 0.0532 - acc: 0.9838
Epoch 4/10
148s - loss: 0.0402 - acc: 0.9879
Epoch 5/10
147s - loss: 0.0335 - acc: 0.9894
Epoch 6/10
155s - loss: 0.0275 - acc: 0.9916
Epoch 7/10
154s - loss: 0.0232 - acc: 0.9928
Epoch 8/10
141s - loss: 0.0204 - acc: 0.9935
Epoch 9/10
139s - loss: 0.0168 - acc: 0.9945
Epoch 10/10
138s - loss: 0.0142 - acc: 0.9957
CNN_Small: 99.05%
13.5 複雜卷積神經網絡剛剛創建一個簡單的卷積神經網絡,在卷積神經網絡中可以使用多個卷積層,接下來就定義一個採用多個卷積層的卷積神經網絡。網絡拓撲結構如下:
I. 卷積層,具有30個特徵圖,感受野大小為5x5。
II. 採樣因子(pool_size)為2x2 的池化層。
III. 卷積層,具有15個特徵圖,感受野大小為3x3。
IV. 採樣因子(pool_size)為2x2 的池化層。
V. Dropout概率為20%的Dropout層。
VI. Flatten層。
VII. 具有128個神經元和relu激活函數的全全聯接層。
VIII. 具有50個神經元和relu激活函數的全全聯接層。
IX. 輸出層。
採用與簡單卷積神經網絡相同的編譯方式和訓練方法,完整代碼如下:
from keras.datasets import mnist
import numpy asnp
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 importbackend
backend.set_image_data_format('channels_first')
# 設定隨機種子
seed = 7
np.random.seed(seed)
# 從Keras導入Mnist數據集
(X_train, y_train), (X_validation, y_validation) = mnist.load_data()
X_train = X_train.reshape(X_train.shape[0], 1, 28, 28).astype('float32')
X_validation = X_validation.reshape(X_validation.shape[0],1, 28, 28).astype('float32')
# 格式化數據到0-1之前
X_train = X_train / 255
X_validation = X_validation / 255
# one-hot編碼
y_train = np_utils.to_categorical(y_train)
y_validation = np_utils.to_categorical(y_validation)
# 創建模型
def create_model():
model = Sequential()
model.add(Conv2D(30, (5, 5), input_shape=(1, 28, 28), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(15, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.2))
model.add(Flatten())
model.add(Dense(units=128, activation='relu'))
model.add(Dense(units=50, activation='relu'))
model.add(Dense(units=10, activation='softmax'))
# 編譯模型
model.compile(loss='categorical_crossentropy',optimizer='adam',metrics=['accuracy'])
return model
model = create_model()
model.fit(X_train, y_train, epochs=10, batch_size=200, verbose=2)
score = model.evaluate(X_validation, y_validation, verbose=0)
print('CNN_Large:%.2f%%' % (score[1] * 100))
可以看到識別的準確度為99.18%,有一定程度的提升。當準確度在90%以上是即使輕微的提升也是難能可貴。執行結果如下:
Epoch 1/10
176s - loss: 0.3918 - acc: 0.8804
Epoch 2/10
163s - loss: 0.0954 - acc: 0.9708
Epoch 3/10
169s - loss: 0.0694 - acc: 0.9787
Epoch 4/10
177s - loss: 0.0563 - acc: 0.9826
Epoch 5/10
155s - loss: 0.0478 - acc: 0.9851
Epoch 6/10
154s - loss: 0.0433 - acc: 0.9862
Epoch 7/10
156s - loss: 0.0383 - acc: 0.9877
Epoch 8/10
154s - loss: 0.0343 - acc: 0.9893
Epoch 9/10
153s - loss: 0.0320 - acc: 0.9902
Epoch 10/10
153s - loss: 0.0273 - acc: 0.9910
CNN_Large: 99.18%