1.前言
如圖是一個神經網絡的簡化結構,隱藏層每一個節點都是一個神經元,比如下圖的a1,a2,a3。機器學習中的神經網絡是模擬生物神經網絡結構,每個神經元與其他神經元相連,當神經元的電位超過了一個‘閾值’,那麼它就會被激活,即‘興奮’起來。
機器學習的神經網絡是怎麼模擬大腦神經元‘興奮’這個概念的?結合a1這個神經元做簡要的分析:
首先對於a1的定義,我們給出如下的公式..
剖析神經元興奮的定義“當神經元的電位超過了一個‘閾值’,那麼它就會被激活”,對應與上面公式,怎麼使得上訴公式達到神經元的效果?
當達到某一閥值時候,即興奮狀態,反之當未達到閥值時,即未興奮狀態。
所以理想情況下這個函數應該如下圖的紅色,因爲起特性就是大於零被激活,小於零未激活,所以稱之爲激活函數。 :
但是紅色函數存在不連續,不光滑等不太好的性質...所以在實際情況下,我們一般會使用sigmod函數來作爲激活函數(即圖中藍線):
其實我們可以發現,對於神經網絡來說,每一個神經元我們都可以理解成一個邏輯迴歸(LR)模型。這樣去理解它或許對你之後的理解有所幫助。對於邏輯迴歸算法模型的推到及編程可以查看我之前的一篇文章:點擊此處跳轉
2.前向傳播的神經網絡運算
由圖,輸入層的輸入矩陣()可以表示爲: 隱藏層的輸入矩陣()可以表示爲:
輸入層的每個輸入對應每個隱藏層的權重爲,所以權重的矩陣()可以表示爲:
由神經元的興奮啓發可以將上式表示爲:
由上式子可以整理得;
(b爲偏置單元,對應圖1的,x0,a0,在本節介紹中省略)
將矩陣代入上式:
同理,我們對於接下來的隱藏層和輸出層也用同樣的方法,那麼我們最終的輸出層就是一個在0,1之間分佈的值,當其大於0時,即輸入的x屬於1這個類別。當其小於0時,輸入的x屬於0這個類別。
3.梯度下降與代價函數
你可能會因爲不清楚第二節的前向傳播算法中的權重矩陣()是怎麼來的,從而導致你對整個算法的過程有些不知所云。這一章節會爲你解決這個疑惑。
需要明白的是,在設計一個神經網絡算法模型的時候,有些參數是提前人爲定義的。
#參數1:
隱藏層的層數
#參數2:
每層隱藏層的神經元個數
#參數3:
輸出層的神經元個數
#參數4
定義每次訓練的樣本數
#參數5
定義訓練的次數
#參數6
定義學習率
當上訴參數設定後,將在神經網絡中起到怎樣的作用呢?結合下圖來說明一下:
1.在定義了隱藏層的層數,每層神經元的個數,輸出層神經元個數後我們能確定上圖中w,b,v,b_,u,b__的矩陣大小。在神經網絡第一輪訓練時候,上訴矩陣的參數即w11,w12...等的值是人爲定義的一系列滿足正態分佈的隨機值。實質上之後你會發現,無論起先w,b的矩陣值如何,通過代價函數的反覆迭代都能讓其收斂到最低點。
2.定義每次訓練的樣本數即確定了輸入層輸入x的個數。
3.人爲定義不同的訓練的次數,學習率,通過觀察準確率的變化,來選擇最適合神經網絡的參數。
在前言部分爲大家介紹到,一個神經網絡的每個神經元可以看做一個邏輯迴歸算法模型。
邏輯迴歸的代價函數如下:
關於邏輯迴歸代價函數的推到,我簡要說明一下:
1.在二分類問題中,對於每個觀察樣本:
這個公式很好理解,拆分開來可以這樣表示:
2.對於n個出現的樣本,樣本間相互獨立,則n個出現的概率爲每一個出現的概率的乘積。
3.爲了滿足凸函數求最優解的思想,我們對L(w)取對數,並化簡:
4.由sigmod函數可以推到出如下結果:
5.有次推導邏輯迴歸的代價函數:
對於神經網絡來說,我們可以由上一章節的前向傳播的算法得到最後的輸出,之後我們算出這個輸出與真實值的誤差,然後用同樣的方式反向傳播回去迭代修正之前w,b,v,b_,u,b__矩陣的值。
而對於每個隱藏層來說,其每個神經元都可以作爲一個邏輯迴歸單元,這樣神經網絡誤差算法的代價函數就可以做出如下表示:
(k表示神經元的序號)
同理我們可以用邏輯迴歸算法模型的方式來對做梯度下降算法,即找出滿足:
時,的w和b的值。對的求解,可以使用鏈式法則:
對於神經元的求和:
(這個公式可由前向傳播算法矩陣運算中得出)
所以
假設:
那麼:
所以由梯度下降算法可以求得:
(爲人爲設定的學習率。)
4.BP反向傳播算法
通過第四節,我們對於神經網絡更新參數w,b有了一定的認識,而在上節最後,原來的更新w,b的問題轉爲了求解
用一張動態圖表示前向(FP)和後向(BP)傳播的全過程:
對於輸出層來說:
(用均方誤差來定義樣本的y值和通過算法模型求解出來的y值的距離差距)
其中
對於隱藏層來說:
用替換調,用替換掉:
這個式子說明了反向傳播算法的本質,我們先求得輸入層,然後通過不斷後向推到....
通過一輪一輪反覆迭代修正w,b。使得最終的w,b是最能完成分類的參數。
5.編程實現
如果需要更爲詳細的編程實現過程,點下面鏈接:
import numpy as np
import random
import os, struct
from array import array as pyarray
from numpy import append, array, int8, uint8, zeros
class NeuralNet(object):
# 初始化神經網絡,sizes是神經網絡的層數和每層神經元個數
def __init__(self, sizes):
self.sizes_ = sizes
self.num_layers_ = len(sizes) # 層數
self.w_ = [np.random.randn(y, x) for x, y in zip(sizes[:-1], sizes[1:])] # w_、b_初始化爲正態分佈隨機數
self.b_ = [np.random.randn(y, 1) for y in sizes[1:]]
# Sigmoid函數,S型曲線,
def sigmoid(self, z):
return 1.0/(1.0+np.exp(-z))
# Sigmoid函數的導函數
def sigmoid_prime(self, z):
return self.sigmoid(z)*(1-self.sigmoid(z))
def feedforward(self, x):
for b, w in zip(self.b_, self.w_):
x = self.sigmoid(np.dot(w, x)+b)
return x
def backprop(self, x, y):
nabla_b = [np.zeros(b.shape) for b in self.b_]
nabla_w = [np.zeros(w.shape) for w in self.w_]
activation = x
activations = [x]
zs = []
for b, w in zip(self.b_, self.w_):
z = np.dot(w, activation)+b
zs.append(z)
activation = self.sigmoid(z)
activations.append(activation)
delta = self.cost_derivative(activations[-1], y) * \
self.sigmoid_prime(zs[-1])
nabla_b[-1] = delta
nabla_w[-1] = np.dot(delta, activations[-2].transpose())
for l in range(2, self.num_layers_):
z = zs[-l]
sp = self.sigmoid_prime(z)
delta = np.dot(self.w_[-l+1].transpose(), delta) * sp
nabla_b[-l] = delta
nabla_w[-l] = np.dot(delta, activations[-l-1].transpose())
return (nabla_b, nabla_w)
def update_mini_batch(self, mini_batch, eta):
nabla_b = [np.zeros(b.shape) for b in self.b_]
nabla_w = [np.zeros(w.shape) for w in self.w_]
for x, y in mini_batch:
delta_nabla_b, delta_nabla_w = self.backprop(x, y)
nabla_b = [nb+dnb for nb, dnb in zip(nabla_b, delta_nabla_b)]
nabla_w = [nw+dnw for nw, dnw in zip(nabla_w, delta_nabla_w)]
self.w_ = [w-(eta/len(mini_batch))*nw for w, nw in zip(self.w_, nabla_w)]
self.b_ = [b-(eta/len(mini_batch))*nb for b, nb in zip(self.b_, nabla_b)]
# training_data是訓練數據(x, y);epochs是訓練次數;mini_batch_size是每次訓練樣本數;eta是learning rate
def SGD(self, training_data, epochs, mini_batch_size, eta, test_data=None):
if test_data:
n_test = len(test_data)
n = len(training_data)
for j in range(epochs):
random.shuffle(training_data)
mini_batches = [training_data[k:k+mini_batch_size] for k in range(0, n, mini_batch_size)]
for mini_batch in mini_batches:
self.update_mini_batch(mini_batch, eta)
if test_data:
print("Epoch {0}: {1} / {2}".format(j, self.evaluate(test_data), n_test))
else:
print("Epoch {0} complete".format(j))
def evaluate(self, test_data):
test_results = [(np.argmax(self.feedforward(x)), y) for (x, y) in test_data]
return sum(int(x == y) for (x, y) in test_results)
def cost_derivative(self, output_activations, y):
return (output_activations-y)
# 預測
def predict(self, data):
value = self.feedforward(data)
return value.tolist().index(max(value))
# 保存訓練模型
def save(self):
pass # 把_w和_b保存到文件(pickle)
def load(self):
pass
打賞一下作者: