bp神經網絡推導及python實例

bp神經網絡

BP(back propagation,反向傳播)神經網絡(neural network),通常指具有三層網絡結構的淺層神經網絡。神經網絡由一個個神經元(Neuron)組成,神經元由輸入、計算、輸出單元組成。

image

對應上圖輸入爲x1,x2, ,xnx_1,x_2,\cdots,x_n和截距+1+1,輸出爲:
y^=hw,b(X)=f(wTX)=f(i=1nwixi+b) \hat y=h_{w,b}(X)=f(w^T X)=f(\sum_{i=1}^n w_i x_i+b)
其中w表示權重值,函數f爲激活函數,有如下激活函數:
sigmoid:f(x)=11+expx sigmoid: f(x)=\frac{1}{1+exp^{-x} }
tanh:f(x)=exexex+ex tanh: f(x)=\frac{e^x-e^{-x}}{e^x+e^{-x}}
ReLu:f(x)=max(0,x) ReLu: f(x)=max(0,x)
SoftPlus:f(x)=loge(1+ex) SoftPlus: f(x)=log_e(1+e^x)
對應圖像爲:

image

一個三層的神經網絡結構圖:

image

相關參數說明

  • 網絡層數nln_l,三層網絡nl=3n_l=3,第ll層記爲LlL_l
  • L1,L2,L3L_1,L_2,L_3分別爲輸入層,隱藏層,輸出層
  • 權重w=(w1,w2)w=(w^1,w^2),其中wijlw_{ij}^l表示ll層的第jj個神經元與l+1l+1層的第ii個神經元的參數,如w211w_{21}^1表示1層第1個神經元與2層第2個神經元的連接參數
  • 偏置bilb_i^l表示第l+1l+1層的第i個神經元的偏置項,如b21b_2^1表示2層第2個神經元的偏置項
  • zilz_i^l表示ll層第ii個神經元的輸入
  • aila_i^l表示ll層第ii個神經元的輸出
  • SlS_l表示ll層的神經元個數,如S3S_3表示3層有2個神經元

參數計算關係如下:
a22=f(z22)=f(w211x1+w221x2+w231x3+b21) a_2^2=f(z_2^2)=f(w_{21}^1 x_1+w_{22}^1 x_2 +w_{23}^1 x_3+b_2^1)
即每個神經元的輸入爲上一層所有神經元輸出的加權求和,神經元輸入值經過激活函數處理後得到神經元輸出。

損失函數

對於每個訓練樣本(X,y)(X,y),損失函數爲:
J(W,b;X,y)=12hw,b(X)y2 J(W,b;X,y)=\frac{1}{2}||h_{w,b}(X)-y||^2
表示最後一層輸出層的輸出值與實際值的歐式距離,結果是一個向量,向量維度等於輸出層神經元數量。

爲得到損失函數最小值,首先對參數進行初始化,初始化爲一個接近0的隨機值。再利用前向傳播得到預測值,從而計算損失值。此時需要利用損失函數調整參數,可使用梯度下降法,梯度下降公式爲:
Wijl=WijlαJ(W,b)Wijl W_{ij}^l=W_{ij}^l-\alpha\frac{\partial J(W,b)}{\partial W_{ij}^l}
bil=bilαJ(W,b)bil b_i^l=b_i^l-\alpha\frac{\partial J(W,b)}{\partial b_i^l}
其中偏導部分:
J(W,b)Wijl=[1mk=1mJ(W,b;xk,yk)Wijl] \frac{\partial J(W,b)}{\partial W_{ij}^l}=[\frac{1}{m}\sum_{k=1}^m \frac{\partial J(W,b;x^k,y^k)}{\partial W_{ij}^l} ]
J(W,b)bil==[1mk=1mJ(W,b;xk,yk)bil] \frac{\partial J(W,b)}{\partial b_i^l}==[\frac{1}{m}\sum_{k=1}^m \frac{\partial J(W,b;x^k,y^k)}{\partial b_i^l} ]
由於每兩層之間有WW參數矩陣,考慮到預測值在最後一層輸出層,可以先求解Wijnl1W_{ij}^{n_l -1},推導如下:
J(W,b)Wijnl1=12anly2Wijnl1= \frac{\partial J(W,b)}{\partial W_{ij}^{n_l -1}}=\frac{\partial \frac{1}{2}||a^{n_l}-y||^2}{\partial W_{ij}^{n_l -1}}=
12k=1Snl(aknlyk)2Wijnl1= \frac{\partial \frac{1}{2} \sum_{k=1}^{S_{n_l}} (a_k^{n_l}-y_k)^2 }{\partial W_{ij}^{n_l -1}}=
12k=1Snl(f(zknl)yk)2Wijnl1 \frac{\partial \frac{1}{2} \sum_{k=1}^{S_{n_l}} (f(z_k^{n_l})-y_k)^2 }{\partial W_{ij}^{n_l -1}}
其中zinlz_i^{n_l}等於:
zinl=p=1Snl1[Wipnl1apnl1+binl1] z_i^{n_l}=\sum_{p=1}^{S_{n_l -1}}[W_{ip}^{n_l -1}a_p^{n_l -1}+b_i^{n_l -1}]
zinlz_i^{n_l}Wijnl1W_{ij}^{n_l -1}可導,可以使用鏈式法則求導:
J(W,b)Wijnl1= \frac{\partial J(W,b)}{\partial W_{ij}^{n_l -1}}=
12k=1Snl(f(zknl)yk)2zinlzinlWijnl1= \frac{\partial \frac{1}{2} \sum_{k=1}^{S_{n_l}} (f(z_k^{n_l})-y_k)^2 }{\partial z_i^{n_l}} \cdot \frac {\partial z_i^{n_l}} {\partial W_{ij}^{n_l -1}}=
[f(zinl)yi]f(zinl)zinlWijnl1= [f(z_i^{n_l})-y_i]\cdot f'(z_i^{n_l})\cdot \frac {\partial z_i^{n_l}} {\partial W_{ij}^{n_l -1}}=
[f(zinl)yi]f(zinl)ajnl1 [f(z_i^{n_l})-y_i]\cdot f'(z_i^{n_l})\cdot a_j^{n_l -1}

