在本章節中,並不會對神經網絡進行介紹,因此如果不了解神經網絡的話,強烈推薦先去看《西瓜書》,或者看一下我的上一篇博客:數據挖掘入門系列教程(七點五)之神經網絡介紹
本來是打算按照《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個神經元。
如果電腦的性能不夠的話,可是適當的減少步數和訓練集的大小,以及隱層神經元的個數。