深度學習(3): BP神經網絡推導及實驗

在這裏插入圖片描述

注:轉載請標明原文出處鏈接:https://xiongyiming.blog.csdn.net/article/details/99113544


1 BP神經網絡簡介

BP(back propagation) 神經網絡是1986年由Rumelhart和McClelland爲首的科學家提出的概念,是一種按照誤差逆向傳播算法訓練的多層前饋神經網絡,是目前應用最廣泛的神經網絡。
BP算法(Back Propagation algorithm, 反向傳播算法)適合於多層神經元網絡的一種學習算法,它建立在梯度下降法的基礎上。BP網絡的輸入輸出關係實質上是一種映射關係:一個nn輸入mm輸出的BP神經網絡所完成的功能是從nn維歐氏空間向mm維歐氏空間中一有限域的連續映射,這一映射具有高度非線性。它的信息處理能力來源於簡單非線性函數的多次複合,因此具有很強的函數復現能力。這是BP算法得以應用的基礎。
(以上均來自百度百科)



2 BP神經網絡結構與原理

一般的神經網絡結構圖如下圖所示:

在這裏插入圖片描述

首先 定義符號說明
(1) nl{n_l} : 表示網絡層數,對於上圖中的網絡,L=4L=4n1{n_1} 表示輸入層, n4{n_4} 表示輸出層,其他爲隱層。
(2) bi(l)b_i^{(l)} : 表示第 l+1l+1 層的第 ii 個單元 與 第 ll 層的第 jj 個單元 的連接權重 (從後往前看)
(3) bi(l)b_i^{(l)} : 表示第 ll 層的第 ii 個單元的偏置(激活閾值)
(4) zi(l)z_i^{(l)} : 表示第 ll 層的第 ii 個單元的權重累計(輸入值)
(5) ai(l)a_i^{(l)} : 表示第 ll 層的第 ii 個單元的激活值(輸出值)
(6) hw,b(X){h_{w,b}}(X) : 表示最後輸出層的輸出值
(7) Sl{S_l} : 表示第 ll 層的神經元個數
(8) 樣本個數爲 mm,特徵個數爲 nn
(9) ff : 表示神經元的激活函數


通過上面的定義,對照着網絡結構圖,則

第一層 n1{n_1}ai(1)=xi(1)a_i^{(1)} = {x_i} \tag{1}


第二層 n1{n_1} ,4個神經元:
z1(2)=(j=13(w1,j(1)aj(1)))+b1(1)(2)z_1^{(2)} = \left( {\sum\limits_{j = 1}^3 {(w_{1,j}^{(1)}a_j^{(1)})} } \right) + b_1^{(1)} \tag{2}

a1(2)=f(z1(2))=f(w1,1(1)+w1,2(1)+w1,3(1)+b2(1))(3)a_1^{(2)} = f\left( {z_1^{(2)}} \right) = f\left( {w_{1,1}^{(1)} + w_{1,2}^{(1)} + w_{1,3}^{(1)} + b_2^{(1)}} \right) \tag{3}

a2(2)=f(z2(2))=f(w2,1(1)+w2,2(1)+w2,3(1)+b2(1))(4)a_2^{(2)} = f\left( {z_2^{(2)}} \right) = f\left( {w_{2,1}^{(1)} + w_{2,2}^{(1)} + w_{2,3}^{(1)} + b_2^{(1)}} \right) \tag{4}

a3(2)=f(z3(2))=f(w3,1(1)+w3,2(1)+w3,3(1)+b3(1))(5)a_3^{(2)} = f\left( {z_3^{(2)}} \right) = f\left( {w_{3,1}^{(1)} + w_{3,2}^{(1)} + w_{3,3}^{(1)} + b_3^{(1)}} \right) \tag{5}

a4(2)=f(z4(2))=f(w4,1(1)+w4,2(1)+w4,3(1)+b4(1))(6)a_4^{(2)} = f\left( {z_4^{(2)}} \right) = f\left( {w_{4,1}^{(1)} + w_{4,2}^{(1)} + w_{4,3}^{(1)} + b_4^{(1)}} \right) \tag{6}


第三層 ,4個神經元:
z1(3)=(j=14(w1,j(2)aj(2)))+b1(2)(7)z_1^{(3)} = \left( {\sum\limits_{j = 1}^4 {(w_{1,j}^{(2)}a_j^{(2)})} } \right) + b_1^{(2)} \tag{7}

a1(3)=f(z1(3))=f(w1,1(2)+w1,2(2)+w1,3(2)+w1,4(2)+b2(2))(8)a_1^{(3)} = f\left( {z_1^{(3)}} \right) = f\left( {w_{1,1}^{(2)} + w_{1,2}^{(2)} + w_{1,3}^{(2)} + w_{1,4}^{(2)} + b_2^{(2)}} \right) \tag{8}

a2(3)=f(z2(3))=f(w2,1(2)+w2,2(2)+w2,3(2)+w2,4(2)+b2(2))(9)a_2^{(3)} = f\left( {z_2^{(3)}} \right) = f\left( {w_{2,1}^{(2)} + w_{2,2}^{(2)} + w_{2,3}^{(2)} + w_{2,4}^{(2)} + b_2^{(2)}} \right) \tag{9}