反向傳播算法的思路爲,對於給定訓練數據(X,y)(X,y),通過前向傳播算法計算每個神經元的輸出值,當所有神經元的輸出都計算完成後,對每個神經元計算殘差,如第ll層的第i個神經元的殘差表示爲δil\delta_i^l,該殘差表示該神經元對最終殘差的影響,最後一層的殘差公式爲:
δinl=J(W,b)zinl=[f(zinl)yi]f(zinl) \delta_i^{n_l}=\frac{\partial J(W,b)}{\partial z_i^{n_l}}=[f(z_i^{n_l})-y_i]\cdot f'(z_i^{n_l})
δil\delta_i^l代入,得出:
J(W,b)Wijnl1=δinlajnl1 \frac{\partial J(W,b)}{\partial W_{ij}^{n_l -1}}=\delta_i^{n_l}\cdot a_j^{n_l -1}

其中ajnl1a_j^{n_l -1}可以通過前向傳播得到,需要求解δinl\delta_i^{n_l},可以推導倒數第二層殘差與最後一層殘差的關係:
δinl1=J(W,b)zinl1= \delta_i^{n_l -1}=\frac{\partial J(W,b)}{\partial z_i^{n_l -1}}=
12k=1Snl(f(zknl)yk)2zinl1= \frac{\partial \frac{1}{2} \sum_{k=1}^{S_{n_l}} (f(z_k^{n_l})-y_k)^2 }{\partial z_i^{n_l -1}}=
12k=1Snl(f(zknl)yk)2zinl1= \frac{1}{2} \sum_{k=1}^{S_{n_l}} \frac{\partial (f(z_k^{n_l})-y_k)^2 }{\partial z_i^{n_l -1}}=
12k=1Snl(f(zknl)yk)2zknlzknlzinl1= \frac{1}{2} \sum_{k=1}^{S_{n_l}} \frac{\partial (f(z_k^{n_l})-y_k)^2 }{\partial z_k^{n_l}}\cdot \frac{\partial z_k^{n_l}}{\partial z_i^{n_l -1}}=
k=1Snlδknlzknlzinl1= \sum_{k=1}^{S_{n_l}} \delta_k^{n_l}\cdot \frac{\partial z_k^{n_l}}{\partial z_i^{n_l -1}}=
k=1Snlδknlj=1Snl1[f(zjnl1)Wkjnl1+bjnl1]zinl1= \sum_{k=1}^{S_{n_l}} \delta_k^{n_l}\cdot \frac{\partial \sum_{j=1}^{S_{n_l-1}} [f(z_j^{n_l-1})\cdot W_{kj}^{n_l-1}+b_j^{n_l-1}]}{\partial z_i^{n_l -1}}=
k=1SnlδknlWkinl1f(zinl1)= \sum_{k=1}^{S_{n_l}} \delta_k^{n_l}\cdot W_{ki}^{n_l-1} f'(z_i^{n_l-1})=
[k=1SnlδknlWkinl1]f(zinl1) [\sum_{k=1}^{S_{n_l}} \delta_k^{n_l}\cdot W_{ki}^{n_l-1}] \cdot f'(z_i^{n_l-1})

