注:轉載請標明原文出處鏈接:https://xiongyiming.blog.csdn.net/article/details/99113544
1 BP神經網絡簡介
BP(back propagation) 神經網絡是1986年由Rumelhart和McClelland爲首的科學家提出的概念,是一種按照誤差逆向傳播算法訓練的多層前饋神經網絡,是目前應用最廣泛的神經網絡。
BP算法(Back Propagation algorithm, 反向傳播算法)適合於多層神經元網絡的一種學習算法,它建立在梯度下降法的基礎上。BP網絡的輸入輸出關係實質上是一種映射關係:一個n輸入m輸出的BP神經網絡所完成的功能是從n維歐氏空間向m維歐氏空間中一有限域的連續映射,這一映射具有高度非線性。它的信息處理能力來源於簡單非線性函數的多次複合,因此具有很強的函數復現能力。這是BP算法得以應用的基礎。
(以上均來自百度百科)
2 BP神經網絡結構與原理
一般的神經網絡結構圖如下圖所示:
首先 定義符號說明
(1) nl : 表示網絡層數,對於上圖中的網絡,L=4, n1 表示輸入層, n4 表示輸出層,其他爲隱層。
(2) bi(l) : 表示第 l+1 層的第 i 個單元 與 第 l 層的第 j 個單元 的連接權重 (從後往前看)
(3) bi(l) : 表示第 l 層的第 i 個單元的偏置(激活閾值)
(4) zi(l) : 表示第 l 層的第 i 個單元的權重累計(輸入值)
(5) ai(l) : 表示第 l 層的第 i 個單元的激活值(輸出值)
(6) hw,b(X) : 表示最後輸出層的輸出值
(7) Sl : 表示第 l 層的神經元個數
(8) 樣本個數爲 m,特徵個數爲 n
(9) f : 表示神經元的激活函數
通過上面的定義,對照着網絡結構圖,則
第一層 n1:ai(1)=xi(1)
第二層 n1 ,4個神經元:
z1(2)=(j=1∑3(w1,j(1)aj(1)))+b1(1)(2)
a1(2)=f(z1(2))=f(w1,1(1)+w1,2(1)+w1,3(1)+b2(1))(3)
a2(2)=f(z2(2))=f(w2,1(1)+w2,2(1)+w2,3(1)+b2(1))(4)
a3(2)=f(z3(2))=f(w3,1(1)+w3,2(1)+w3,3(1)+b3(1))(5)
a4(2)=f(z4(2))=f(w4,1(1)+w4,2(1)+w4,3(1)+b4(1))(6)
第三層 ,4個神經元:
z1(3)=(j=1∑4(w1,j(2)aj(2)))+b1(2)(7)
a1(3)=f(z1(3))=f(w1,1(2)+w1,2(2)+w1,3(2)+w1,4(2)+b2(2))(8)
a2(3)=f(z2(3))=f(w2,1(2)+w2,2(2)+w2,3(2)+w2,4(2)+b2(2))(9)
a3(3)=f(z3(3))=f(w3,1(2)+w3,2(2)+w3,3(2)+w3,4(2)+b3(2))(10)
a4(3)=f(z4(3))=f(w4,1(2)+w4,2(2)+w4,3(2)+w4,4(2)+b4(2))(11)
第四層 ,2個神經元:
z1(4)=(j=1∑4(w1,j(3)aj(3)))+b1(3)(12)
a1(4)=f(z1(4))=f(w1,1(3)+w1,2(3)+w1,3(3)+w1,4(3)+b2(3))(13)
a2(4)=f(z2(4))=f(w2,1(3)+w2,2(3)+w2,3(3)+w2,4(3)+b2(3))(14)
hw,b(X)=(a1(4),a2(4))T(15)
和機器學習求解類似,對於一個樣本可以使用均方差作爲損失函數(代價函數)
J(w,b;x,y)=∥hw,b(x)−y∥2(16)
對於所以樣本,損失函數爲
J(w,b)=[m1i=1∑mJ(w,b;x(i),y(i))]+2λl=1∑nl−1i=1∑Slj=1∑Sl+1(wi,j(i))2(17)
則J(w,b)=[m1i=1∑m∥∥∥hw,b(x(i))−y(i)∥∥∥2]+2λl=1∑nl−1i=1∑Slj=1∑Sl+1(wi,j(i))2(18)
其中,第一項爲均方差項,第二項爲正則化項(懲罰項)。
可以看出,損失函數是關於所有權重wi,j(l)和偏置bi(l)的方程,與機器學習求解同樣的思路,需要通過求解損失函數的最小值得到最佳的權重wi,j(l)和偏置bi(l),可以採用梯度下降法,則求解得到的最佳權重wi,j(l)和偏置bi(l)分別爲:
wi,j(i)=wi,j(i)−α∂wi,j(i)∂J(w,b)(19)
bi(l)=bi(l)−α∂bi(l)∂J(w,b)(20)
雖然求得最佳的權重和偏置的式子看上去很簡單,與機器學習中求解不同的是,上面的式子中的求導非常困難。
那麼問題來了,那該如何求解得到最佳的權重和偏置呢?
此時反向傳播算法 (Back Propagation algorithm) 就是解決這個問題的,它是一種方便求解偏導的方法。可以理解爲是一種從後往前找規律的方法。下面就開始進行推導。
3 BP神經網絡推導
既然是反向傳播,所以從最後一層往前進行推導。
對於第l層的參數,分別對權重wi,j(l)和偏置bi(l)求偏導爲: ∂wi,j(i)∂J(w,b)=[m1i=1∑m∂wi,j(i)∂J(w,b;x(i),y(i))]+λwi,j(i)(21)
∂bi(l)∂J(w,b)=[m1i=1∑m∂bi(l)∂J(w,b;x(i),y(i))](22)
現在的問題就轉化爲,分別在每個樣本下求解損失函數關於權重wi,j(l)和偏置bi(l)的偏導,故對於任意一個樣本均有:
J(w,b;x,y)=∥hw,b(x)−y∥2(23)
而 hw,b(x)是關於上一層的w和b的函數,那麼從最後一層開始計算,看是否能找到規律,故有 ∂wi,j(nl−1)∂J(w,b)=∂wi,j(nl−1)∂21∥anl−y∥2(24)
其中,此時的 anl=[a1nl;a2nl], y=[y1;y2]
∂wi,j(nl−1)∂J(w,b)=∂wi,j(nl−1)∂21k=1∑Snl(ak(nl)−yk)2(25)
∂wi,j(nl−1)∂J(w,b)=∂wi,j(nl−1)∂21k=1∑Snl(f(zk(nl))−yk)2(26)
由公式(27):zk(nl)=⎝⎛k=1∑Snlwk,p(nl−1)ap(nl−1)⎠⎞+bk(nl−1)(27)
可知,並且由 鏈式法則 得到 ∂wi,j(nl−1)∂J(w,b)=∂zk(nl)∂21k=1∑Snl(f(zk(nl))−yk)2∂wi,j(nl−1)∂zk(nl)(28)
求偏導得到
∂wi,j(nl−1)∂J(w,b)=[f(zk(nl))−yi]⋅f′(zk(nl))⋅∂wi,j(nl−1)∂zk(nl)(29)
∂wi,j(nl−1)∂J(w,b)=[f(zk(nl))−yi]⋅f′(zk(nl))⋅aj(nl−1)(30)
此時,設 δi(nl)=[f(zk(nl))−yi]⋅f′(zk(nl))(31)
對於最後一層,由於函數yi和 都一直有函數的映射,則 函數唯一確定了,而這個 稱之爲誤差 。
下面推導倒數第二層
∂wi,j(nl−2)∂J(w,b)=∂wi,j(nl−2)∂21k=1∑Snl(f(zk(nl))−yk)2(32)
∂wi,j(nl−2)∂J(w,b)=∂zk(nl−1)∂21k=1∑Snl(f(zk(nl))−yk)2∂wi,j(nl−2)∂zk(nl−1)(33)
∂wi,j(nl−2)∂J(w,b)=k=1∑Snl21∂zk(nl−1)∂(f(zk(nl))−yk)2⋅aj(nl−2)(34)
下面只需要對求 21∂zk(nl−1)∂(f(zk(nl))−yk)2即可,再次使用 鏈式法則
21∂zk(nl−1)∂(f(zk(nl))−yk)2=21∂f(zk(nl))∂(f(zk(nl))−yk)2⋅∂zk(nl−1)∂f(zk(nl))(35)
21∂zk(nl−1)∂(f(zk(nl))−yk)2=(f(zk(nl))−yk)⋅∂zk(nl−1)∂f(zk(nl))(36)
k表示當前層數的第k個神經元,而第k個神經元 f(zk(nl))受到前一層所有的第i個 影響,則
(f(zk(nl))−yk)⋅∂zk(nl−1)∂f(zk(nl))=(f(zk(nl))−yk)⋅∂zk(nl)∂f(zk(nl))∂zi(nl−1)∂zk(nl)(37)
(f(zk(nl))−yk)⋅∂zk(nl−1)∂f(zk(nl))=(f(zk(nl))−yk)⋅f′(zk(nl))∂zi(nl−1)∂zk(nl)(38)
(f(zk(nl))−yk)⋅∂zk(nl−1)∂f(zk(nl))=δk(nl)⋅∂zk(nl−1)∂⎣⎡⎝⎛j=1∑Snl−1wi,j(nl−1)f(zj(nl))⎠⎞+bj(nl−1)⎦⎤(39)
(f(zk(nl))−yk)⋅∂zk(nl−1)∂f(zk(nl))=δk(nl)⋅wk,i(nl−1)⋅f′(zi(nl−1))(40)
注:對於公式(39)中的 wk,i(nl−1),k表示第 nl層的第k個神經元,i表示第 nl−1層的第i個神經元。
則對於公式(34)所有的,故
∂wi,j(nl−2)∂J(w,b)=⎣⎡k=1∑Snl(δk(nl)⋅wk,i(nl−1)⋅f′(zi(nl−1)))⎦⎤⋅aj(nl−2)(41)
進一步歸納得到(對於隱藏層有規律,可進行歸納):
δil=⎣⎡k=1∑Snl+1(δk(nl+1)⋅wk,i(nl))⎦⎤f′(zi(l))(42)
從而,對於隱藏層,有
∂wi,j(l)∂J(w,b;x,y)=aj(l)δi(l+1)(43)
∂bi(l)∂J(w,b)=δi(l+1)(44)
最終的梯度下降方程爲:
wi,j(l)=wi,j(l)−α⋅aj(l)δi(l+1)(45)
bi(l)=bi(l)−α⋅δi(l+1)(46)
上面的推導是對BP算法如何一步步進行計算的,可能過於繁瑣。
結合上面的所有推導,BP算法的算法流程 爲:
(1) 從後往前計算,得到每層的激活函數值;
(2) 最後一層輸出層( nl ),計算誤差 δi(nl)
δi(nl)=−(yi−ai(nl))⋅f′(zi(nl))(47)
注:最後一層(輸出層)不同於隱藏層,所以需要單獨寫出來。
(3) 對於隱藏層 l=nl−1,nl−2,…,2,計算誤差 δi(l)
δil=⎣⎡k=1∑Snl+1(δk(nl+1)⋅wk,i(nl))⎦⎤f′(zi(l))(48)
(4) 更新 權重 wi,j(l) 和偏置 bi(l)
wi,j(l)=wi,j(l)−α⋅aj(l)δi(l+1)(49)
bi(l)=bi(l)−α⋅δi(l+1)(50)
若考慮到正則化,則權重的更新方程爲
wi,j(l)=wi,j(l)(1−αλ)−α⋅aj(l)δi(l+1)(51)
4 實驗
實驗1——實現簡單的BP神經網絡
代碼流程
輸入(Input):輸入層輸入向量
向前傳播 (Feed Forward)
輸出層誤差(Output Error)
反向傳播誤差(Back propagate Error):
隱藏層誤差 輸出(Output):
輸出損失函數的偏置
代碼示例
import numpy as np
import pprint
pp = pprint.PrettyPrinter(indent=4)
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"."""
delta_w = [np.zeros(w.shape) for w in weights]
delta_b = [np.zeros(b.shape) for b in biases]
activation = x
activations = [x]
zs = []
for w, b in zip(weights, biases):
z = np.dot(w, activation) + b
activation = sigmoid(z)
activations.append(activation)
zs.append(z)
delta_L = loss_der(activations[-1], y) * sigmoid_der(zs[-1])
delta_b[-1] = delta_L
delta_w[-1] = np.dot(delta_L, activations[-2].transpose())
delta_l = delta_L
for l in range(2, num_layers):
z = zs[-l]
sp = sigmoid_der(z)
delta_l = np.dot(weights[-l + 1].transpose(), delta_l) * sp
delta_b[-l] = delta_l
delta_w[-l] = np.dot(delta_l, activations[-l - 1].transpose())
return (delta_w, delta_b)
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)
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 層結構框架如下圖所示
構建網絡及測試結果代碼如下
from sklearn import linear_model
from sklearn import datasets
import sklearn
import numpy as np
import matplotlib.pyplot as plt
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)
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):
'''繪製分類邊界圖'''
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
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)
plt.scatter(data[:, 0], data[:, 1], s=40, c=labels, cmap=plt.cm.Spectral)
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))
for i in range(0, num_passes + 1):
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)
delta3 = probs
delta3[range(num_examples), y] -= 1
delta2 = delta3.dot(W2.T) * (1 - np.power(a1, 2))
dW2 = (a1.T).dot(delta3)
db2 = np.sum(delta3, axis=0, keepdims=True)
dW1 = np.dot(X.T, delta2)
db1 = np.sum(delta2, axis=0)
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)
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 ),計算誤差 δi(nl)
δi(nl)=−(yi−ai(nl))⋅f′(zi(nl))
(3) 對於隱藏層 l=nl−1,nl−2,…,2,計算誤差 δi(l)
δil=⎣⎡k=1∑Snl+1(δk(nl+1)⋅wk,i(nl))⎦⎤f′(zi(l))
(4) 更新 權重 wi,j(l) 和偏置 bi(l)
wi,j(l)=wi,j(l)−α⋅aj(l)δi(l+1)
bi(l)=bi(l)−α⋅δi(l+1)
參考資料
[1] 圖解深度學習
[2] 深度學習原理與實踐
[3] TensorFlow實戰Google深度學習框架(第2版)
[4] https://www.bilibili.com/video/av36982926