a3(3)=f(z3(3))=f(w3,1(2)+w3,2(2)+w3,3(2)+w3,4(2)+b3(2))(10)a_3^{(3)} = f\left( {z_3^{(3)}} \right) = f\left( {w_{3,1}^{(2)} + w_{3,2}^{(2)} + w_{3,3}^{(2)} + w_{3,4}^{(2)} + b_3^{(2)}} \right) \tag{10}

a4(3)=f(z4(3))=f(w4,1(2)+w4,2(2)+w4,3(2)+w4,4(2)+b4(2))(11)a_4^{(3)} = f\left( {z_4^{(3)}} \right) = f\left( {w_{4,1}^{(2)} + w_{4,2}^{(2)} + w_{4,3}^{(2)} + w_{4,4}^{(2)} + b_4^{(2)}} \right) \tag{11}


第四層 ,2個神經元:
z1(4)=(j=14(w1,j(3)aj(3)))+b1(3)(12)z_1^{(4)} = \left( {\sum\limits_{j = 1}^4 {(w_{1,j}^{(3)}a_j^{(3)})} } \right) + b_1^{(3)} \tag{12}

a1(4)=f(z1(4))=f(w1,1(3)+w1,2(3)+w1,3(3)+w1,4(3)+b2(3))(13)a_1^{(4)} = f\left( {z_1^{(4)}} \right) = f\left( {w_{1,1}^{(3)} + w_{1,2}^{(3)} + w_{1,3}^{(3)} + w_{1,4}^{(3)} + b_2^{(3)}} \right) \tag{13}

a2(4)=f(z2(4))=f(w2,1(3)+w2,2(3)+w2,3(3)+w2,4(3)+b2(3))(14)a_2^{(4)} = f\left( {z_2^{(4)}} \right) = f\left( {w_{2,1}^{(3)} + w_{2,2}^{(3)} + w_{2,3}^{(3)} + w_{2,4}^{(3)} + b_2^{(3)}} \right) \tag{14}

hw,b(X)=(a1(4),a2(4))T(15){h_{w,b}}(X) = {\left( {a_1^{(4)},a_2^{(4)}} \right)^{\rm{T}}} \tag{15}
和機器學習求解類似,對於一個樣本可以使用均方差作爲損失函數(代價函數)
J(w,b;x,y)=hw,b(x)y2(16)J(w,b;x,y) = {\left\| {{h_{w,b}}(x) - y} \right\|^2} \tag{16}

對於所以樣本,損失函數
J(w,b)=[1mi=1mJ(w,b;x(i),y(i))]+λ2l=1nl1i=1Slj=1Sl+1(wi,j(i))2(17)J(w,b) = \left[ {{1 \over m}\sum\limits_{i = 1}^m {J(w,b;{x^{(i)}},{y^{(i)}})} } \right] + {\lambda \over 2}\sum\limits_{l = 1}^{{n_l} - 1} {\sum\limits_{i = 1}^{{S_l}} {\sum\limits_{j = 1}^{{S_{l + 1}}} {{{\left( {w_{i,j}^{(i)}} \right)}^2}} } } \tag{17}

J(w,b)=[1mi=1mhw,b(x(i))y(i)2]+λ2l=1nl1i=1Slj=1Sl+1(wi,j(i))2(18)J(w,b) = \left[ {{1 \over m}\sum\limits_{i = 1}^m {{{\left\| {{h_{w,b}}({x^{(i)}}) - {y^{(i)}}} \right\|}^2}} } \right] + {\lambda \over 2}\sum\limits_{l = 1}^{{n_l} - 1} {\sum\limits_{i = 1}^{{S_l}} {\sum\limits_{j = 1}^{{S_{l + 1}}} {{{\left( {w_{i,j}^{(i)}} \right)}^2}} } } \tag{18}

其中,第一項爲均方差項,第二項爲正則化項(懲罰項)。
可以看出,損失函數是關於所有權重wi,j(l)w_{i,j}^{(l)}和偏置bi(l)b_i^{(l)}的方程,與機器學習求解同樣的思路,需要通過求解損失函數的最小值得到最佳的權重wi,j(l)w_{i,j}^{(l)}和偏置bi(l)b_i^{(l)},可以採用梯度下降法,則求解得到的最佳權重wi,j(l)w_{i,j}^{(l)}和偏置bi(l)b_i^{(l)}分別爲:
wi,j(i)=wi,j(i)αwi,j(i)J(w,b)(19)w_{i,j}^{(i)} = w_{i,j}^{(i)} - \alpha {\partial \over {\partial w_{i,j}^{(i)}}}J\left( {w,b} \right) \tag{19}
bi(l)=bi(l)αbi(l)J(w,b)(20)b_i^{(l)} = b_i^{(l)} - \alpha {\partial \over {\partial b_i^{(l)}}}J\left( {w,b} \right) \tag{20}

雖然求得最佳的權重和偏置的式子看上去很簡單,與機器學習中求解不同的是,上面的式子中的求導非常困難。
那麼問題來了,那該如何求解得到最佳的權重和偏置呢?
此時反向傳播算法 (Back Propagation algorithm) 就是解決這個問題的,它是一種方便求解偏導的方法。可以理解爲是一種從後往前找規律的方法。下面就開始進行推導。



