數據挖掘入門系列教程(八)之使用神經網絡(基于pybrain)識別數字手寫集MNIST

在本章節中,并不會對神經網絡進行介紹,因此如果不了解神經網絡的話,強烈推薦先去看《西瓜書》,或者看一下我的上一篇博客:數據挖掘入門系列教程(七點五)之神經網絡介紹

本來是打算按照《Python數據挖掘入門與實踐》里面的步驟使用神經網絡來識別驗證碼,但是呢,驗證碼要自己生成,然后我又想了一下,不是有大名鼎鼎的MNIST數據集嗎,為什么不使用它呢,他不香嗎?

MNIST(Mixed National Institute of Standards and Technology database)相信大家基本上都了解過他,大部分的機器學習入門項目就是它。它是一個非常龐大的手寫數字數據集(官網)。里面包含了0~9的手寫的數字。部分數據如下:

數據集分為兩個部分,訓練集和測試集。然后在不同的集合中分為兩個文件,數據Images文件和Labels文件。在數據集中一個有60,000個訓練數據和10,000個測試數據。圖片的大小是28*28。

下載數據集

萬物始于數據集,盡管官網提供了數據集供我們下載,但是在sklearn中提供了更方便方法讓我們下載數據集。代碼如下:

import numpy as np
from sklearn.datasets import fetch_openml
# X為image數據,y為標簽
X, y = fetch_openml('mnist_784', version=1, return_X_y=True)

其中X,y中間既包含了訓練集又包含了測試集。也就是說X或者y中有70,000條數據。 那么數據是什么呢?

在X中,每一條數據是一個長為\(28 \times 28=784\)的數組,數組的數據是圖片的像素值。每一條y數據就是一個標簽,代表這張圖片表示哪一個數字(從0到9)。

然后我們將數據進行二值化,像素值大于0的置為1,并將數據保存到文件夾中:

X[X > 0 ] = 1
np.save("./Data/dataset",X)
np.save("./Data/class",y)

然后在Data文件夾中就出現了以下兩個文件:

我們取出dataset中間的一條數據,然后轉成28*28的格式,如下所示:

數據集既可以使用上面的方法得到,也可以從我的Github上面進行下載(其中dataset數據集因為GitHub文件大小的限制所以進行了壓縮,需要解壓才能夠使用)。

加載數據集

前面的步驟我們下載好了數據集,現在我們就可以來加載數據了。

import numpy as np
X = np.load("./Data/dataset.npy")
y = np.load("./Data/class.npy")

取出X中的一條數據如下所示:

取出y中的一條數據,如下所示:

一切都很完美,但是這里有一個問題,在神經網絡中,輸出層實際上是這樣的:

它并不是直接輸出某一個結果,而是輸出\(y_1,…,y_j,…,y_l\)結果(在MNIST中\(l=10\),因為只有10種數字)。以上面的5為例子,輸出層并不是單純的輸出只輸出一個數字,而是要輸出10個值。那么如何將輸出5變成輸出10個數字呢?這里我們使用”one hot Encoding“。

One-Hot編碼,又稱為一位有效編碼,主要是采用\(N\)位狀態寄存器來對\(N\)個狀態進行編碼,每個狀態都由他獨立的寄存器位,并且在任意時候只有一位有效。

以下面的數據為例,每一行代表一條數據,每一列代表一個屬性。其中第2個屬性只需要3個狀態碼,因為他只有0,1,2三種屬性。這樣我們就可以使用100代表0,010代表1,001代表2。

那么這個數據編碼后的數據長什么樣呢?如下圖:

現在我們就可以將前面加載的數據集標簽\(y\)進行“one hot Encoding”。

代碼如下:

from sklearn.preprocessing import OneHotEncoder
# False代表不生成稀疏矩陣
onehot = OneHotEncoder(sparse = False)
# 首先將y轉成行長為7000,列長為1的矩陣,然后再進行轉化。
y = onehot.fit_transform(y.reshape(y.shape[0],1))

接著就是切割數據集了。將數據集切割成訓練集和測試集。

from sklearn.model_selection import train_test_split
x_train,x_test,y_train,y_test = train_test_split(X,y,random_state=14)

在神經網絡中,我們使用pybrain框架去構建一個神經網絡。但是呢,對于pybrain庫,他很與眾不同,他要使用自己的數據集格式,因此,我們需要將數據轉成它規定的格式。

from pybrain.datasets import SupervisedDataSet

train_data = SupervisedDataSet(x_train.shape[1],y.shape[1])
test_data = SupervisedDataSet(x_test.shape[1],y.shape[1])

for i in range(x_train.shape[0]):
    train_data.addSample(x_train[i],y_train[i])
for i in range(x_test.shape[0]):
    test_data.addSample(x_test[i],y_test[i])

終于,數據集的加載就到這里結束了。接下來我們就可以開始構建一個神經網絡了。

構建神經網絡

首先我們來創建一個神經網絡,網絡中只含有輸入層,輸出層和一層隱層。

from pybrain.tools.shortcuts import buildNetwork
# X.shape[1]代表屬性的個數,100代表隱層中神經元的個數,y.shape[1]代表輸出
net = buildNetwork(X.shape[1],100, y.shape[1], bias=True)

這里說有以下“bias”的作用。bias代表的是偏置神經元,bias = True代表偏置神經元激活,也就是在每一層都使用這個這個神經元。偏置神經元如下圖,實際上也就是閾值,只不過換一種說法而已。

現在我們已經構建好了一個比較簡單的神經網絡,接下來我們就是使用BP算法去得到合適的權重值了。

反向傳播(BP)算法

具體的算法步驟在上一篇博客已經介紹過了,很幸運的是在pybrain中間提供了BP算法的庫供我們使用。這里就直接上代碼吧。關于BackpropTrainer更加細節的使用可以看官網

from pybrain.supervised.trainers import BackpropTrainer
trainer = BackpropTrainer(net, train_data, learningrate=0.01,weightdecay=0.01)

這里面有幾個參數稍微的說明下:

  • net:神經網絡

  • train_data:訓練的數據集

  • learningrate:學習率,也就是下面的\(\eta\),同樣它可以使用lrdecay這個參數去控制衰減率,具體的就去看官網文檔吧。

    \[\begin{equation}\begin{array}{l} \Delta w_{h j}=\eta g_{j} b_{h} \\ \Delta \theta_{j}=-\eta g_{j} \\ \Delta v_{i h}=\eta e_{h} x_{i} \\ \Delta \gamma_{h}=-\eta e_{h} \\ \end{array}\end{equation} \]

  • weightdecay:權重衰減,權重衰減也就是下面的\(\lambda\)

    \[\begin{equation} E=\lambda \frac{1}{m} \sum_{k=1}^{m} E_{k}+(1-\lambda) \sum_{i} w_{i}^{2} \\ \lambda \in(0,1) \end{equation} \]

然后我們就可以開始訓練了。

trainer.trainEpochs(epochs=100)

epochs也就是訓練集被訓練遍歷的次數。

接下載便是等待的時間了。等待訓練集訓練成完成。訓練的時間跟訓練集的大小,隱層神經元的個數,電腦的性能,步數等等有關。

切記切記,這一次的程序就不要在阿里云的學生機上面跑了,還是用自己的機器跑吧。盡管聯想小新pro13 i5版本性能還可以,但是還是跑了一個世紀這么久,哎(耽誤了我打游戲的時間)。

進行預測

通過前面的步驟以及等待一段時間后,我們就完成了模型的訓練。然后我們就可以使用測試集進行預測。

predictions = trainer.testOnClassData(dataset=test_data)

predictions的部分數據,代表著測試集預測的結果:

然后我們就可以開始驗證準確度了,這里繼續使用F1評估。這個已經在前面介紹過了,就不再介紹了。

F1驗證

這里有個地方需要注意,因為之前的y_test數據我們使用one-hot encoding進行了編碼,因此我們需要先將one-hot編碼轉成正常的形式。

# 取每一行最大值的索引。
y_test_arry = y_test.argmax(axis =1)

具體效果如下:

然后使用F1值進行驗證。

from sklearn.metrics import f1_score
print("F-score: {0:.2f}".format(f1_score(predictions,y_test_arry,average='micro')))

然后結果如下:

結果只能說還行吧,不是特別的差,但是也不是特別的好。

總結

項目地址:Github。盡管上面的準確度不咋地,只有\(86\%\),但是也還行吧,畢竟也是使用了一層隱層,然后隱層也只有100個神經元。

如果電腦的性能不夠的話,可是適當的減少步數和訓練集的大小,以及隱層神經元的個數。

參考

posted @ 2020-04-05 01:36  段小輝  閱讀(...)  評論(...編輯  收藏