用Python從頭開始構建神經網絡

做者|Rashida Nasrin Sucky
編譯|VK
來源|Mediumpython

神經網絡已經被開發用來模擬人腦。雖然咱們尚未作到這一點,但神經網絡在機器學習方面是很是有效的。它在上世紀80年代和90年代很流行,最近愈來愈流行。計算機的速度足以在合理的時間內運行一個大型神經網絡。在本文中,我將討論如何實現一個神經網絡。git

我建議你仔細閱讀「神經網絡的思想」部分。但若是你不太清楚,不要擔憂。能夠轉到實現部分。我把它分解成更小的碎片幫助理解。github

神經網絡的工做原理

在一個簡單的神經網絡中,神經元是基本的計算單元。它們獲取輸入特徵並將其做爲輸出。如下是基本神經網絡的外觀:網絡

這裏,「layer1」是輸入特徵。「Layer1」進入另外一個節點layer2,最後輸出預測的類或假設。layer2是隱藏層。可使用多個隱藏層。app

你必須根據你的數據集和精度要求來設計你的神經網絡。

前向傳播

從第1層移動到第3層的過程稱爲前向傳播。前向傳播的步驟:dom

  1. 爲每一個輸入特徵初始化係數θ。比方說,咱們有100個訓練例子。這意味着100行數據。在這種狀況下,若是假設有10個輸入特徵,咱們的輸入矩陣的大小是100x10。如今肯定$θ_1$的大小。行數須要與輸入特徵的數量相同。在這個例子中,是10。列數應該是你選擇的隱藏層的大小。
  2. 將輸入特徵X乘以相應的θ,而後添加一個偏置項。經過激活函數傳遞結果。
有幾個激活函數可用,如sigmoid,tanh,relu,softmax,swish

我將使用一個sigmoid激活函數來演示神經網絡。機器學習

這裏,「a」表明隱藏層或第2層,b表示偏置。函數

g(z)是sigmoid激活函數:學習

  1. 爲隱藏層初始化$\theta_2$。大小將是隱藏層的長度乘以輸出類的數量。在這個例子中,下一層是輸出層,由於咱們沒有更多的隱藏層。
  2. 而後咱們須要按照之前同樣的流程。將θ和隱藏層相乘,經過sigmoid激活層獲得預測輸出。

反向傳播

反向傳播是從輸出層移動到第二層的過程。在這個過程當中,咱們計算了偏差。ui

  1. 首先,從原始輸出y減去預測輸出,這就是咱們的$\delta_3$。

  1. 如今,計算$\theta_2$的梯度。將$\delta_3$乘以$\theta_2$。乘以「$a^2$」乘以「$1-a^2$」。在下面的公式中,「a」上的上標2表示第2層。請不要把它誤解爲平方。

  1. 用訓練樣本數m計算沒有正則化版本的梯度$\delta$。

訓練網絡

修正$\delta$。將輸入特徵乘以$\delta_2$乘以學習速率獲得$\theta_1$。請注意$\theta_1$的維度。

重複前向傳播和反向傳播的過程,並不斷更新參數,直到達到最佳成本。這是成本函數的公式。只是提醒一下,成本函數代表,預測離原始輸出變量有多遠。

若是你注意到的話,這個成本函數公式幾乎和邏輯迴歸成本函數同樣。

神經網絡的實現

我將使用Andrew Ng在Coursera的機器學習課程的數據集。請從如下連接下載數據集:

https://github.com/rashida048...

下面是一個逐步實現的神經網絡。我鼓勵你本身運行每一行代碼並打印輸出以更好地理解它。

  1. 首先導入必要的包和數據集。
import pandas as pd
import numpy as np
xls = pd.ExcelFile('ex3d1.xlsx')
df = pd.read_excel(xls, 'X', header = None)

這是數據集的前五行。這些是數字的像素值。

在這個數據集中,輸入和輸出變量被組織在單獨的excel表格中。讓咱們導入輸出變量:

y = pd.read_excel(xls, 'y', header=None)

這也是數據集的前五行。輸出變量是從1到10的數字。這個項目的目標是使用存儲在'df'中的輸入變量來預測數字。

  1. 求輸入輸出變量的維數
df.shape
y.shape

輸入變量或df的形狀爲5000 x 400,輸出變量或y的形狀爲5000 x 1。

  1. 定義神經網絡

爲了簡單起見,咱們將只使用一個由25個神經元組成的隱藏層。

hidden_layer = 25

獲得輸出類。

y_arr = y[0].unique()#輸出:
array([10,  1,  2,  3,  4,  5,  6,  7,  8,  9], dtype=int64)

正如你在上面看到的,有10個輸出類。

  1. 初始化θ和偏置

咱們將隨機初始化層1和層2的θ。由於咱們有三層,因此會有$\theta_1$和$\theta_2$。

$\theta_1$的維度:第1層的大小x第2層的大小

$\theta_2$的維度:第2層的大小x第3層的大小

從步驟2開始,「df」的形狀爲5000 x 400。這意味着有400個輸入特徵。因此,第1層的大小是400。當咱們指定隱藏層大小爲25時,層2的大小爲25。咱們有10個輸出類。因此,第3層的大小是10。