3 BP神經網絡推導

既然是反向傳播,所以從最後一層往前進行推導。

對於第ll層的參數,分別對權重wi,j(l)w_{i,j}^{(l)}和偏置bi(l)b_i^{(l)}求偏導爲: wi,j(i)J(w,b)=[1mi=1mwi,j(i)J(w,b;x(i),y(i))]+λwi,j(i)(21){\partial \over {\partial w_{i,j}^{(i)}}}J(w,b) = \left[ {{1 \over m}\sum\limits_{i = 1}^m {{\partial \over {\partial w_{i,j}^{(i)}}}J(w,b;{x^{(i)}},{y^{(i)}})} } \right] + \lambda w_{i,j}^{(i)} \tag{21}
bi(l)J(w,b)=[1mi=1mbi(l)J(w,b;x(i),y(i))](22){\partial \over {\partial b_i^{(l)}}}J(w,b) = \left[ {{1 \over m}\sum\limits_{i = 1}^m {{\partial \over {\partial b_i^{(l)}}}J(w,b;{x^{(i)}},{y^{(i)}})} } \right] \tag{22}

現在的問題就轉化爲,分別在每個樣本下求解損失函數關於權重wi,j(l)w_{i,j}^{(l)}和偏置bi(l)b_i^{(l)}的偏導,故對於任意一個樣本均有:
J(w,b;x,y)=hw,b(x)y2(23)J(w,b;x,y) = {\left\| {{h_{w,b}}(x) - y} \right\|^2} \tag{23}

hw,b(x){h_{w,b}}(x)是關於上一層的wwbb的函數,那麼從最後一層開始計算,看是否能找到規律,故有 wi,j(nl1)J(w,b)=wi,j(nl1)12anly2(24){\partial \over {\partial w_{i,j}^{({n_l} - 1)}}}J(w,b) = {\partial \over {\partial w_{i,j}^{({n_l} - 1)}}}{1 \over 2}{\left\| {{{\bf{a}}^{{n_l}}} - {\bf{y}}} \right\|^2} \tag{24}

其中,此時的 anl=[a1nl;a2nl]{{\bf{a}}^{{n_l}}} = [a_1^{{n_l}};a_2^{{n_l}}]y=[y1;y2]{\bf{y}} = [{y_1};{y_2}]

wi,j(nl1)J(w,b)=wi,j(nl1)12k=1Snl(ak(nl)yk)2(25){\partial \over {\partial w_{i,j}^{({n_l} - 1)}}}J(w,b) = {\partial \over {\partial w_{i,j}^{({n_l} - 1)}}}{1 \over 2}\sum\limits_{k = 1}^{{S_{{n_l}}}} {{{\left( {a_k^{({n_l})} - {y_k}} \right)}^2}} \tag{25}
wi,j(nl1)J(w,b)=wi,j(nl1)12k=1Snl(f(zk(nl))yk)2(26){\partial \over {\partial w_{i,j}^{({n_l} - 1)}}}J(w,b) = {\partial \over {\partial w_{i,j}^{({n_l} - 1)}}}{1 \over 2}\sum\limits_{k = 1}^{{S_{{n_l}}}} {{{\left( {f\left( {z_k^{({n_l})}} \right) - {y_k}} \right)}^2}} \tag{26}

由公式(27):zk(nl)=(k=1Snlwk,p(nl1)ap(nl1))+bk(nl1)(27)z_k^{({n_l})} = \left( {\sum\limits_{k = 1}^{{S_{{n_l}}}} {w_{k,p}^{({n_l} - 1)}a_p^{({n_l} - 1)}} } \right) + b_k^{({n_l} - 1)} \tag{27}

可知,並且由 鏈式法則 得到 wi,j(nl1)J(w,b)=zk(nl)12k=1Snl(f(zk(nl))yk)2zk(nl)wi,j(nl1)(28){\partial \over {\partial w_{i,j}^{({n_l} - 1)}}}J(w,b) = {\partial \over {\partial z_k^{({n_l})}}}{1 \over 2}\sum\limits_{k = 1}^{{S_{{n_l}}}} {{{\left( {f\left( {z_k^{({n_l})}} \right) - {y_k}} \right)}^2}} {{\partial z_k^{({n_l})}} \over {\partial w_{i,j}^{({n_l} - 1)}}} \tag{28}
求偏導得到

wi,j(nl1)J(w,b)=[f(zk(nl))yi]f(zk(nl))zk(nl)wi,j(nl1)(29){\partial \over {\partial w_{i,j}^{({n_l} - 1)}}}J(w,b) = \left[ {f\left( {z_k^{({n_l})}} \right) - {y_i}} \right] \cdot f'\left( {z_k^{({n_l})}} \right) \cdot {{\partial z_k^{({n_l})}} \over {\partial w_{i,j}^{({n_l} - 1)}}} \tag{29}
wi,j(nl1)J(w,b)=[f(zk(nl))yi]f(zk(nl))aj(nl1)(30){\partial \over {\partial w_{i,j}^{({n_l} - 1)}}}J(w,b) = \left[ {f\left( {z_k^{({n_l})}} \right) - {y_i}} \right] \cdot f'\left( {z_k^{({n_l})}} \right) \cdot a_j^{({n_l} - 1)} \tag{30}
此時,設 δi(nl)=[f(zk(nl))yi]f(zk(nl))(31)\delta _i^{({n_l})} = \left[ {f\left( {z_k^{({n_l})}} \right) - {y_i}} \right] \cdot f'\left( {z_k^{({n_l})}} \right) \tag{31}

