神經網絡實踐介紹:從頭開始在Python中實現單個神經元

神經網絡實踐介紹:從頭開始在Python中實現單個神經元

在過去的十年中,人工智能(AI)已經深入公眾關注的焦點,很大程度上歸功於機器學習(ML)和人工神經網絡(ANN)的進步。

但是,現在這個領域的噪音已經非常大。這就是為什麼我認為回到基礎並使用Python 實際實現一個單獨的神經元是有用的。

人工神經元

在我們進入之前,我只想快速談論一個神經元是什麼。早期的人工智能支持者注意到生物神經元能夠從大量數據中進行概念化和學習,並假設在機器中對這個神經元進行建模可能會產生類似的能力。為此,神經元被抽象為模型的輸入,輸出和權重。

神經網絡實踐介紹:從頭開始在Python中實現單個神經元

圖1:簡單的神經元模型

在機器學習術語中,神經元(x1,x2,... xn)的每個輸入被稱為特徵,每個特徵用一個數字加權以表示該輸入(w1j,w2j,... wnj)的強度。輸入的加權和(netj)然後通過激活函數, 其通用目的是通過根據公式將加權和轉換成新數字來模擬生物神經元的“firing rate”。

如果這就是神經元的工作原理,我們來看看它是如何學習的。簡單地說,訓練神經元是指迭代地更新與每個輸入相關的權重,以便在給定的數據集中逐步逼近底層關係。一旦經過適當的訓練,一個神經元就可以被用來做正確的事情,比如,把貓和狗的圖像完全歸類到不同的類,就像人們一樣。在機器學習術語中,這被稱為分類。

訓練