$\theta_1$的維度:400 x 25

$\theta_2$的維度:25×10

一樣,會有兩個隨機初始化的偏置b1和b2。

$b_1$的維度:第2層的大小(本例中爲25)

$b_1$的維度:第3層的大小(本例中爲10)

定義一個隨機初始化theta的函數:

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]

如今,初始化咱們上面討論過的偏置項:

b1 = np.random.randn(25,)
b2 = np.random.randn(10,)
  1. 實現前向傳播

使用前向傳播部分中的公式。

爲了方便起見,定義一個函數來乘以θ和X

def z_calc(X, theta):
    return np.dot(X, theta.T)

咱們也將屢次使用激活函數。一樣定義一個函數

def sigmoid(z):
    return 1/(1+ np.exp(-z))

如今我將逐步演示正向傳播。首先,計算z項:

z1 =z_calc(df, theta1) + b1

如今經過激活函數傳遞這個z1,獲得隱藏層

a1 = sigmoid(z1)

a1是隱藏層。a1的形狀是5000 x 25。重複相同的過程來計算第3層或輸出層

z2 = z_calc(a1, theta2) + b2
a2 = sigmoid(z2)

a2的形狀是5000 x 10。10列表明10個類。a2是咱們的第3層或最終輸出。若是在這個例子中有更多的隱藏層,在從一個層到另外一個層的過程當中會有更多的重複步驟。這種利用輸入特徵計算輸出層的過程稱爲前向傳播。

l = 3  #層數
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
  1. 實現反向傳播

這是反向計算梯度和更新θ的過程。在此以前,咱們須要修改'y'。咱們在「y」有10個類。但咱們須要將每一個類在其列中分開。例如,針對第10類的列。咱們將爲10替換1,爲其他類替換0。這樣咱們將爲每一個類建立一個單獨的列。

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()

以前我一步一步地演示了向前傳播,而後把全部的都放在一個函數中,我將對反向傳播作一樣的事情。使用上述反向傳播部分的梯度公式,首先計算$\delta_3$。咱們將使用前向傳播實現中的z一、z二、a1和a2。

del3 = y1-a2

如今使用如下公式計算delta2:

這裏是delta2:

del2 = np.dot(del3, theta2) * a1*(1 - a1)

在這裏咱們須要學習一個新的概念。這是一個sigmoid梯度。sigmoid梯度的公式爲:

若是你注意到了,這和delta公式中的a(1-a)徹底相同。由於a是sigmoid(z)。咱們來寫一個關於sigmoid梯度的函數:

def sigmoid_grad(z):
    return sigmoid(z)*(1 - sigmoid(z))

最後,使用如下公式更新θ:

咱們須要選擇一個學習率。我選了0.003。我鼓勵你嘗試使用其餘學習率,看看它的表現:

theta1 = np.dot(del2.T, pd.DataFrame(a1)) * 0.003
theta2 = np.dot(del3.T, pd.DataFrame(a2)) * 0.003

這就是θ須要更新的方式。這個過程稱爲反向傳播,由於它向後移動。在編寫反向傳播函數以前,咱們須要定義成本函數。由於我會把成本的計算也包括在反向傳播方法中。但它是能夠添加到前向傳播中,或者能夠在訓練網絡時將其分開的。

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是訓練實例的數量。綜合起來的代碼:

m = len(df)
def backpropagation(df, theta, y1, alpha):
    out, a, z = hypothesis(df, theta)
    delta = []
    delta.append(y1-a[-1])
    i = l - 2
    while i > 0:
        delta.append(np.dot(delta[-i], theta[-i])*sigmoid_grad(z[-(i+1)]))
        i -= 1
    theta[0] = np.dot(delta[-1].T, df) * alpha
    for i in range(1, len(theta)):
        theta[i] = np.dot(delta[-(i+1)].T, pd.DataFrame(a[0])) * alpha
    out, a, z = hypothesis(df, theta)
    cost = cost_function(y1, a[-1], 1)
    return theta, cost
  1. 訓練網絡

我將用20個epoch訓練網絡。我在這個代碼片斷中再次初始化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的學習率並運行了20個epoch。可是請看文章末提供的GitHub連接。我有試着用不一樣的學習率和不一樣的epoch數訓練模型。

咱們獲得了每一個epoch計算的成本,以及最終更新的θ。用最後的θ來預測輸出。

  1. 預測輸出並計算精度

只需使用假設函數並傳遞更新後的θ來預測輸出:

out, a, z = hypothesis(df, theta)

如今計算一下準確率,

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%的準確率。有時得到70%的準確率是很好的,這取決於數據集。

恭喜!你剛剛開發了一個完整的神經網絡!

如下是完整工做代碼的GitHub連接:

https://github.com/rashida048...

原文連接:https://medium.com/towards-ar...

歡迎關注磐創AI博客站:
http://panchuang.net/

sklearn機器學習中文官方文檔:
http://sklearn123.com/

歡迎關注磐創博客資源彙總站:
http://docs.panchuang.net/

相關文章
相關標籤/搜索