簡介
在上一篇博客:數據挖掘入門系列教程(十點五)之DNN介紹及公式推導中,詳細的介紹了DNN,並對其進行了公式推導。本來這篇博客是准備直接介紹CNN的,但是想了一下,覺得還是使用keras構建一個DNN網絡,然后進行一定的分類操作,這樣能夠更加的直觀一點。
在這篇博客中將介紹:
- keras的基本使用
- 使用keras構建DNN對MNIST數據集進行預測
使用前准備
這次我們將使用keras庫去構建神經網絡,然后默認使用tensorflow作為后端,我是用的python庫版本如下:
- keras:version 2.3.1
- tensorflow:version 2.1.0
這篇博客並不會講keras,tensorflow的安裝,不過值得注意的是,如果自己電腦有英偉達的顯卡就盡量去裝gpu版本的tensorflow,然后安裝對應版本的cuda。一般來說使用GPU能夠大幅度提高程序計算的速度(我的mx250哭暈在廁所)。至於AMD的顯卡,emm,我就不知道支不支持了。
關於keras的具體使用,可以去看一看官方文檔,但是目前官方文檔的目錄欄有一點問題,因此建議大家去看這個keras 中文文檔,這個是根據官方文檔生成的。
准備數據集
這里我們使用keras提供MNIST數據集,訓練集為 60,000 張$ 28\times 28 = 784$像素灰度圖像,測試集為 10,000 同規格圖像,總共 10 類數字標簽。
from keras.datasets import mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()
然后我們可以看一看數據集的shape:
print(x_train.shape, 'x_train samples')
print(x_test.shape, 'x_test samples')
print(y_train.shape, 'y_trian samples')
print(y_test.shape, 'Y_test samples')
此時,我們就已經加載好數據集了。我們可以稍微的看一看數據集長什么樣子:
import matplotlib.pyplot as plt
%matplotlib inline
plt.figure(figsize=(12,10))
x, y = 8, 6
for i in range(x*y):
plt.subplot(y, x, i+1)
plt.imshow(x_train[i],interpolation='nearest')
plt.show()
因為數據集並不能能夠直接進行訓練或者訓練效果不好,在這里我們將數據進行一下變換。比如說歸一化,onehot編碼(這個必須做)。
數據集的變換
首先,我們的DNN的設計如下:
輸入層是由784
個神經元構成的,但是在前面我們已經知道x_train.shape
是(6000,28,28)
。輸出層由10
個神經元構成,而y_train.shape
是(6000,)
,毋庸置疑,我們需要對x,y
數據進行編碼。
對於x
數據來說,我們需要將它變成(6000,784)
的類型,對於y
我們進行onehot編碼就行了(one hot編碼在前面已經介紹過了,這里就不多做介紹了),同時在這里我們還將數據進行歸一化(可以提高模型的准確率)。
import keras
x_train = x_train.reshape(60000, 784)
x_test = x_test.reshape(10000, 784)
# 將數據變成float類型,這樣能夠被255除
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= 255
x_test /= 255
# one hot 編碼,將類向量(整數)轉換為二進制類矩陣。
y_train = keras.utils.to_categorical(y_train, 10)
y_test = keras.utils.to_categorical(y_test, 10)
構建DNN網絡
首先我們導入Sequential
。
from keras.models import Sequential
Sequential
是一個順序模型:多個網絡層的線性堆疊,每一層接受上一層的輸入,向下一層進行輸出,示意圖如下:
這里,再說一個概念,全連接層
,很簡單理解,就是某一層用數學公式來表達就是\(output = W\times input\),比如說下圖中的layer2和layer3
就是全連接的,也就是說layer2
是全連接層。在keras中,可以使用Dense去創建一個全連接層。
關於Dense的參數,截取官方文檔如下:
參數
- units: 正整數,輸出空間維度。
- activation: 激活函數 (詳見 activations)。 若不指定,則不使用激活函數 (即,「線性」激活:
a(x) = x
)。- use_bias: 布爾值,該層是否使用偏置向量。
- kernel_initializer:
kernel
權值矩陣的初始化器 (詳見 initializers)。- bias_initializer: 偏置向量的初始化器 (see initializers).
- kernel_regularizer: 運用到
kernel
權值矩陣的正則化函數 (詳見 regularizer)。- bias_regularizer: 運用到偏置向的的正則化函數 (詳見 regularizer)。
- activity_regularizer: 運用到層的輸出的正則化函數 (它的 "activation")。 (詳見 regularizer)。
- kernel_constraint: 運用到
kernel
權值矩陣的約束函數 (詳見 constraints)。- bias_constraint: 運用到偏置向量的約束函數 (詳見 constraints)。
然我們來看一看具體的使用(里面涉及的幾個激活函數隨便去網上搜一下就知道了):
from keras.layers import Dense, Dropout
# 創建一個網絡模型
model = Sequential()
# 創建輸入層 512代表的是輸出維度為512,也就是第二層神經元有512個,輸入維度為(784,),激活函數為Relu
model.add(Dense(512, activation='relu', input_shape=(784,)))
model.add(Dropout(0.2))
# 創建layer2,然后向下層輸出的空間維度為512
model.add(Dense(512, activation='relu'))
model.add(Dropout(0.2))
# 輸出層,因為只有10個數字,所以輸出空間維度為10,激活函數為softmax。
model.add(Dense(10, activation='softmax'))
# 網絡模型的介紹
print(model.summary())
Dropout
應用於輸入。Dropout 包括在訓練中每次更新時, 將輸入單元的按比率隨機設置為 0, 這有助於防止過擬合。
簡單點來說,他就是這樣的,隨機(按比率)將某一個輸入值變成0,這樣他就沒辦法向下一層傳遞信息。
打印的網絡結構模型如下所示,還是有蠻多參數的:
接着我們就來講一下如何配置這個模型,在keras中,配置一個模型至少需要兩個參數loss
和optimizer
。其中loss
就是損失函數,optimizer
就是優化器,也就是前篇博客提到的優化方法,比如說梯度下降法,牛頓法等等,metrics
代表在訓練和測試期間的模型評估標准。
from keras.optimizers import RMSprop
model.compile(loss='categorical_crossentropy',
optimizer=RMSprop(),
metrics=['accuracy'])
這里我們選擇RMSprop
,具體內容可以參考Day 69: rmsprop,然后loss我們選擇categorical_crossentropy
,因為它是多分類任務。其表達式為:
\(C E(x)=-\sum_{i=1}^{C} y_{i} \log f_{i}(x)\)
其中, \(x\)表示輸入樣本, \(C\)為待分類的類別總數。 \(y_{i}\) 為第\(i\)個類別對應的真實標簽, \(f_{i}(x)\) 為對應的模 型輸出值。
開始訓練
最后我們可以使用這個模型來訓練數據了,代碼:
# history保存了每一次訓練的loss,accuracy
history = model.fit(x_train, y_train,
batch_size=128,
epochs=32,
verbose=1,
validation_data=(x_test, y_test))
x_train, y_train
這個就沒必要解釋了,很簡單,就是訓練的數據集。
batch_size
代表每一次梯度更新的樣本數,默認32。如何理解這個這一個參數呢?意思就是說每次一訓練的時候訓練batch_size
個樣本,針對每一個樣本我們都可以求出它對應的loss,然后我們求和然后取平均\(C E(x)_{\text {final}}=\frac{\sum_{b=1}^{batch\_size} C E\left(x^{(b)}\right)}{N}\),然后再使用BP算法。epochs
: 訓練模型迭代輪次。verbose
: 0, 1 或 2。日志顯示模式。 0 = 安靜模式, 1 = 進度條, 2 = 每輪一行。validation_data
:用來評估損失,以及在每輪結束時的任何模型度量指標。 模型將不會在這個數據上進行訓練。
部分打印數據如下:
在我的筆記本i5十代u,mx250的情況下,訓練了大概1分多鍾,畢竟數據集還是比較小的,然后參數也不是很多。
進行評估
# 這里的verbose和fit中的含義一樣
score = model.evaluate(x_test, y_test, verbose=0)
print('Test loss:', score[0])
print('Test accuracy:', score[1])
最后結果如下圖:
訓練過程的accuracy和loss
這里我們將每一次輪訓練以及測試對應的loss和accuracy畫出來:
# 繪制訓練過程中訓練集和測試集合的准確率值
plt.plot(history.history['accuracy'])
plt.plot(history.history['val_accuracy'])
plt.title('Model accuracy')
plt.ylabel('Accuracy')
plt.xlabel('Epoch')
plt.legend(['Train', 'Test'], loc='upper left')
plt.show()
# 繪制訓練過程中訓練集和測試集合的損失值
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.title('Model loss')
plt.ylabel('Loss')
plt.xlabel('Epoch')
plt.legend(['Train', 'Test'], loc='upper left')
plt.show()
總結
ok,這篇博客就結束了。總的來說還是蠻順利的,因為不需要去推數學公式,基本上沒有什么難度(當然,如果想更好的提高准確率還是蠻難的,可以去kaggle里面看一下大佬的解決方案)。這篇博客簡單的介紹了keras使用,我力求將每一個用到的函數都講清楚,但是實際上很難,因為可能我認為講清楚了,但是實際上你可能還是沒有明白。這個時候可以去多看一下官方文檔,或者別人的博客,當然,私信我也是🆗的。
項目地址:Github