對於最後一層,由於函數yi{y_i}和 都一直有函數的映射,則 函數唯一確定了,而這個 稱之爲誤差

下面推導倒數第二層
wi,j(nl2)J(w,b)=wi,j(nl2)12k=1Snl(f(zk(nl))yk)2(32){\partial \over {\partial w_{i,j}^{({n_l} - 2)}}}J(w,b) = {\partial \over {\partial w_{i,j}^{({n_l} - 2)}}}{1 \over 2}\sum\limits_{k = 1}^{{S_{{n_l}}}} {{{\left( {f\left( {z_k^{({n_l})}} \right) - {y_k}} \right)}^2}} \tag{32}
wi,j(nl2)J(w,b)=zk(nl1)12k=1Snl(f(zk(nl))yk)2zk(nl1)wi,j(nl2)(33){\partial \over {\partial w_{i,j}^{({n_l} - 2)}}}J(w,b) = {\partial \over {\partial z_k^{({n_l} - 1)}}}{1 \over 2}\sum\limits_{k = 1}^{{S_{{n_l}}}} {{{\left( {f\left( {z_k^{({n_l})}} \right) - {y_k}} \right)}^2}} {{\partial z_k^{({n_l} - 1)}} \over {\partial w_{i,j}^{({n_l} - 2)}}} \tag{33}
wi,j(nl2)J(w,b)=k=1Snl12zk(nl1)(f(zk(nl))yk)2aj(nl2)(34){\partial \over {\partial w_{i,j}^{({n_l} - 2)}}}J(w,b) = \sum\limits_{k = 1}^{{S_{{n_l}}}} {{1 \over 2}{\partial \over {\partial z_k^{({n_l} - 1)}}}{{\left( {f\left( {z_k^{({n_l})}} \right) - {y_k}} \right)}^2}} \cdot a_j^{({n_l} - 2)} \tag{34}
下面只需要對求 12zk(nl1)(f(zk(nl))yk)2{1 \over 2}{\partial \over {\partial z_k^{({n_l} - 1)}}}{\left( {f\left( {z_k^{({n_l})}} \right) - {y_k}} \right)^2}即可,再次使用 鏈式法則

12zk(nl1)(f(zk(nl))yk)2=12f(zk(nl))(f(zk(nl))yk)2f(zk(nl))zk(nl1)(35){1 \over 2}{\partial \over {\partial z_k^{({n_l} - 1)}}}{\left( {f\left( {z_k^{({n_l})}} \right) - {y_k}} \right)^2} = {1 \over 2}{\partial \over {\partial f\left( {z_k^{({n_l})}} \right)}}{\left( {f\left( {z_k^{({n_l})}} \right) - {y_k}} \right)^2} \cdot {{\partial f\left( {z_k^{({n_l})}} \right)} \over {\partial z_k^{({n_l} - 1)}}} \tag{35}
12zk(nl1)(f(zk(nl))yk)2=(f(zk(nl))yk)f(zk(nl))zk(nl1)(36){1 \over 2}{\partial \over {\partial z_k^{({n_l} - 1)}}}{\left( {f\left( {z_k^{({n_l})}} \right) - {y_k}} \right)^2} = \left( {f\left( {z_k^{({n_l})}} \right) - {y_k}} \right) \cdot {{\partial f\left( {z_k^{({n_l})}} \right)} \over {\partial z_k^{({n_l} - 1)}}} \tag{36}