為了訓練一個簡單的分類器,我們使用公開可用的sklearn breast cancer數據集(http://scikit-learn.org/stable/modules/generated/sklearn.datasets.load_breast_cancer.html),它具有以下屬性:

神經網絡實踐介紹:從頭開始在Python中實現單個神經元

數據集中的每個樣本都是乳房腫物的圖像,已被翻譯成30個數字(特徵)。使用一部分樣本來訓練我們的神經元,我們將會看看它是否能將乳房腫塊的不可見部分分類為惡性或良性。換句話說,我們需要執行監督式學習 任務,使用明確標記的數據點作為神經元的教師來學習相關模式。

要運行和修改下面的代碼,請查看腳本:single-layer-perceptron.py(https://github.com/dguliani/neural-network-tutorials/blob/master/single-layer-perceptron.py)

首先,我們加載數據集,隨機地將惡性和良性的樣本混合在一起,同時保持每個樣本的標籤。這是因為我們不希望我們的神經元根據它所看到的樣本的順序來得出結論——只根據每個樣本的特徵。

# Using scikit-learn's breast cancer dataset: classifying cell features as malignant or benign

bc = datasets.load_breast_cancer()

x = bc['data'] # array of features with shape: (n_samples, n_features)

y = bc['target'] # array of binary values for the two classes: (n_samples)

shuffle_ind = np.random.permutation(len(x)) # shuffle data for training

x = np.array(x[shuffle_ind])

# convert y to a column to make it more consistent with x

y = np.array(y[shuffle_ind])[np.newaxis].T

x /= np.max(x) # linearly scaling the inputs to a range between 0 and 1

train_fraction = 0.75

train_idx = int(len(x)*train_fraction)

x_train = x[:train_idx]

y_train = y[:train_idx]

x_test = x[train_idx:]

y_test = y[train_idx:]

為了訓練我們的神經元,我們基本上需要做三件事:

  • 1.請神經元對樣本進行分類。
  • 2.根據預測的錯誤更新神經元的權重。
  • 3.重複。

由於神經元本質上只是權重的集合,我們可以使用Python的矩陣操作包numpy來隨機初始化權重向量。權重的數量對應於初始化功能(輸入)神經元的數量,每個特性是加權求和。

# initialize single layer weights randomly with mean 0: single neuron in layer

W = 2*np.random.random((np.array(x).shape[1], 1)) - 1

Forward Pass

在訓練的第一步,我們要求神經元對訓練樣本進行預測。這就是所謂的正向傳遞(Forward Pass),它包含了輸入特性的加權和,並通過激活函數傳遞這個和。

# forward pass

l0 = x_train # layer 0 output

l1 = sigmoid(np.matmul(l0, W)) # perceptron output

上面代碼片段中的l0是具有形狀的特徵矩陣(n_samples * n_features)。代表我們單個神經元的權重是形狀的(n_features * 1)。因此,這兩個矩陣之間的矩陣乘法將為每個樣本提供所有特徵的加權和。(如果你親自嘗試這個,你會發現它並不像聽起來那麼複雜。)

當通過激活函數時,這些加權和會有效地成為每個訓練樣本的類別預測。

Sigmoid函數

sigmoid函數是logistic函數的一個特殊情況,在這裡選擇它作為激活函數有以下幾個原因:它是容易微分的,非線性的,有界的,具有以下形狀和定義:

神經網絡實踐介紹:從頭開始在Python中實現單個神經元

Figure 2: Sigmoid function shape.

神經網絡實踐介紹:從頭開始在Python中實現單個神經元

圖3:Sigmoid函數定義

該函數由一行實現,如下所示:

def sigmoid(x):

return 1/(1+np.exp(-x))

一個標準的logistic函數有一個輕鬆的導數計算形式

神經網絡實踐介紹:從頭開始在Python中實現單個神經元

圖4:Sigmoid函數的導數

其中f(x)表示logistic函數。正如我們將很快看到的,當試圖最小化我們神經元預測中的錯誤時,這個屬性非常有用。

sigmoid函數的導數實現如下:

def dsigmoid(y):

return y*(1-y)

梯度下降

現在來看看有趣的(也是棘手的)部分 - 實際上讓我們的神經元學習數據集中的基礎關係。現在我們已經對每個訓練樣本進行了有界預測,我們可以計算這些預測中的誤差 / 損失,並更新與此誤差成正比的神經元權重。

我們將為此權重更新使用梯度下降優化算法。為了使用這種算法,我們需要一個誤差函數來表示我們的神經元預測和地面實況之間的差距。這個錯誤函數被定義為均方誤差的縮放版本(縮放使得區分變得容易)。

神經網絡實踐介紹:從頭開始在Python中實現單個神經元

圖5:均方誤差函數

在代碼中,這個均方誤差函數的實現如下:

def mse(y, pred):

return np.mean(np.square(y - pred))/2

在數學中,梯度是一個函數的導數向量,它依賴於多個變量。回想一下,向量既有大小又有方向。我們的神經元的誤差依賴於輸入的所有權重。梯度是誤差的偏導對每個權值的偏導。

作為一個矢量,梯度點在函數增長速度最大的方向上。因此,向與梯度方向相反的方向移動,可以使函數最小化。如果我們能夠計算出神經元的誤差函數相對於每一個權重的梯度,我們就可以按比例更新權重以最小化誤差。把誤差函數想象成一個有山脊和山谷的曲面。通過與梯度相反的下降,我們進入了誤差較低的山谷。

下面是使用鏈式法則的誤差函數梯度的簡單推導。

神經網絡實踐介紹:從頭開始在Python中實現單個神經元

圖6:對每個神經元權重的誤差的偏導數

這裡,E是誤差函數,wij是一個特定權重,oj是神經元的輸出,netj是神經元輸入的加權和。指數i和j分別對應於權重和神經元。我們將分別計算偏導數的每個因子。

第一個因子很簡單,是誤差對神經元輸出的導數:

神經網絡實踐介紹:從頭開始在Python中實現單個神經元

圖7:與神經元輸出有關的輸出誤差的導數

第二因子也是簡單,並且是圖4中所述的sigmoid函數的導數

神經網絡實踐介紹:從頭開始在Python中實現單個神經元

圖8:相對於加權和的神經元輸出的導數

第三個也是最後一個因子簡化了等於特定神經元的輸入

神經網絡實踐介紹:從頭開始在Python中實現單個神經元

圖9:相對於每個權重的輸入的加權總和的偏導數

在圖9中,oi是該神經元輸入的向量,在我們的例子中是來自我們訓練集的特徵。

權重更新規則

將我們剛剛看到的偏導數與下降結合起來,給我們更新表示我們神經元的權重的規則:

神經網絡實踐介紹:從頭開始在Python中實現單個神經元

圖10:權重更新規則

圖10顯示每個權重將在梯度的負方向上更新,與附加項n成比例。比例因子n決定了更新神經元權重時我們採取的步驟有多大,從而有效地控制了神經元學習的速度。我們稱之為學習率。

實現權重更新

下面是我們單個神經元的梯度計算和權重更新的實現。您可以根據註釋查找權重更新規則所需的每一步導數。

lr = 0.5 # learning rate

epochs = 600

for iter in range(epochs):

# forward pass

l0 = x_train # layer 0 output

l1 = sigmoid(np.matmul(l0, W)) # layer 1 output

# backward pass

l1_error = l1 - y_train # output layer error, (dE/do)

l1_gradient = dsigmoid(l1) # (do/dnetx)

l1_delta = np.multiply(l1_error, l1_gradient) # (dE/do * do/dnetx = dE/dnetx)

l1_weight_delta = np.matmul(l0.T, l1_delta) # (dnetx/dwij * dE/dnetx = dE/dwij)

# update weights with a scaling factor of learning rate

W -= l1_weight_delta * lr

在訓練神經網絡的同時,相同的訓練數據通過網絡多次運行,每次全程都被稱為epoch。在每個epoch,權重會進一步更新以嘗試降低誤差。對於我們這個簡單的例子,通過反覆試驗來選擇epoch的數量和學習率,觀察它們的損失減少和收斂。

結果

神經網絡實踐介紹:從頭開始在Python中實現單個神經元

Figure 11: Training results.

圖11顯示,在數百個epochs,訓練數據集和測試數據集的損失減少,準確性增加。接下來,我們通過在相同數據集上訓練10個不同的隨機初始化神經元來檢查這個訓練過程是否可重複。在10次訓練結束時,平均測試準確度為90.49%(s = 2.40%),平均總準確率為90.33%(s = 0.304%)。

如果我們發現訓練和測試精度差異很大,或者如果測試損失增加而訓練損失減少,我們就有理由相信神經元沒有學習隱藏在數據集中的模式。雖然這種驗證水平還不足以將此神經元放入生產環境,但跡象表明神經元已經在數據集中學習了一種模式。

結論

我們在這裡查看了人造神經網絡的最簡單形式,即具有由梯度下降驅動的單個神經元的網絡。網絡可以由許多神經元或其他可訓練的濾波器/單元組成,並根據其目的使用各種loss和激活功能。所有這些擴展允許ANN執行廣泛的任務,如對象檢測,語言翻譯,時間序列預測等。


分享到:


相關文章: