神經網路NN演算法(應用篇)

起步

在神經網路的理論篇中,神經元在對權重進行求和後,需要進行一個非線性轉化,即作為參數傳入激活函數去。這個激活函數是一個 S 型函數( Sigmoid )。

雖然理論篇給了一個函數,但事實上,S 函數不是指某個函數,而是一類函數,那篇只是給了這類中的一個,S 函數詳細可以看維基上的: https://zh.wikipedia.org/wiki/S函數 。下面有兩個函數滿足這樣的S 函數:

  • 雙曲函數( tanh );
  • 邏輯函數(logistic function

Sigmoid 函數

S 曲線函數可以將一個數值轉為值域在 0 到 1 之間,廣義上S 函數是滿足y值在某個值域範圍,漸變的一個曲線就可以了。通常情況下使用什麼樣的 Sigmoid 函數呢?有兩種。

雙曲函數( tanh 雙曲函數是一類與常見的三角函數(也叫圓函數)類似的函數。詳情看維基:https://zh.wikipedia.org/wiki/雙曲函數 。

邏輯函數(logistic function 邏輯函數或邏輯曲線是一種常見的S函數,維基:https://zh.wikipedia.org/wiki/邏輯函數。

一個簡單的邏輯函數可用公式表示:

從百科中可以得到它們的求導,雙曲函數的導數:

邏輯函數的導數:

實現神經網路

知道神經網路的工作原理,現在可以著手實現一個神經網路了。

基礎函數

根據上面的sigmoid 函數以及其求導得到對應的python代碼:

# coding: utf-8nimport numpy as npnndef tanh(x):n return np.tanh(x)nndef tanh_deriv(x):n """n tanh的導數n """n return 1.0 - np.tanh(x) * np.tanh(x)nndef logistic(x):n return 1.0 / (1 + np.exp(-x))nndef logistic_deriv(x):n """n 邏輯函數的導數n """n fx = logistic(x)n return fx * (1 - fx)n

設計神經網路的類結構

類的結構大致如下:

class NeuralNetwork(object):n def __init__(self, layers, activation=tanh):n passn def fit(self, X, Y, learning_rate=0.2, epochs=10000):n passn def predict(self, x);n passn

構造函數

在構造函數中,需要確定神經網路的層數,每層的個數,從而確定單元間的權重規格和單元的偏向。

def __init__(self, layers, activation=logistic):n """n :param layers: 層數,如[4, 3, 2] 表示兩層len(list)-1,(因為第一層是輸入層,,有4個單元),n 第一層有3個單元,第二層有2個單元n :param activation:n """n if activation == tanh:n self.activation = tanhn self.activation_deriv = tanh_derivn elif activation == logistic:n self.activation = logisticn self.activation_deriv = logistic_derivnn # 初始化隨機權重n self.weights = []n for i in range(len(layers) - 1):n tmp = (np.random.random([layers[i], layers[i + 1]]) * 2 - 1) * 0.25n self.weights.append(tmp)nn # 偏向隨機初始化n self.bias = []n for i in range(1, len(layers)):n self.bias.append((np.random.random(layers[i]) * 2 - 1) * 0.25)n

其中,layers 參數表示神經網路層數以及各個層單元的數量,例如下圖這個模型:

這個模型就對應了 layers = [4, 3, 2] 。這是一個 2 層,即 len(layers) - 1 層的神經網路。輸入層 4 個單元,其他依次是 3 ,2 。權重是表示層之間單元與單元之間的連接。因此權重也有 2 層。第一層是一個 4 x 3 的矩陣,即 layers[0] x layers[1]。同理,偏向也有 2 層,第一層個數 3 ,即 leyers[1]

利用 np.random.random([m, n]) 來創建一個 m x n 的矩陣。在這個神經網路的類中,初始化都隨機 -0.25 到 0.25 之間的數。

訓練函數

在神經網路的訓練中,需要先設定一個訓練的終止條件,在理論篇介紹了3種停止條件,這邊使用的是 達到預設一定的循環次數 。每次訓練是從樣本中隨機挑選一個實例進行訓練,將這個實例的預測結果和真實結果進行對比,再進行反向傳播得到各層的誤差,然後再更新權重和偏向:

def fit(self, X, y, learning_rate=0.2, epochs=10000):n X = np.atleast_2d(X)n y = np.array(y)n # 隨即梯度n for k in range(epochs):n i = np.random.randint(X.shape[0])n a = [X[i]] # 隨即取某一條實例n for j in range(len(self.weights)):n a.append(self.activation(np.dot(a[j], self.weights[j]) + self.bias[j] ))n errors = y[i] - a[-1]n deltas = [errors * self.activation_deriv(a[-1]) ,] # 輸出層的誤差n # 反向傳播,對於隱藏層的誤差n for j in range(len(a) - 2, 0, -1):n tmp = np.dot(deltas[-1], self.weights[j].T) * self.activation_deriv(a[j])n deltas.append(tmp)n deltas.reverse()nn # 更新權重n for j in range(len(self.weights)):n layer = np.atleast_2d(a[j])n delta = np.atleast_2d(deltas[j])n self.weights[j] += learning_rate * np.dot(layer.T, delta)nn # 更新偏向n for j in range(len(self.bias)):n self.bias[j] += learning_rate * deltas[j]n

參數 learning_rate 表示學習率, epochs 表示設定的循環次數。

預測

預測就是將測試實例從輸入層傳入,通過正向傳播,最後返回輸出層的值即可:

def predict(self, row):n a = np.array(row) # 確保是 ndarray 對象n for i in range(len(self.weights)):n a = self.activation(np.dot(a, self.weights[i]) + self.bias[i])n return an

這個神經網路只用了 50 多行的代碼了。來應用看看實際效果吧。

手寫數字識別

手寫數字數據集來自 sklearn ,其中由1797個圖像組成,其中每個圖像是表示手寫數字的 8x8 像素圖像:

可以推出,這個神經網路的輸入層將有 64 個輸入單元,分類結果是 0~9 ,因此輸出層有10個單元,構造為:

nn = NeuralNetwork(layers=[64, 100, 10])n

載入數據集

from sklearn import datasetsndigits = datasets.load_digits()nX = digits.datany = digits.targetn

拆分成訓練集和數據集,分類結果離散化:

from sklearn.model_selection import train_test_splitnfrom sklearn.preprocessing import LabelBinarizernn# 拆分為訓練集和測試集nX_train, X_test, y_train, y_test = train_test_split(X, y)nn# 分類結果離散化nlabels_train = LabelBinarizer().fit_transform(y_train)nlabels_test = LabelBinarizer().fit_transform(y_test)n

訓練

取訓練集進行訓練:

nn.fit(X_train, labels_train)n

測試模型

# 收集測試結果npredictions = []nfor i in range(X_test.shape[0]):n o = nn.predict(X_test[i] )n predictions.append(np.argmax(o))nn# 列印對比結果nfrom sklearn.metrics import confusion_matrix, classification_reportnprint (confusion_matrix(y_test, predictions) )nprint (classification_report(y_test, predictions))n

利用測試集對模型進行測試,得到 confusion_matrix 的列印結果:

[[51 0 0 0 1 0 0 0 0 0]n [ 0 44 0 1 0 0 0 0 0 1]n [ 0 0 34 0 0 0 0 0 0 0]n [ 0 0 0 41 0 0 0 0 0 0]n [ 0 0 0 0 39 0 0 0 0 0]n [ 0 0 0 0 0 50 0 0 0 0]n [ 0 1 0 0 0 0 42 0 1 0]n [ 0 0 0 0 0 0 0 59 0 0]n [ 0 4 1 3 0 0 0 0 31 0]n [ 1 0 0 0 0 1 0 0 0 44]]n

行列中,表示預測值與真實值的情況,比方預測值為0,真實值也為0,那麼就在 [0][0] 計數 1。因此這個對角線計數越大表示預測越準確。

另一個報告 classification_report :

precision recall f1-score supportnn 0 0.98 0.98 0.98 52n 1 0.90 0.96 0.93 46n 2 0.97 1.00 0.99 34n 3 0.91 1.00 0.95 41n 4 0.97 1.00 0.99 39n 5 0.98 1.00 0.99 50n 6 1.00 0.95 0.98 44n 7 1.00 1.00 1.00 59n 8 0.97 0.79 0.87 39n 9 0.98 0.96 0.97 46nnavg / total 0.97 0.97 0.97 450n

正確率在 97% ,相當不錯的。

附錄

附上本次實驗代碼:github.com/hongweipeng/


推薦閱讀:

(一)深度學習基礎(基本概念、優化演算法、初始化、正則化等)
科普:分散式深度學習系統(二)
樸素貝葉斯分類實例-單詞糾正問題

TAG:神经网络 | 人工智能 | 机器学习 |