δinl1=[k=1SnlδknlWkinl1]f(zinl1) \delta_i^{n_l -1}=[\sum_{k=1}^{S_{n_l}} \delta_k^{n_l}\cdot W_{ki}^{n_l-1}] \cdot f'(z_i^{n_l-1})
推導到一般情況:
δil=[k=1Sl+1δkl+1Wkil]f(zil) \delta_i^l=[\sum_{k=1}^{S_{l+1}} \delta_k^{l+1}\cdot W_{ki}^l] \cdot f'(z_i^l)
J(W,b)Wijl=δil+1ajl \frac{\partial J(W,b)}{\partial W_{ij}^l}=\delta_i^{l+1}\cdot a_j^l
J(W,b)bil=δil+1 \frac{\partial J(W,b)}{\partial b_i^l}=\delta_i^{l+1}

python實例

# 代碼來自《Python機器學習算法》一書
def bp_train(feature, label, n_hidden, maxCycle, alpha, n_output):
    '''計算隱含層的輸入
    input:  feature(mat):特徵
            label(mat):標籤
            n_hidden(int):隱含層的節點個數
            maxCycle(int):最大的迭代次數
            alpha(float):學習率
            n_output(int):輸出層的節點個數
    output: w0(mat):輸入層到隱含層之間的權重
            b0(mat):輸入層到隱含層之間的偏置
            w1(mat):隱含層到輸出層之間的權重
            b1(mat):隱含層到輸出層之間的偏置
    '''
    m, n = np.shape(feature)
    # 1、初始化
    w0 = np.mat(np.random.rand(n, n_hidden))
    w0 = w0 * (8.0 * sqrt(6) / sqrt(n + n_hidden)) - \
     np.mat(np.ones((n, n_hidden))) * \
      (4.0 * sqrt(6) / sqrt(n + n_hidden))
    b0 = np.mat(np.random.rand(1, n_hidden))
    b0 = b0 * (8.0 * sqrt(6) / sqrt(n + n_hidden)) - \
     np.mat(np.ones((1, n_hidden))) * \
      (4.0 * sqrt(6) / sqrt(n + n_hidden))
    w1 = np.mat(np.random.rand(n_hidden, n_output))
    w1 = w1 * (8.0 * sqrt(6) / sqrt(n_hidden + n_output)) - \
     np.mat(np.ones((n_hidden, n_output))) * \
      (4.0 * sqrt(6) / sqrt(n_hidden + n_output))
    b1 = np.mat(np.random.rand(1, n_output))
    b1 = b1 * (8.0 * sqrt(6) / sqrt(n_hidden + n_output)) - \
     np.mat(np.ones((1, n_output))) * \
      (4.0 * sqrt(6) / sqrt(n_hidden + n_output))

    # 2、訓練
    i = 0
    while i <= maxCycle:
        # 2.1、信號正向傳播
        # 2.1.1、計算隱含層的輸入
        hidden_input = hidden_in(feature, w0, b0)  # mXn_hidden
        # 2.1.2、計算隱含層的輸出
        hidden_output = hidden_out(hidden_input)
        # 2.1.3、計算輸出層的輸入
        output_in = predict_in(hidden_output, w1, b1)  # mXn_output
        # 2.1.4、計算輸出層的輸出
        output_out = predict_out(output_in)

        # 2.2、誤差的反向傳播
        # 2.2.1、隱含層到輸出層之間的殘差
        delta_output = -np.multiply((label - output_out), partial_sig(output_in))
        # 2.2.2、輸入層到隱含層之間的殘差
        delta_hidden = np.multiply((delta_output * w1.T), partial_sig(hidden_input))

        # 2.3、 修正權重和偏置       
        w1 = w1 - alpha * (hidden_output.T * delta_output)
        b1 = b1 - alpha * np.sum(delta_output, axis=0) * (1.0 / m)
        w0 = w0 - alpha * (feature.T * delta_hidden)
        b0 = b0 - alpha * np.sum(delta_hidden, axis=0) * (1.0 / m)
        if i % 100 == 0:
            print "\t-------- iter: ", i, \
            " ,cost: ",  (1.0/2) * get_cost(get_predict(feature, w0, w1, b0, b1) - label)
        i += 1
    return w0, w1, b0, b1
(1)初始化參數
  • 樣本特徵數 n=2

  • 隱藏層節點數 n_hidden=20

  • 輸出層節點數(分類數量) n_output=2

  • 構成 2*20*2 的三層神經網絡結構

  • 輸入層到隱藏層的權重w0 = np.mat(np.random.rand(n, n_hidden)),即2*20

  • 輸入層到隱藏層的偏置b0 = np.mat(np.random.rand(1, n_hidden)),即20

  • 隱藏層到輸出層的權重w1 = np.mat(np.random.rand(n_hidden, n_output)),即20*2

  • 隱藏層到輸出層的偏置b1 = np.mat(np.random.rand(1, n_output)),即2

