人工智能初級算法——誤差反向傳播算法(BP)

誤差反向傳播算法(BP)

一、神經網絡基本概念

二、神經元模型

神經元細胞示意圖
在這裏插入圖片描述
在這裏插入圖片描述

1. 單個神經元

1> 感知器模型

在這裏插入圖片描述

2> 輸入與輸出

3> 激活函數

Sigmoid(x)=11+ex Sigmoid(x) = \frac{1}{1+e^{-x}}
在這裏插入圖片描述
Sigmoid(x)=Sigmoid(x)(1Sigmoid(x)) Sigmoid'(x) = Sigmoid(x) * (1 - Sigmoid(x))

2. 單個神經元的學習

1> 識別

在這裏插入圖片描述

3. 多層神經元模型

在這裏插入圖片描述

三、誤差反向傳播算法(BP)

1. 算法的推導

在這裏插入圖片描述

符號說明:

  • X: 輸入向量
  • YtrueY_{true}: 輸入向量 X 對應的真實結果
  • YpredY_{pred}: 根據輸入向量 X 預測的結果
  • Yi: 向量的某個分量
  • h_in: 隱藏層輸入
  • h_out: 隱藏層輸出(經過激活函數處理)
  • h_w: 隱藏層權重矩陣
  • output_in: 輸出層輸入
  • output_out: 輸出層輸出(經過激活函數處理)
  • output_w: 輸出層權重矩陣

1> 輸出層權重偏導

已知對於某個訓練樣例 d 的誤差函數:
Error=12ioutput(YitrueYipred)2 Error = \frac{1}{2} * \sum_{i \in output}(Yi_{true}-Yi_{pred})^2
可得,總誤差函數對輸出層權重 w35w_{35} 的偏導數:
Ew35=Eoutput5_outoutput5_outoutput5_inoutput5_inw35 \frac{\partial{E}}{\partial{w_{35}}} = \frac{\partial{E}}{\partial{output5\_out}} * \frac{\partial{output5\_out}}{\partial{output5\_in}} * \frac{\partial{output5\_in}}{\partial{w_{35}}}
各項展開得:
Eoutput5_out=(Y5trueoutput5_out) \frac{\partial{E}}{\partial{output5\_out}} = -(Y5_{true} - output5\_out)
output5_outoutput5_in=Sigmoid(output5_in)(1Sigmoid(output5_in)) \frac{\partial{output5\_out}}{\partial{output5\_in}} = Sigmoid(output5\_in)*(1-Sigmoid(output5\_in))
output5_in = h3_out * w_{35} + h4_out * w_{45}
output_inw35=h3_out \frac{\partial{output\_in}}{\partial{w_{35}}} = h3\_out

最終偏導爲:
Ew35=(Y5trueoutput5_out)Sigmoid(output5_in)(1Sigmoid(output5_in))h3_out \frac{\partial{E}}{\partial{w35}} = -(Y5_{true} - output5\_out) * Sigmoid(output5\_in)*(1-Sigmoid(output5\_in)) * h3\_out

2> 隱藏層權重偏導

總體上,隱藏層權重偏導公式如下:
Ew13=Eh3_outh3_outh3_inh3_inw13 \frac{\partial{E}}{\partial{w_{13}}} = \frac{\partial{E}}{\partial{h3\_out}} * \frac{\partial{h3\_out}}{\partial{h3\_in}} * \frac{\partial{h3\_in}}{\partial{w_{13}}}
展開,得:
Ew13=Eh3_outSigmoid(h3_in)(1Sigmoid(h3_in))x1 \frac{\partial{E}}{\partial{w_{13}}} = \frac{\partial{E}}{\partial{h3\_out}} * Sigmoid(h3\_in) * (1 - Sigmoid(h3\_in))* x_1
仔細分析,我們只需對權重 w13w_{13} 直接影響的節點求導即可:
Eh3_out=iDownstream(h3)Eoutputi_outoutputi_outoutputi_inoutputi_inh3_out \frac{\partial{E}}{\partial{h3\_out}} = \sum_{i \in Downstream(h3)} \frac{\partial{E}}{\partial{outputi\_out}} * \frac{\partial{outputi\_out}}{\partial{outputi\_in}} * \frac{\partial{outputi\_in}}{\partial{h3\_out}}
各項展開得:
Eh3_out=iDownstream(h3)(Yitrueoutputi_out)Sigmoid(outputi_in)(1Sigmoid(outputi_in))w3i \frac{\partial{E}}{\partial{h3\_out}} = \sum_{i \in Downstream(h3)} -(Yi_{true} - outputi\_out) * Sigmoid(outputi\_in) * (1 - Sigmoid(outputi\_in)) * w_{3i}

3. 訓練

1> 調整單個權重 w。我們追求誤差減小,故,有以下公式:

w35=w35Δw35 w_{35} = w_{35} - \Delta w_{35}
定義學習速率:rr
w35=w35rEw35 w_{35} = w_{35} - r * \frac{\partial{E}}{\partial{w_{35}}}
輸出層權重調整:
w35=w35+r(Y5trueoutput5_out)Sigmoid(output5_in)(1Sigmoid(output5_in))h3_out w_{35} = w_{35} + r * (Y5_{true} - output5\_out) * Sigmoid(output5\_in)*(1-Sigmoid(output5\_in)) * h3\_out
w45=w45+r(Y5trueoutput5_out)Sigmoid(output5_in)(1Sigmoid(output5_in))h4_out w_{45} = w_{45} + r * (Y5_{true} - output5\_out) * Sigmoid(output5\_in)*(1-Sigmoid(output5\_in)) * h4\_out
w36=w36+r(Y6trueoutput6_out)Sigmoid(output6_in)(1Sigmoid(output6_in))h3_out w_{36} = w_{36} + r * (Y6_{true} - output6\_out) * Sigmoid(output6\_in)*(1-Sigmoid(output6\_in)) * h3\_out
w46=w46+r(Y6trueoutput6_out)Sigmoid(output6_in)(1Sigmoid(output6_in))h4_out w_{46} = w_{46} + r * (Y6_{true} - output6\_out) * Sigmoid(output6\_in)*(1-Sigmoid(output6\_in)) * h4\_out
利用矩陣運算簡化
ow+=r(Y5trueY5predY6trueY6pred)Sigmoid(output5_inoutput6_in)×(h3_outh4_out) ow += r * \left( \begin{array}{cc} Y5_{true} - Y5_{pred} \\ Y6_{true} - Y6_{pred} \end{array} \right) \cdot Sigmoid'\left( \begin{array}{cc} output5\_{in} \\ output6\_{in} \end{array} \right) \times \left( \begin{array}{c} h3\_{out} & h4\_{out} \end{array} \right)
繼續化簡(默認向量爲列向量):
ow+=r(YtrueYpred)Sigmoid(output_in)×h_out.T ow += r * (Y_{true} - Y_{pred}) \cdot Sigmoid'(output\_in) \times h\_out.T
令:
output_errors=(YtrueYpred) output\_errors = (Y_{true} - Y_{pred})
則:
hw+=r(output_errorsSigmoid(output_in))×h_out.T hw += r * (output\_errors \cdot Sigmoid'(output\_in)) \times h\_out.T

隱藏層權重調整:
w13=w13+rSigmoid(h3_in)x1iDownstream(h3)(Yitrueoutputi_out)Sigmoid(outputi_in)w3i w_{13} = w_{13} + r * Sigmoid'(h3\_in) * x_1 * \sum_{i \in Downstream(h3)} (Yi_{true} - outputi\_out) * Sigmoid'(outputi\_in) * w_{3i}

w23=w23+rSigmoid(h3_in)x2iDownstream(h3)(Yitrueoutputi_out)Sigmoid(outputi_in)w3i w_{23} = w_{23} + r * Sigmoid'(h3\_in) * x_2 * \sum_{i \in Downstream(h3)} (Yi_{true} - outputi\_out) * Sigmoid'(outputi\_in) * w_{3i}

w14=w14+rSigmoid(h4_in)x1iDownstream(h4)(Yitrueoutputi_out)Sigmoid(outputi_in)w4i w_{14} = w_{14} + r * Sigmoid'(h4\_in) * x_1 * \sum_{i \in Downstream(h4)} (Yi_{true} - outputi\_out) * Sigmoid'(outputi\_in) * w_{4i}

w24=w24+rSigmoid(h4_in)x2iDownstream(h4)(Yitrueoutputi_out)Sigmoid(outputi_in)w4i w_{24} = w_{24} + r * Sigmoid'(h4\_in) * x_2 * \sum_{i \in Downstream(h4)} (Yi_{true} - outputi\_out) * Sigmoid'(outputi\_in) * w_{4i}
利用矩陣簡化:
hwij+=rSigmoid(hj_inhj_in)(x1x2)×kDownstream(hj)(YktrueYkpred)Sigmoid(outputk_in)×wjk hw_{ij} += r * Sigmoid'\left(\begin{array}{c} hj\_in \\ hj\_in \end{array}\right) \cdot \left(\begin{array}{c} x_1 \\ x_2 \end{array}\right) \times \sum_{k \in Downstream(h_j)} ( Yk_{true} - Yk_{pred} ) \cdot Sigmoid'( outputk\_{in} ) \times w_{jk}
令(默認向量爲列向量):
hidden_errors=output_w.T×((YktrueYkpred)Sigmoid(output_in)) hidden\_errors = output\_w.T \times ((Yk_{true} - Yk_{pred} ) \cdot Sigmoid'( output\_{in}) )
帶入得(默認向量爲列向量):
hw+=r(hidden_errorsSigmoid(h_in))×X.T hw += r * (hidden\_errors \cdot Sigmoid'(h\_in)) \times X.T

2> 隨機梯度下降

  1. 對訓練樣本中的每一個數據,進行一次訓練
  2. 對整個樣本進行多次(例如:100次)訓練

4. 編碼

#_author :NineSun
#data: 2019-10-12

import  numpy as np

def activate(x):
    return 1/(1+np.exp(-x))
def activate_de(x):
    return activate(x)*(1-activate(x))
class NeutualNetWork:
    # 構造方法,用於構造神經網絡
    def __init__(self):
        # 隱藏層權重矩陣
        self.hw=np.random.normal(np.zeros((2,2)))
        # 輸出層權重矩陣
        self.ow=np.random.normal(np.zeros((2,2)))
        # 學習速率
        self.r=0.1
        # 訓練遍數
        self.epoch=100

    # 預測函數,根據輸入數據預測結果
    # x:輸入向量
    def prdict(self,x):
        # 將輸入向量轉化爲列向量
        x=np.array(x,ndmin=2).T
        # 計算隱藏層輸入
        h_in=np.dot(self.hw,x)
        # 計算隱藏層輸出
        h_out = activate(h_in)
        # 計算輸出層輸入 output_in
        output_in=np.dot(self.ow,h_out)
        # 計算輸出層輸出
        output_out=activate(output_in)
        return output_out

    # 訓練神經網絡
    # x_data:輸入數據集
    # ytrue_data:輸出數據集
    def train(self,x_data,ytrue_data):
        # 訓練epoch遍
        for i in range(self.epoch):
            # 針對每一個樣本訓練一遍
            for data in zip(x_data,ytrue_data):
                self.train_once(data[0],data[1])


    # 針對一個樣本(x,ytrue)進行訓練
    def train_once(self,x,ytrue):
        '''
        :param x: 輸入行向量
        :param ytrue: 輸出行向量
        :return:
        '''
        # 將輸入輸出向量轉化爲列向量
        x = np.array(x, ndmin=2).T
        ytrue = np.array(ytrue, ndmin=2).T


        # 根據輸入計算 Ypred,output_out
        h_in=np.dot(self.hw,x)
        h_out=activate(h_in)
        output_in=np.dot(self.ow,h_out)
        output_out=activate(output_in)

        # 計算輸出誤差
        output_errors=ytrue-output_out
        cal_tmp=output_errors*activate_de(output_in)
        # 更新輸出層權重
        self.ow+=self.r*np.dot(cal_tmp,h_out.T)

        # 計算隱藏層誤差
        hidden_errors=np.dot(self.ow.T,cal_tmp)

        # 更新隱藏層權重
        self.hw+=self.r*np.dot(hidden_errors*activate_de(h_in),x.T)

    # 1.準備數據
in_data = [
        [165, 55],
        [160, 53],
        [175, 55],
        [163, 55],
        [173, 49],
        [163, 56],
        [180, 77],
        [155, 54],
        [176, 79],
        [161, 49],
        [180, 60],
        [168, 57],
        [172, 69],
        [166, 50],
        [172, 90],
        [163, 51],
        [175, 70],
        [164, 53],
        [160, 45],
        [160, 56],
        [182, 70],
        [180, 74],
        [185, 73],
        [165, 55],
        [185, 75]
    ]
out_data = [
            [1,0],
            [0,1],
            [1, 0],
            [0, 1],
            [1, 0],
            [0, 1],
            [1, 0],
            [0, 1],
            [1, 0],
            [0, 1],
            [1, 0],
            [0, 1],
            [1, 0],
            [0, 1],
            [1, 0],
            [0, 1],
            [1, 0],
            [0, 1],
            [1, 0],
            [0, 1],
            [1, 0],
            [0, 1],
            [1, 0],
            [0, 1],
            [1, 0],
            [0, 1],
            [1, 0],
            [0, 1],
            [1, 0],
            [0, 1]
    ]
    # 測試數據
in_test=[
        [165, 55],
        [160, 53],
        [175, 55],
        [163, 55],
        [173, 49]
    ]
out_test=[
        [0,1],
        [0, 1],
        [1, 0],
        [0, 1],
        [1, 0],
    ]

nn = NeutualNetWork()
    # 3.訓練
nn.train(in_data,out_data)
    # 4.預測
for test in zip(in_test,out_test):
        ypred=nn.prdict(test[0])
        print("in:",test[0],"yture:",test[1])
        print("predit:",ypred)

這串代碼可以直接運行

五、 算法優化

1. 指定網絡結構

可隨意指定:輸入層,隱藏層,輸出層的神經元個數

2. 存儲當前神經網絡的訓練數據

將神經網絡結構,包括:學習速率,訓練次數和所有權重矩陣存成文件,以便以後可以不必訓練,加載數據後可直接進行預測。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章