BP神經網絡與Python實現

人工神經網絡是一種經典的機器學習模型,隨着深度學習的發展神經網絡模型日益完善.html

聯想你們熟悉的迴歸問題, 神經網絡模型其實是根據訓練樣本創造出一個多維輸入多維輸出的函數, 並使用該函數進行預測, 網絡的訓練過程即爲調節該函數參數提升預測精度的過程.神經網絡要解決的問題與最小二乘法迴歸解決的問題並沒有根本性區別.python

迴歸和分類是經常使用神經網絡處理的兩類問題, 若是你已經瞭解了神經網絡的工做原理能夠在http://playground.tensorflow.org/上體驗一個淺層神經網絡的工做過程.git

感知機(Perceptron)是一個簡單的線性二分類器, 它保存着輸入權重, 根據輸入和內置的函數計算輸出.人工神經網絡中的單個神經元便是感知機.算法

在前饋神經網絡的預測過程當中, 數據流從輸入到輸出單向流動, 不存在循環和返回的通道.網絡

目前大多數神經網絡模型都屬於前饋神經網絡, 在下文中咱們將詳細討論前饋過程.app

多層感知機(Multi Layer Perceptron, MLP)是由多個感知機層全鏈接組成的前饋神經網絡, 這種模型在非線性問題中表現出色.dom

所謂全鏈接是指層A上任一神經元與臨近層B上的任意神經元之間都存在鏈接.機器學習

反向傳播(Back Propagation,BP)是偏差反向傳播的簡稱,這是一種用來訓練人工神經網絡的常見算法, 一般與最優化方法(如梯度降低法)結合使用.函數

本文介紹的神經網絡模型在結構上屬於MLP, 由於採用BP算法進行訓練, 人們也稱其爲BP神經網絡.工具

BP神經網絡原理

經典的BP神經網絡一般由三層組成: 輸入層, 隱含層與輸出層.一般輸入層神經元的個數與特徵數相關,輸出層的個數與類別數相同, 隱含層的層數與神經元數都可以自定義.

每一個神經元表明對數據的一次處理:

每一個隱含層和輸出層神經元輸出與輸入的函數關係爲:

\[I_j=\sum_iW_{ij}O_i\]

\[O_j= sigmod(I_l) =\frac{1}{1+e^{-I_l}}\]

其中\(W_{ij}\)表示神經元i與神經元j之間鏈接的權重,\(O_j\)表明神經元j的輸出, sigmod是一個特殊的函數用於將任意實數映射到(0,1)區間.

上文中的sigmod函數稱爲神經元的激勵函數(activation function), 除了sigmod函數\(\frac{1}{1+e^{-I_l}}\)外, 經常使用還有tanh和ReLU函數.

咱們用一個完成訓練的神經網絡處理迴歸問題, 每一個樣本擁有n個輸入.相應地,神經網絡擁有n個輸入神經元和1個輸出神經元.

實際應用中咱們一般在輸入層額外增長一個偏置神經元, 提供一個可控的輸入修正;或者爲每一個隱含層神經元設置一個偏置參數.

咱們將n個特徵依次送入輸入神經元, 隱含層神經元得到輸入層的輸出並計算本身輸出值, 輸出層的神經元根據隱含層輸出計算出迴歸值.

上述過程通常稱爲前饋(Feed-Forward)過程, 該過程當中神經網絡的輸入輸出與多維函數無異.

如今咱們的問題是如何訓練這個神經網絡.

做爲監督學習算法,BP神經網絡的訓練過程便是根據前饋獲得的預測值和參考值比較, 根據偏差調整鏈接權重\(W_{ij}\)的過程.

訓練過程稱爲反向傳播過程(BackPropagation), 數據流正好與前饋過程相反.

首先咱們隨機初始化鏈接權重\(W_{ij}\), 對某一訓練樣本進行一次前饋過程獲得各神經元的輸出.

首先計算輸出層的偏差:

\[E_j= sigmod'(O_j)*(T_j-O_j) =O_j(1-O_j)(T_j-O_j)\]

其中\(E_j\)表明神經元j的偏差,\(O_j\)表示神經元j的輸出, \(T_j\)表示當前訓練樣本的參考輸出, \(sigmod'(x)\)是上文sigmod函數的一階導數.

計算隱含層偏差:

\[E_j= sigmod'(O_j)*\sum_kE_kW_{jk} =O_j(1-O_j)\sum_kE_kW_{jk}\]

隱含層輸出不存在參考值, 使用下一層偏差的加權和代替\((T_j-O_j)\).

計算完偏差後就能夠更新\(W_{ij}\)\(\theta_j\):

\[ W_{ij}=W_{ij}+\lambda E_jO_i \]

其中\(\lambda\)是一個稱爲學習率的參數,通常在(0,0.1)區間上取值.

實際上爲了加快學習的效率咱們引入稱爲矯正矩陣的機制, 矯正矩陣記錄上一次反向傳播過程當中的\(E_jO_i\)值, 這樣\(W_j\)更新公式變爲:

\[ W_{ij}=W_{ij}+\lambda E_jO_i + \mu C_{ij}\]

\(\mu\)是一個稱爲矯正率的參數.隨後更新矯正矩陣:

\[ C_{ij} = E_jO_i \]

每個訓練樣本都會更新一次整個網絡的參數.咱們須要額外設置訓練終止的條件.

最簡單的訓練終止條件爲設置最大迭代次數, 如將數據集迭代1000次後終止訓練.

單純的設置最大迭代次數不能保證訓練結果的精確度, 更好的辦法是使用損失函數(loss function)做爲終止訓練的依據.

損失函數能夠選用輸出層各節點的方差:

\[ L = \sum_j(T_j-O_j)^2\]

爲了不神經網絡進行無心義的迭代, 咱們一般在訓練數據集中抽出一部分用做校驗.當預測偏差高於閾值時提早終止訓練.

Python實現BP神經網絡

首先實現幾個工具函數:

def rand(a, b):
    return (b - a) * random.random() + a


def make_matrix(m, n, fill=0.0):  # 創造一個指定大小的矩陣
    mat = []
    for i in range(m):
        mat.append([fill] * n)
    return mat

定義sigmod函數和它的導數:

def sigmoid(x):
    return 1.0 / (1.0 + math.exp(-x))


def sigmod_derivate(x):
    return x * (1 - x)

定義BPNeuralNetwork類, 使用三個列表維護輸入層,隱含層和輸出層神經元, 列表中的元素表明對應神經元當前的輸出值.使用兩個二維列表以鄰接矩陣的形式維護輸入層與隱含層, 隱含層與輸出層之間的鏈接權值, 經過一樣的形式保存矯正矩陣.

定義setup方法初始化神經網絡:

def setup(self, ni, nh, no):
    self.input_n = ni + 1
    self.hidden_n = nh
    self.output_n = no
    # init cells
    self.input_cells = [1.0] * self.input_n
    self.hidden_cells = [1.0] * self.hidden_n
    self.output_cells = [1.0] * self.output_n
    # init weights
    self.input_weights = make_matrix(self.input_n, self.hidden_n)
    self.output_weights = make_matrix(self.hidden_n, self.output_n)
    # random activate
    for i in range(self.input_n):
        for h in range(self.hidden_n):
            self.input_weights[i][h] = rand(-0.2, 0.2)
    for h in range(self.hidden_n):
        for o in range(self.output_n):
            self.output_weights[h][o] = rand(-2.0, 2.0)
    # init correction matrix
    self.input_correction = make_matrix(self.input_n, self.hidden_n)
    self.output_correction = make_matrix(self.hidden_n, self.output_n)

定義predict方法進行一次前饋, 並返回輸出:

def predict(self, inputs):
    # activate input layer
    for i in range(self.input_n - 1):
        self.input_cells[i] = inputs[i]
    # activate hidden layer
    for j in range(self.hidden_n):
        total = 0.0
        for i in range(self.input_n):
            total += self.input_cells[i] * self.input_weights[i][j]
        self.hidden_cells[j] = sigmoid(total)
    # activate output layer
    for k in range(self.output_n):
        total = 0.0
        for j in range(self.hidden_n):
            total += self.hidden_cells[j] * self.output_weights[j][k]
        self.output_cells[k] = sigmoid(total)
    return self.output_cells[:]

定義back_propagate方法定義一次反向傳播和更新權值的過程, 並返回最終預測偏差:

def back_propagate(self, case, label, learn, correct):
    # feed forward
    self.predict(case)
    # get output layer error
    output_deltas = [0.0] * self.output_n
    for o in range(self.output_n):
        error = label[o] - self.output_cells[o]
        output_deltas[o] = sigmod_derivate(self.output_cells[o]) * error
    # get hidden layer error
    hidden_deltas = [0.0] * self.hidden_n
    for h in range(self.hidden_n):
        error = 0.0
        for o in range(self.output_n):
            error += output_deltas[o] * self.output_weights[h][o]
        hidden_deltas[h] = sigmod_derivate(self.hidden_cells[h]) * error
    # update output weights
    for h in range(self.hidden_n):
        for o in range(self.output_n):
            change = output_deltas[o] * self.hidden_cells[h]
            self.output_weights[h][o] += learn * change + correct * self.output_correction[h][o]
            self.output_correction[h][o] = change
    # update input weights
    for i in range(self.input_n):
        for h in range(self.hidden_n):
            change = hidden_deltas[h] * self.input_cells[i]
            self.input_weights[i][h] += learn * change + correct * self.input_correction[i][h]
            self.input_correction[i][h] = change
    # get global error
    error = 0.0
    for o in range(len(label)):
        error += 0.5 * (label[o] - self.output_cells[o]) ** 2
    return error

定義train方法控制迭代, 該方法能夠修改最大迭代次數, 學習率\(\lambda\), 矯正率\(\mu\)三個參數.

def train(self, cases, labels, limit=10000, learn=0.05, correct=0.1):
    for i in range(limit):
        error = 0.0
        for i in range(len(cases)):
            label = labels[i]
            case = cases[i]
            error += self.back_propagate(case, label, learn, correct)

編寫test方法,演示如何使用神經網絡學習異或邏輯:

def test(self):
    cases = [
            [0, 0],
            [0, 1],
            [1, 0],
            [1, 1],
        ]
    labels = [[0], [1], [1], [0]]
    self.setup(2, 5, 1)
    self.train(cases, labels, 10000, 0.05, 0.1)
    for case in cases:
        print(self.predict(case))

完整源代碼參見bpnn.py

使用tensorflow實現一個神經網絡多是更簡單高效的方法, 能夠參見tensorflow入門指南中的第二節:實現一個簡單神經網絡.

相關文章
相關標籤/搜索