可以利用更科學的隨機算法,得到隨機化的w0,b0,w1,b1

(2)正向傳播
# 2.1.1、計算隱含層的輸入
hidden_input = hidden_in(feature, w0, b0)  # mXn_hidden
# 2.1.2、計算隱含層的輸出
hidden_output = hidden_out(hidden_input)
# 2.1.3、計算輸出層的輸入
output_in = predict_in(hidden_output, w1, b1)  # mXn_output
# 2.1.4、計算輸出層的輸出
output_out = predict_out(output_in)

hidden_in方法計算隱藏層的輸入值,對應公式:
zil=k=1Sl1[Wikl1akl1+bil1] z_i^l=\sum_{k=1}^{S_{l -1}}[W_{ik}^{l -1}a_k^{l -1}+b_i^{l -1}]

def hidden_in(feature, w0, b0):
    '''計算隱含層的輸入
    input:  feature(mat):特徵
            w0(mat):輸入層到隱含層之間的權重
            b0(mat):輸入層到隱含層之間的偏置
    output: hidden_in(mat):隱含層的輸入
    '''
    m = np.shape(feature)[0]
    hidden_in = feature * w0
    for i in xrange(m):
        hidden_in[i, ] += b0
    return hidden_in

hidden_out方法計算隱藏層的輸出,對應公式:
ail=f(zil) a_i^l=f(z_i^l)

def hidden_out(hidden_in):
    '''隱含層的輸出
    input:  hidden_in(mat):隱含層的輸入
    output: hidden_output(mat):隱含層的輸出
    '''
    hidden_output = sig(hidden_in)
    return hidden_output;

predict_in方法等同於hidden_inpredict_out方法等同於hidden_out

(3)反向傳播
# 2.2.1、隱含層到輸出層之間的殘差
delta_output = -np.multiply((label - output_out), partial_sig(output_in))
# 2.2.2、輸入層到隱含層之間的殘差
delta_hidden = np.multiply((delta_output * w1.T), partial_sig(hidden_input))

partial_sig方法計算輸入值的偏導值,delta_output對應最後一層的殘差公式:
δinl=J(W,b)zinl=[f(zinl)yi]f(zinl) \delta_i^{n_l}=\frac{\partial J(W,b)}{\partial z_i^{n_l}}=[f(z_i^{n_l})-y_i]\cdot f&#x27;(z_i^{n_l})
delta_hidden對應一般情況的殘差公式:
δil=[k=1Sl+1δkl+1Wkil]f(zil) \delta_i^l=[\sum_{k=1}^{S_{l+1}} \delta_k^{l+1}\cdot W_{ki}^l] \cdot f&#x27;(z_i^l)

def partial_sig(x):
    '''Sigmoid導函數的值
    input:  x(mat/float):自變量,可以是矩陣或者是任意實數
    output: out(mat/float):Sigmoid導函數的值
    '''
    m, n = np.shape(x)
    out = np.mat(np.zeros((m, n)))
    for i in xrange(m):
        for j in xrange(n):
            out[i, j] = sig(x[i, j]) * (1 - sig(x[i, j]))
    return out
(4)更新權重和偏置
w1 = w1 - alpha * (hidden_output.T * delta_output)
b1 = b1 - alpha * np.sum(delta_output, axis=0) * (1.0 / m)
w0 = w0 - alpha * (feature.T * delta_hidden)
b0 = b0 - alpha * np.sum(delta_hidden, axis=0) * (1.0 / m)

對應公式:
J(W,b)Wijl=δil+1ajl \frac{\partial J(W,b)}{\partial W_{ij}^l}=\delta_i^{l+1}\cdot a_j^l
J(W,b)bil=δil+1 \frac{\partial J(W,b)}{\partial b_i^l}=\delta_i^{l+1}

(5)預測
def get_predict(feature, w0, w1, b0, b1):
    '''計算最終的預測
    input:  feature(mat):特徵
            w0(mat):輸入層到隱含層之間的權重
            b0(mat):輸入層到隱含層之間的偏置
            w1(mat):隱含層到輸出層之間的權重
            b1(mat):隱含層到輸出層之間的偏置
    output: 預測值
    '''
    return predict_out(predict_in(hidden_out(hidden_in(feature, w0, b0)), w1, b1))

傳入訓練完成的參數,計算測試樣本在輸出層每個神經元的輸出值,選取最大值的神經元作爲分類結果。

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