1. 程式人生 > >從頭開始實現神經網路:入門

從頭開始實現神經網路:入門

本文中我們會從頭實現一個簡單的3層神經網路。我們不會推導所有的數學公式,但會給我們正在做的事情一個相對直觀的解釋。我也會給出你研讀所需的資源連結。

這裡假設你已經比較熟悉微積分和機器學習的概念了。比如,你知道什麼是分類和正則化。當然你也應該瞭解一點優化技巧,如梯度下降是如何工作的。但是即使你對上面提到的任何一個概念都不熟悉,你仍然會發現本文的有趣所在。

但是為什麼要從頭實現一個神經網路呢?即使你打算將來使用像PyBrain這樣的神經網路庫,從頭實現神經網路仍然是一次非常有價值的練習。它會幫助你理解神經網路的工作原理,而這是設計有效模型的必備技能。

需要注意的是這裡的示例程式碼並不是十分高效,它們本就是用來幫助理解的。在接下來的文章中,我會探索如何使用

Theano寫一個高效的神經網路實現。

產生資料集

讓我們從力所能及的產生資料集開始吧。幸運的是,scikit-learn提供了一些很有用的資料集產生器,所以我們不需要自己寫程式碼了。我們將從make_moons 函式開始。

Python
1234# Generate a dataset and plot itnp.random.seed(0)X,y=sklearn.datasets.make_moons(200,noise=0.20)plt.scatter(X[:,0],X[:,1],s=40,c=y,cmap=plt.cm.Spectral)

產生的資料集中有兩類資料,分別以紅點和藍點表示。你可以把藍點看作是男性病人,紅點看作是女性病人,x和y軸表示藥物治療。

我們的目標是,在給定x和y軸的情況下訓練機器學習分類器以預測正確的分類(男女分類)。注意,資料並不是線性可分的,我們不能直接畫一條直線以區分這兩類資料。這意味著線性分類器,比如Logistic迴歸,將不適用於這個資料集,除非手動構建在給定資料集表現很好的非線性特徵(比如多項式)。

事實上,這也是神經網路的主要優勢。你不用擔心特徵構建,神經網路的隱藏層會為你學習特徵。

Logistic迴歸

為了證明這個觀點,我們來訓練一個Logistic迴歸分類器。它的輸入是x和y軸的值,輸出預測的分類(0或1)。為了簡單,我們使用scikit-learn庫裡的Logistic迴歸類。

Python
1 2 3 4 5 6 7 # Train the logistic rgeression classifier clf=sklearn.linear_model.LogisticRegressionCV() clf.fit(X,y) # Plot the decision boundary plot_decision_boundary(lambdax:clf.predict(x)) plt.title("Logistic Regression")

上圖展示了Logistic迴歸分類器學習到的決策邊界。使用一條直線儘量將資料分離開來,但它並不能捕捉到資料的“月形”特徵。

訓練神經網路

讓我們來建立具有一個輸入層、一個隱藏層、一個輸出層的三層神經網路。輸入層的結點數由資料維度決定,這裡是2維。類似地,輸出層的結點數由類別數決定,也是2。(因為我們只有兩類輸出,實際中我們會避免只使用一個輸出結點預測0和1,而是使用兩個輸出結點以使網路以後能很容易地擴充套件到更多類別)。網路的輸入是x和y座標,輸出是概率,一個是0(女性)的概率,一個是1(男性)的概率。它看起來像下面這樣:

我們可以為隱藏層選擇維度(結點數)。放入隱藏層的結點越多,我們能訓練的函式就越複雜。但是維度過高也是有代價的。首先,預測和學習網路的引數就需要更多的計算。引數越多就意味著我們可能會過度擬合數據。

如何選擇隱藏層的規模?儘管有一些通用的指導和建議,但還是依賴於具體問題具體分析,與其說它是一門科學不如說是一門藝術。我們稍後會在隱藏層的結點數上多做一點事情,然後看看它會對輸出有什麼影響。

我們還需要為隱藏層挑選一個啟用函式。啟用函式將該層的輸入轉換為輸出。一個非線性啟用函式允許我們擬合非線性假設。常用的啟用函式有tanh、the sigmoid函式或者是ReLUs。這裡我們選擇使用在很多場景下都能表現很好的tanh函式。這些函式的一個優點是它們的導數可以使用原函式值計算出來。例如,tanh x的導數是1-tanh^2 x。這個特性是很有用的,它使得我們只需要計算一次tanh x值,之後只需要重複使用這個值就可以得到導數值。

因為我們想要得到神經網路輸出概率,所以輸出層的啟用函式就要是softmax。這是一種將原始分數轉換為概率的方法。如果你很熟悉logistic迴歸,可以把softmax看作是它在多類別上的一般化。

神經網路如何預測

神經網路使用前向傳播進行預測。前向傳播只不過是一堆矩陣相乘並使用我們上面定義的啟用函數了。假如x是該網路的2維輸入,我們將按如下計算預測值(也是二維的):

zi是輸入層、ai是輸出層。W1,b1,W2,b2是需要從訓練資料中學習的網路引數。你可以把它們看作是神經網路各層之間資料轉換矩陣。看著上文的矩陣相乘,我們可以計算出這些矩陣的維度。如果我們的隱藏層中使用500個結點,那麼有

現在你明白了為什麼增大隱藏層的規模會導致需要訓練更多引數。

學習引數

學習該網路的引數意味著要找到使訓練集上錯誤率最小化的引數(W1,b1,W2,b2)。但是如何定義錯誤率呢?我們把衡量錯誤率的函式叫做損失函式(loss function)。輸出層為softmax時多會選擇交叉熵損失(cross-entropy loss)。假如我們有N個訓練例子和C個分類,那麼預測值(hat{y})相對真實標籤值的損失就由下列公式給出:

這個公式看起來很複雜,但實際上它所做的事情不過是把所有訓練例子求和,然後加上預測值錯誤的損失。所以,hat{y}(預測值)距離 hat{y}(真實標籤值)越遠,損失值就越大。

要記住,我們的目標是找到能最小化損失函式的引數值。我們可以使用梯度下降方法找到最小值。我會實現梯度下降的一種最普通的版本,也叫做有固定學習速率的批量梯度下降法。諸如SGD(隨機梯度下降)或minibatch梯度下降通常在實踐中有更好的表現。所以,如果你是認真的,這些可能才是你的選擇,最好還能逐步衰減學習率

作為輸入,梯度下降需要一個與引數相關的損失函式的梯度(導數向量):,。為了計算這些梯度,我們使用了著名的後向傳播演算法。這個演算法是從輸出計算梯度的一種很高效的方法。在這裡我不會深入講解後向傳播如何工作,但是在網路上流傳有很多很優秀的講解(參見這裡或是這裡)。

應用後向傳播公式我們發現以下內容(這點你要相信我):

實現

現在我們要準備開始實現網路了。我們從定義梯度下降一些有用的變數和引數開始:

Python
1234567num_examples=len(X)# training set sizenn_input_dim=2# input layer dimensionality