kk表示當前層數的第kk個神經元,而第kk個神經元 f(zk(nl))f\left( {z_k^{({n_l})}} \right)受到前一層所有的第ii個 影響,則
(f(zk(nl))yk)f(zk(nl))zk(nl1)=(f(zk(nl))yk)f(zk(nl))zk(nl)zk(nl)zi(nl1)(37)\left( {f\left( {z_k^{({n_l})}} \right) - {y_k}} \right) \cdot {{\partial f\left( {z_k^{({n_l})}} \right)} \over {\partial z_k^{({n_l} - 1)}}} = \left( {f\left( {z_k^{({n_l})}} \right) - {y_k}} \right) \cdot {{\partial f\left( {z_k^{({n_l})}} \right)} \over {\partial z_k^{({n_l})}}}{{\partial z_k^{({n_l})}} \over {\partial z_i^{({n_l} - 1)}}} \tag{37}
(f(zk(nl))yk)f(zk(nl))zk(nl1)=(f(zk(nl))yk)f(zk(nl))zk(nl)zi(nl1)(38)\left( {f\left( {z_k^{({n_l})}} \right) - {y_k}} \right) \cdot {{\partial f\left( {z_k^{({n_l})}} \right)} \over {\partial z_k^{({n_l} - 1)}}} = \left( {f\left( {z_k^{({n_l})}} \right) - {y_k}} \right) \cdot f'\left( {z_k^{({n_l})}} \right){{\partial z_k^{({n_l})}} \over {\partial z_i^{({n_l} - 1)}}} \tag{38}
(f(zk(nl))yk)f(zk(nl))zk(nl1)=δk(nl)zk(nl1)[(j=1Snl1wi,j(nl1)f(zj(nl)))+bj(nl1)](39)\left( {f\left( {z_k^{({n_l})}} \right) - {y_k}} \right) \cdot {{\partial f\left( {z_k^{({n_l})}} \right)} \over {\partial z_k^{({n_l} - 1)}}} = \delta _k^{({n_l})} \cdot {\partial \over {\partial z_k^{({n_l} - 1)}}}\left[ {\left( {\sum\limits_{j = 1}^{{S_{{n_l} - 1}}} {w_{i,j}^{({n_l} - 1)}f\left( {z_j^{({n_l})}} \right)} } \right) + b_j^{({n_l} - 1)}} \right] \tag{39}
(f(zk(nl))yk)f(zk(nl))zk(nl1)=δk(nl)wk,i(nl1)f(zi(nl1))(40)\left( {f\left( {z_k^{({n_l})}} \right) - {y_k}} \right) \cdot {{\partial f\left( {z_k^{({n_l})}} \right)} \over {\partial z_k^{({n_l} - 1)}}} = \delta _k^{({n_l})} \cdot w_{k,i}^{({n_l} - 1)} \cdot f'\left( {z_i^{({n_l} - 1)}} \right) \tag{40}

注:對於公式(39)中的 wk,i(nl1)w_{k,i}^{({n_l} - 1)}kk表示第 nl{n_l}層的第kk個神經元,ii表示第 nl1{n_l} - 1層的第ii個神經元。

則對於公式(34)所有的,故
wi,j(nl2)J(w,b)=[k=1Snl(δk(nl)wk,i(nl1)f(zi(nl1)))]aj(nl2)(41){\partial \over {\partial w_{i,j}^{({n_l} - 2)}}}J(w,b) = \left[ {\sum\limits_{k = 1}^{{S_{{n_l}}}} {\left( {\delta _k^{({n_l})} \cdot w_{k,i}^{({n_l} - 1)} \cdot f'\left( {z_i^{({n_l} - 1)}} \right)} \right)} } \right] \cdot a_j^{({n_l} - 2)} \tag{41}

進一步歸納得到(對於隱藏層有規律,可進行歸納):
δil=[k=1Snl+1(δk(nl+1)wk,i(nl))]f(zi(l))(42)\delta _i^l = \left[ {\sum\limits_{k = 1}^{{S_{{n_l} + 1}}} {\left( {\delta _k^{({n_l} + 1)} \cdot w_{k,i}^{({n_l})}} \right)} } \right]f'\left( {z_i^{(l)}} \right) \tag{42}

從而,對於隱藏層,有
wi,j(l)J(w,b;x,y)=aj(l)δi(l+1)(43){\partial \over {\partial w_{i,j}^{(l)}}}J(w,b;x,y) = a_j^{(l)}\delta _i^{(l + 1)} \tag{43}
bi(l)J(w,b)=δi(l+1)(44){\partial \over {\partial b_i^{(l)}}}J(w,b) = \delta _i^{(l + 1)} \tag{44}

最終的梯度下降方程爲:
wi,j(l)=wi,j(l)αaj(l)δi(l+1)(45)w_{i,j}^{(l)} = w_{i,j}^{(l)} - \alpha \cdot a_j^{(l)}\delta _i^{(l + 1)} \tag{45}
bi(l)=bi(l)αδi(l+1)(46)b_i^{(l)} = b_i^{(l)} - \alpha \cdot \delta _i^{(l + 1)} \tag{46}



上面的推導是對BP算法如何一步步進行計算的,可能過於繁瑣。

結合上面的所有推導,BP算法的算法流程 爲:

(1) 從後往前計算,得到每層的激活函數值

(2) 最後一層輸出層( nl{n_l} ),計算誤差 δi(nl)\delta _i^{({n_l})}
δi(nl)=(yiai(nl))f(zi(nl))(47)\delta _i^{({n_l})} = - \left( {{y_i} - a_i^{({n_l})}} \right) \cdot f'\left( {z_i^{({n_l})}} \right) \tag{47}

注:最後一層(輸出層)不同於隱藏層,所以需要單獨寫出來。

(3) 對於隱藏層 l=nl1,nl2,,2l = {n_l} - 1,{n_l} - 2, \ldots ,2,計算誤差 δi(l)\delta _i^{(l)}

δil=[k=1Snl+1(δk(nl+1)wk,i(nl))]f(zi(l))(48)\delta _i^l = \left[ {\sum\limits_{k = 1}^{{S_{{n_l} + 1}}} {\left( {\delta _k^{({n_l} + 1)} \cdot w_{k,i}^{({n_l})}} \right)} } \right]f'\left( {z_i^{(l)}} \right) \tag{48}

(4) 更新 權重 wi,j(l)w_{i,j}^{(l)} 和偏置 bi(l)b_i^{(l)}
wi,j(l)=wi,j(l)αaj(l)δi(l+1)(49)w_{i,j}^{(l)} = w_{i,j}^{(l)} - \alpha \cdot a_j^{(l)}\delta _i^{(l + 1)} \tag{49}
bi(l)=bi(l)αδi(l+1)(50)b_i^{(l)} = b_i^{(l)} - \alpha \cdot \delta _i^{(l + 1)} \tag{50}

若考慮到正則化,則權重的更新方程爲
wi,j(l)=wi,j(l)(1αλ)αaj(l)δi(l+1)(51)w_{i,j}^{(l)} = w_{i,j}^{(l)}(1 - \alpha \lambda ) - \alpha \cdot a_j^{(l)}\delta _i^{(l + 1)} \tag{51}





4 實驗

實驗1——實現簡單的BP神經網絡


代碼流程

輸入(Input):輸入層輸入向量
向前傳播 (Feed Forward)
輸出層誤差(Output Error)
反向傳播誤差(Back propagate Error):
隱藏層誤差 輸出(Output):
輸出損失函數的偏置


代碼示例

import numpy as np
import pprint
pp = pprint.PrettyPrinter(indent=4)

# 定義神經網絡的模型架構 [input, hidden, output]
network_sizes = [3, 4, 2]

# 初始化該神經網絡的參數
sizes = network_sizes
num_layers = len(sizes)
biases = [np.random.randn(h, 1) for h in sizes[1:]]
weights = [np.random.randn(y, x) for x, y in zip(sizes[:-1], sizes[1:])]


def loss_der(network_y, real_y):
    """
    返回損失函數的偏導,損失函數使用 MSE
    L = 1/2(network_y-real_y)^2
    delta_L = network_y-real_y
    """
    return (network_y - real_y)


def sigmoid(z):
    """激活函數使用 sigmoid."""
    return 1.0 / (1.0 + np.exp(-z))


def sigmoid_der(z):
    """sigmoid函數的導數 derivative of sigmoid."""
    return sigmoid(z) * (1 - sigmoid(z))


def backprop(x, y):
    """
    根據損失函數 C通過反向傳播算法返回
    """
    """Return a tuple "(nabla_b, nabla_w)" representing the
    gradient for the cost function C_x.  "nabla_b" and
    "nabla_w" are layer-by-layer lists of numpy arrays, similar
    to "self.biases" and "self.weights"."""

    # 初始化網絡參數的導數 權重w的偏導和偏置b的偏導
    delta_w = [np.zeros(w.shape) for w in weights]
    delta_b = [np.zeros(b.shape) for b in biases]

    # 向前傳播 feed forward
    activation = x  # 把輸入的數據作爲第一次激活值
    activations = [x]  # 存儲網絡的激活值
    zs = []  # 存儲網絡的加權輸入值 (z=wx+b)

    for w, b in zip(weights, biases):
        z = np.dot(w, activation) + b
        activation = sigmoid(z)

        activations.append(activation)
        zs.append(z)

    # 反向傳播 back propagation
    # BP1 計算輸出層誤差
    delta_L = loss_der(activations[-1], y) * sigmoid_der(zs[-1])

    # BP3 損失函數在輸出層關於偏置的偏導
    delta_b[-1] = delta_L
    # BP4 損失函數在輸出層關於權值的偏導
    delta_w[-1] = np.dot(delta_L, activations[-2].transpose())

    delta_l = delta_L
    for l in range(2, num_layers):
        # BP2 計算第l層誤差
        z = zs[-l]
        sp = sigmoid_der(z)
        delta_l = np.dot(weights[-l + 1].transpose(), delta_l) * sp
        # BP3 損失函數在l層關於偏置的偏導
        delta_b[-l] = delta_l
        # BP4 損失函數在l層關於權值的偏導
        delta_w[-l] = np.dot(delta_l, activations[-l - 1].transpose())

    return (delta_w, delta_b)




#####  生成數據 並進行訓練
# 輸入(Input):輸入層輸入向量
# 向前傳播 (Feed Forward)
# 輸出層誤差(Output Error)
# 反向傳播誤差(Back propagate Error):隱藏層誤差
# 輸出(Output):輸出損失函數的偏置

training_x = np.random.rand(3).reshape(3, 1)
training_y = np.array([0, 1]).reshape(2, 1)
print("training data x:\n{},\n training data y:\n{}".format(training_x, training_y))
delta_w, delta_b=backprop(training_x, training_y)

print("delta_w:\n{},\n delta_b:\n{}".format(delta_w, delta_b))

運行結果

在這裏插入圖片描述




實驗2——醫療數據診斷

一般情況下,病人去醫院抽血、進行細胞病變等檢查之後,如下表所示,檢查室開出一張醫療診斷表格, 上面有白細胞、鏈球菌 、血小板等參數。醫生通過檢查這些參數值的大小和變化, 可以頂測並判斷該病人是否帶有某種病原體。
在這裏插入圖片描述
下面我們將以類似的醫療數據作爲背景:首先把醫療檢測 的結果進行數學解釋,通過構建一個3層的人工神經網絡 ANN 模型對提前收集到的醫療數據進行訓練,得到該醫療數據的分類模型,最後利用新的醫療數據,頂測其所屬病理分類。

注:在實際中 使用交叉熵作爲損失函數


(1) 創建數據

代碼示例

from sklearn import linear_model
from sklearn import datasets
import sklearn
import numpy as np
import matplotlib.pyplot as plt

# 創建數據
def generate_data():
    np.random.seed(0)
    X, y = datasets.make_moons(200, noise=0.20)  # 300個數據點,噪聲設定0.3
    return X, y


# 讀取數據並顯示
data,labels=generate_data()  # 讀取數據
plt.scatter(data[:, 0], data[:, 1], s=50, c=labels,cmap=plt.cm.Spectral, edgecolors="#313131")
plt.title("Medical data")
plt.show()

運行結果
在這裏插入圖片描述


(2) 構建網絡模型
使用3層的神經網絡,隱藏層神經節點數量爲3,基本模型如下圖所示

在這裏插入圖片描述

隱藏層需要一個激活函數,激活函數把輸出層轉換爲下一層的輸入層。非線性的激活函數能夠讓我們去處理非線性的問題。常用的非線性激活函數有 Tanh 函數、Si gmoid 函數和 ReLU 函數。我們選擇使用 Tanh 函數,同樣也可以嘗試把 Tanh 函數換成其他函數查看輸出,最後通過 Softmax 層把激活函數的輸出轉換爲概率。

注:在神經網絡中 Softmax 函數常常作用於輸出層,將神經網絡的輸出向量轉換成同分布的概率分佈。

模型加入Softmax 層結構框架如下圖所示

在這裏插入圖片描述


構建網絡及測試結果代碼如下

#!/usr/bin/python
# -*- coding: UTF-8 -*-
from sklearn import linear_model
from sklearn import datasets
import sklearn
import numpy as np
import matplotlib.pyplot as plt



# # 創建數據
# def generate_data():
#     np.random.seed(0)
#     X, y = datasets.make_moons(200, noise=0.20)  # 300個數據點,噪聲設定0.3
#     return X, y


# # 讀取數據並顯示
# data,labels=generate_data()  # 讀取數據
# plt.scatter(data[:, 0], data[:, 1], s=50, c=labels,cmap=plt.cm.Spectral, edgecolors="#313131")
# plt.title("Medical data")
# plt.show()



class Config:
    input_dim = 2  # 輸入的維度
    output_dim = 2  # 輸出的分類數

    epsilon = 0.01  # 梯度下降學習速度
    reg_lambda = 0.01  # 正則化強度


def generate_data():
    np.random.seed(0)
    X, y = datasets.make_moons(200, noise=0.20)  # 300個數據點,噪聲設定0.3
    return X, y


def display_model(model):
    print("W1 {}: \n{}\n".format(model['W1'].shape, model['W1']))
    print("b1 {}: \n{}\n".format(model['b1'].shape, model['b1']))
    print("W2 {}: \n{}\n".format(model['W2'].shape, model['W2']))
    print("b1 {}: \n{}\n".format(model['b2'].shape, model['b2']))


def plot_decision_boundary(pred_func, data, labels):
    '''繪製分類邊界圖'''
    # 設置最大值和最小值並增加0.5的邊界(0.5 padding)
    x_min, x_max = data[:, 0].min() - 0.5, data[:, 0].max() + 0.5
    y_min, y_max = data[:, 1].min() - 0.5, data[:, 1].max() + 0.5
    h = 0.01

    # 生成一個點陣網格,點陣間距離爲h
    xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
                         np.arange(y_min, y_max, h))

    # 預測整個網格當中的函數值
    z = pred_func(np.c_[xx.ravel(), yy.ravel()])
    z = z.reshape(xx.shape)

    # 繪製輪廓和訓練樣本
    plt.contourf(xx, yy, z, cmap=plt.cm.Spectral,alpha=0.2) # 透明度alpha=0.2
    plt.scatter(data[:, 0], data[:, 1], s=40, c=labels, cmap=plt.cm.Spectral)
   # plt.show()


def calculate_loss(model, X, y):
    '''
    損失函數
    '''
    num_examples = len(X)  # 訓練集大小
    W1, b1, W2, b2 = model['W1'], model['b1'], model['W2'], model['b2']
    # 正向傳播計算預測值
    z1 = X.dot(W1) + b1
    a1 = np.tanh(z1)
    z2 = a1.dot(W2) + b2
    exp_scores = np.exp(z2)
    probs = exp_scores / np.sum(exp_scores, axis=1, keepdims=True)
    # 計算損失值
    corect_logprobs = -np.log(probs[range(num_examples), y])
    data_loss = np.sum(corect_logprobs)
    # 對損失值進行歸一化(可以不加)
    data_loss += Config.reg_lambda / 2 * \
        (np.sum(np.square(W1)) + np.sum(np.square(W2)))
    return 1. / num_examples * data_loss


def predict(model, x):
    '''
    預測函數
    '''
    W1, b1, W2, b2 = model['W1'], model['b1'], model['W2'], model['b2']
    # 向前傳播
    z1 = x.dot(W1) + b1
    a1 = np.tanh(z1)
    z2 = a1.dot(W2) + b2
    exp_scores = np.exp(z2)
    probs = exp_scores / np.sum(exp_scores, axis=1, keepdims=True)
    return np.argmax(probs, axis=1)


def ANN_model(X, y, nn_hdim, num_passes=20000, print_loss=False):
    '''
    網絡學習函數,並返回網絡
    - nn_hdim: 隱層的神經元節點(隱層的數目)
    - num_passes: 梯度下降迭代次數
    - print_loss: 是否顯示損失函數值
    '''
    num_examples = len(X)  # 訓練的數據集
    model = {}  # 模型存儲定義

    # 隨機初始化參數
    np.random.seed(0)
    W1 = np.random.randn(Config.input_dim, nn_hdim) / np.sqrt(Config.input_dim)
    b1 = np.zeros((1, nn_hdim))
    W2 = np.random.randn(nn_hdim, Config.output_dim) / np.sqrt(nn_hdim)
    b2 = np.zeros((1, Config.output_dim))
    # display_model({'W1': W1, 'b1': b1, 'W2': W2, 'b2': b2})

    # 批量梯度下降法
    for i in range(0, num_passes + 1):
        # 向前傳播
        z1 = X.dot(W1) + b1  # M_200*2 .* M_2*3 --> M_200*3
        a1 = np.tanh(z1)
        z2 = a1.dot(W2) + b2  # M_200*3 .* M_3*2 --> M_200*2
        exp_scores = np.exp(z2)
        probs = exp_scores / np.sum(exp_scores, axis=1, keepdims=True)

        # 向後傳播
        delta3 = probs  # 得到的預測值
        delta3[range(num_examples), y] -= 1  # 預測值減去實際值
        delta2 = delta3.dot(W2.T) * (1 - np.power(a1, 2))
        dW2 = (a1.T).dot(delta3)  # W2的導數
        db2 = np.sum(delta3, axis=0, keepdims=True)  # b2的導數
        dW1 = np.dot(X.T, delta2)  # W1的導數
        db1 = np.sum(delta2, axis=0)  # b1的導數

        # 添加正則化項
        dW1 += Config.reg_lambda * W1
        dW2 += Config.reg_lambda * W2

        # 根據梯度下降值更新權重
        W1 += -Config.epsilon * dW1
        b1 += -Config.epsilon * db1
        W2 += -Config.epsilon * dW2
        b2 += -Config.epsilon * db2

        # 把新的參數加入模型當中
        model = {'W1': W1, 'b1': b1, 'W2': W2, 'b2': b2}

        if print_loss and i % 1000 == 0:
            print("Loss after iteration %i: %f" %
                  (i, calculate_loss(model, X, y)))

    return model


### 創建數據並進行網絡訓練
data, labels = generate_data()


model = ANN_model(data, labels, 3, print_loss=True)  # 建立三個神經元的隱層
    # print(display_model(model))

plot_decision_boundary(lambda x: predict(model, x), data, labels)
plt.title("Hidden Layer size 3")

運行結果

在這裏插入圖片描述



設置不同數量的隱層神經元,看看對模型有什麼的影響。
隱層神經元個數分別設置爲: 1, 2, 3, 4, 30, 10 。其分類結果如下圖所示:

在這裏插入圖片描述
由上圖可以看出,隨着隱層神經元個數的增加,出現過擬合的概率越大,如隱層神經元個數爲100時。所以並不是隱層神經元個數越多越好。



5 總結

BP神經網絡是一個從後往前計算的思路,BP算法的核心記住下面4個表達式即可:

(1) 從後往前計算,得到每層的激活函數值

(2) 最後一層輸出層( nl{n_l} ),計算誤差 δi(nl)\delta _i^{({n_l})}
δi(nl)=(yiai(nl))f(zi(nl))\delta _i^{({n_l})} = - \left( {{y_i} - a_i^{({n_l})}} \right) \cdot f'\left( {z_i^{({n_l})}} \right)
(3) 對於隱藏層 l=nl1,nl2,,2l = {n_l} - 1,{n_l} - 2, \ldots ,2,計算誤差 δi(l)\delta _i^{(l)}

δil=[k=1Snl+1(δk(nl+1)wk,i(nl))]f(zi(l))\delta _i^l = \left[ {\sum\limits_{k = 1}^{{S_{{n_l} + 1}}} {\left( {\delta _k^{({n_l} + 1)} \cdot w_{k,i}^{({n_l})}} \right)} } \right]f'\left( {z_i^{(l)}} \right)
(4) 更新 權重 wi,j(l)w_{i,j}^{(l)} 和偏置 bi(l)b_i^{(l)}
wi,j(l)=wi,j(l)αaj(l)δi(l+1)w_{i,j}^{(l)} = w_{i,j}^{(l)} - \alpha \cdot a_j^{(l)}\delta _i^{(l + 1)}

bi(l)=bi(l)αδi(l+1)b_i^{(l)} = b_i^{(l)} - \alpha \cdot \delta _i^{(l + 1)}




參考資料

[1] 圖解深度學習
[2] 深度學習原理與實踐
[3] TensorFlow實戰Google深度學習框架(第2版)
[4] https://www.bilibili.com/video/av36982926

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