免費(fèi)Python機(jī)器學(xué)習(xí)課程六:神經(jīng)網(wǎng)絡(luò)算法
神經(jīng)網(wǎng)絡(luò)已被開發(fā)來模仿人類的大腦。盡管我們還不存在,但是神經(jīng)網(wǎng)絡(luò)在機(jī)器學(xué)習(xí)中非常有效。它在1980年代和1990年代很流行。最近,它變得越來越流行??赡苁且?yàn)橛?jì)算機(jī)足夠快,可以在合理的時間內(nèi)運(yùn)行大型神經(jīng)網(wǎng)絡(luò)。在本文中,我將討論如何在python中從頭開發(fā)神經(jīng)網(wǎng)絡(luò)算法。
我建議請仔細(xì)閱讀"神經(jīng)網(wǎng)絡(luò)的想法"部分。但是,如果您不太清楚,請不要擔(dān)心。繼續(xù)執(zhí)行部分。我把它分解成小塊。同樣,您自己運(yùn)行所有代碼將使您更清晰。
神經(jīng)網(wǎng)絡(luò)如何工作
在簡單的神經(jīng)網(wǎng)絡(luò)中,神經(jīng)元是基本的計(jì)算單元。他們采用輸入要素并將其作為輸出進(jìn)行輸出。基本的神經(jīng)網(wǎng)絡(luò)如下所示:

在這里," layer1"是輸入功能。"第1層"進(jìn)入另一個節(jié)點(diǎn)layer2,最后輸出預(yù)測的類別或假設(shè)。Layer2是隱藏層。您可以使用多個隱藏層。
您必須根據(jù)數(shù)據(jù)集和準(zhǔn)確性要求設(shè)計(jì)神經(jīng)網(wǎng)絡(luò)。
正向傳播
從第1層移動到第3層的過程稱為前向傳播。正向傳播的步驟:
(1) 初始化每個輸入要素的系數(shù)theta。假設(shè)有10個輸入功能。說,我們有100個培訓(xùn)示例。這意味著100行數(shù)據(jù)。在這種情況下,我們輸入矩陣的大小為100 x10?,F(xiàn)在,您確定theta1的大小。行數(shù)必須與輸入功能的數(shù)目相同。在此示例中,該值為10。列數(shù)應(yīng)為您選擇的隱藏層的大小。
(2) 將輸入要素X與相應(yīng)的theta相乘,然后添加一個偏差項(xiàng)。通過激活函數(shù)傳遞結(jié)果。
有幾種可用的激活功能,例如S形,tanh,relu,softmax,swish
我將使用S型激活函數(shù)來演示神經(jīng)網(wǎng)絡(luò)。

在這里," a"代表隱藏的圖層或layer2,而b是偏差。
g(z)是S型激活:

(3) 初始化隱藏層的theta2。大小將是隱藏層的長度乘以輸出類的數(shù)量。在此示例中,下一層是輸出層,因?yàn)槲覀儧]有更多的隱藏層。
(4) 然后,我們需要遵循與以前相同的過程。將theta和隱藏層相乘,然后通過S型激活層以獲取假設(shè)或預(yù)測輸出。
反向傳播
反向傳播是從輸出層移動到layer2的過程。在此過程中,我們計(jì)算誤差。
(1) 首先,從原始輸出y中減去假設(shè)。那將是我們的增量。

(2) 現(xiàn)在,計(jì)算theta2的梯度。將delta3乘以theta2。將其乘以" a2"乘以" 1- a2"。在下面的公式中," a"上的上標(biāo)2表示layer2。請不要誤解它為正方形。

(3) 根據(jù)訓(xùn)練樣本數(shù)m從潛水三角洲計(jì)算梯度的非正規(guī)化形式。
訓(xùn)練網(wǎng)絡(luò)
修改theta。將輸入要素乘以學(xué)習(xí)率乘以delta2即可得出theta1。請注意theta的尺寸。

重復(fù)正向傳播和反向傳播的過程,并不斷更新參數(shù),直到達(dá)到最佳成本為止。這是成本函數(shù)的公式。提醒一下,代價函數(shù)表示預(yù)測距原始輸出變量有多遠(yuǎn)。

如果您注意到,則此成本函數(shù)公式幾乎類似于邏輯回歸成本函數(shù)。
神經(jīng)網(wǎng)絡(luò)的實(shí)現(xiàn)
我將使用安德魯·伍(Andrew Ng)在Coursera的機(jī)器學(xué)習(xí)課程中的數(shù)據(jù)集。可以從以下鏈接隨意下載數(shù)據(jù)集:
https://github.com/rashida048/Machine-Learning-With-Python/blob/master/ex3d1.xlsx
這是逐步實(shí)現(xiàn)神經(jīng)網(wǎng)絡(luò)的方法。我鼓勵您自己運(yùn)行每一行代碼并打印輸出以更好地理解它。
(1) 首先導(dǎo)入必要的包和數(shù)據(jù)集。
- import pandas as pd
- import numpy as np
- xls = pd.ExcelFile('ex3d1.xlsx')
- df = pd.read_excel(xls, 'X', header = None)

這是數(shù)據(jù)集的前五行。這些是數(shù)字的像素值。請隨時下載數(shù)據(jù)集并遵循:
在此數(shù)據(jù)集中,輸入和輸出變量組織在單獨(dú)的Excel工作表中。讓我們將輸出變量導(dǎo)入筆記本中:
- y = pd.read_excel(xls, 'y', header=None)

這也是僅數(shù)據(jù)集的前五行。輸出變量是1到10之間的數(shù)字。該項(xiàng)目的目標(biāo)是使用存儲在" df"中的輸入變量來預(yù)測數(shù)字。
(2) 查找輸入和輸出變量的維度
- df.shapey.shape
輸入變量或df的形狀為5000 x 400,輸出變量或y的形狀為5000 x 1。
(3) 定義神經(jīng)網(wǎng)絡(luò)
為簡單起見,我們僅使用25個神經(jīng)元的一個隱藏層。
- hidden_layer = 25
找出輸出類。
- yy_arr = y[0].unique()
- #Output:
- array([10, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype=int64)
如上所示,有10個輸出類。
(4) 初始化θ和偏差
我們將隨機(jī)初始化layer1和layer2的theta。因?yàn)槲覀冇腥龑?,所以會有theta1和theta2。
- theta1的形狀:圖層1的大小x圖層2的大小
- theta2的形狀:第2層的大小x第3層的大小
從第2步開始," df"的形狀為5000 x400。這意味著有400個輸入要素。因此,layer1的大小為400。由于我們將隱藏層的大小指定為25,因此layer2的大小為25。我們有10個輸出類。因此,layer3的大小為10。
- theta1的形狀:400 x 25
- theta2的形狀:25 x 10
同樣,將有兩個隨機(jī)初始化的偏置項(xiàng)b1和b2。
- b1的形狀:layer2的大小(在這種情況下為25)
- b2的形狀:layer3的大小(在這種情況下為10)
定義用于隨機(jī)初始化theta的函數(shù):
- def randInitializeWeights(Lin, Lout):
- epi = (6**1/2) / (Lin + Lout)**0.5
- w = np.random.rand(Lout, Lin)*(2*epi) -epi
- return w
使用此功能并初始化theta
- hidden_layer = 25
- output =10
- theta1 = randInitializeWeights(len(df.T), hidden_layer)
- theta2 = randInitializeWeights(hidden_layer, output)
- theta = [theta1, theta2]
現(xiàn)在,如上所述,初始化偏差項(xiàng):
- b1 = np.random.randn(25,)
- b2 = np.random.randn(10,)
(5) 實(shí)施正向傳播
使用前向傳播部分中的公式。

為了方便起見,定義了一個將theta和X相乘的函數(shù)
- def z_calc(X, theta):
- return np.dot(X, theta.T)
我們還將使用激活功能幾次。還要具有乙狀結(jié)腸激活功能
- def sigmoid(z):
- return 1/(1+ np.exp(-z))
現(xiàn)在,我將逐步演示正向傳播。首先,計(jì)算z項(xiàng):
- z1 =z_calc(df, theta1) + b1
現(xiàn)在通過激活函數(shù)傳遞此z1以獲得我們的隱藏層
- a1 = sigmoid(z1)
a1是隱藏層。a1的形狀為5000 x25。重復(fù)相同的過程以計(jì)算layer3或輸出層
- z2 = z_calc(a1, theta2) + b2
- a2 = sigmoid(z2)
a2的形狀為5000 x10。10列表示10類。a2是我們的layer3或最終輸出或假設(shè)。如果在此示例中存在更多隱藏層,則將重復(fù)執(zhí)行同一過程以從一層轉(zhuǎn)移到另一層。使用輸入要素計(jì)算輸出層的過程稱為前向傳播。將它們放到一個函數(shù)中,因此我們可以對任意數(shù)量的層執(zhí)行正向傳播:
- l = 3 #the umber of layers
- b = [b1, b2]
- def hypothesis(df, theta):
- a = []
- z = []
- for i in range (0, l-1):
- z1 = z_calc(df, theta[i]) + b[i]
- out = sigmoid(z1)
- a.append(out)
- z.append(z1)
- df = out
- return out, a, z
(6) 實(shí)施反向傳播
這是向后計(jì)算梯度并更新theta的過程。在此之前,我們需要修改" y"。" y"有10個班級。但是我們需要將每個類劃分到其列中。例如,第一列用于類10。對于其余類,我們將10替換為1,將其替換為0。這樣,我們將為每個類創(chuàng)建一個單獨(dú)的列。
- y1 = np.zeros([len(df), len(y_arr)])
- y1 = pd.DataFrame(y1)
- for i in range(0, len(y_arr)):
- for j in range(0, len(y1)):
- if y[0][j] == y_arr[i]:
- y1.iloc[j, i] = 1
- else:
- y1.iloc[j, i] = 0
- y1.head()
現(xiàn)在,我首先逐步演示正向傳播,然后將其全部放入一個函數(shù)中,對于反向傳播,我將執(zhí)行相同的操作。使用上面反向傳播部分中的梯度公式,首先計(jì)算delta3。我們將使用前向傳播實(shí)現(xiàn)中的z1,z2,a1和a2。
- del3 = y1-a2
現(xiàn)在,使用以下公式計(jì)算delta2:

這是delta2:
- del2 = np.dot(del3, theta2) * a1*(1 - a1)
在這里,我們需要學(xué)習(xí)一個新概念。那是一個S形梯度。S型梯度的公式為:

如果您注意到,這與增量公式中的a(1 — a)完全相同。因?yàn)閍是sigmoid(z)。因?yàn)檫@是一個約定,所以當(dāng)我將它們?nèi)拷M合在一起以編寫函數(shù)時,我將用此S形梯度代替delta2公式中的a(1-a)項(xiàng)。他們是完全一樣的。我只是想演示兩個。讓我們?yōu)镾型梯度編寫一個函數(shù):
- def sigmoid_grad(z):
- return sigmoid(z)*(1 - sigmoid(z))
最后,是時候使用以下公式更新theta了:

我們需要選擇學(xué)習(xí)率。我選擇了0.003。我鼓勵您嘗試其他學(xué)習(xí)率,以了解其效果:
- theta1 = np.dot(del2.T, pd.DataFrame(a1)) * 0.003
- theta2 = np.dot(del3.T, pd.DataFrame(a2)) * 0.003
這就是theta需要更新的方式。此過程稱為反向傳播,因?yàn)樗蚝笠苿?。在編寫用于反向傳播的函?shù)之前,我們需要定義成本函數(shù)。因?yàn)槲乙矊⒊杀镜挠?jì)算包括在反向傳播方法中。盡管可以在正向傳播中添加它,也可以在訓(xùn)練網(wǎng)絡(luò)時將其分開。這是成本函數(shù)的方法
- def cost_function(y, y_calc, l):
- return (np.sum(np.sum(-np.log(y_calc)*y - np.log(1-y_calc)*(1-y))))/m
這里m是訓(xùn)練示例的數(shù)量。放在一起:
- y1 = np.zeros([len(df), len(y_arr)])
- y1 = pd.DataFrame(y1)
- for i in range(0, len(y_arr)):
- for j in range(0, len(y1)):
- if y[0][j] == y_arr[i]:
- y1.iloc[j, i] = 1
- else:
- y1.iloc[j, i] = 0
- y1.head()
(7) 訓(xùn)練網(wǎng)絡(luò)
我將訓(xùn)練網(wǎng)絡(luò)20個紀(jì)元。我將在此代碼片段中再次初始化theta。因?yàn)槲乙呀?jīng)使用了theta并對其進(jìn)行了更新。因此,如果我不再次對其進(jìn)行初始化,那么我將最終從更新的theta開始。但我想重新開始。
- theta1 = randInitializeWeights(len(df.T), hidden_layer)
- theta2 = randInitializeWeights(hidden_layer, output)
- theta = [theta1, theta2]
- cost_list = []
- for i in range(20):
- theta, cost= backpropagation(df, theta, y1, 0.003)
- cost_list.append(cost)
- cost_list
我使用了0.003的學(xué)習(xí)率,并將其運(yùn)行了20個時期。但是請查看下面提供的GitHub鏈接。我嘗試了不同的學(xué)習(xí)速度和不同的時期,終于到達(dá)了這里。
我們獲得了在每個時期計(jì)算出的成本清單以及最終更新的theta。使用此最終theta預(yù)測輸出。
(8) 預(yù)測輸出并計(jì)算精度
只需使用假設(shè)函數(shù)來傳遞此更新的theta以預(yù)測輸出:
- out, a, z = hypothesis(df, theta)
現(xiàn)在計(jì)算精度,
- accuracy= 0
- for i in range(0, len(out)):
- for j in range(0, len(out[i])):
- if out[i][j] >= 0.5 and y1.iloc[i, j] == 1:
- accuracy += 1
- accuracy/len(df)
精度為100%。完美吧?但是,我們并非始終都能獲得100%的準(zhǔn)確性。有時,獲得70%的準(zhǔn)確性非常好,具體取決于數(shù)據(jù)集。
恭喜!您剛剛開發(fā)了完整的神經(jīng)網(wǎng)絡(luò)!
結(jié)論
對于更簡單的分類問題,邏輯回歸仍然非常有效!但是對于更復(fù)雜的問題,神經(jīng)網(wǎng)絡(luò)可以提供更好的結(jié)果。如您所見,通過向前和向后傳播,它可以更好地學(xué)習(xí)訓(xùn)練數(shù)據(jù)。在自然語言處理和圖像分類中,神經(jīng)網(wǎng)絡(luò)在AI行業(yè)中的表現(xiàn)非常出色。
這是Github的完整工作代碼鏈接:
https://github.com/rashida048/Machine-Learning-With-Python/blob/master/NeuralNetworkFinal.ipynb