一、神經網路介紹:
神經網路演算法參考人的神經元原理(軸突、樹突、神經核),在很多神經元基礎上構建神經網路模型,每個神經元可看作一個個學習單元。這些神經元採納一定的特徵作為輸入,根據自身的模型得到輸出。
圖1 神經網路構造的例子(符號說明:上標[l]表示與第l層;上標(i)表示第i個例子;下標i表示向量第i項)
圖2 單層神經網路示例
神經元模型是先計算一個線性函式(z=Wx+b),接著再計算一個啟用函式。一般來說,神經元模型的輸出值是a=g(Wx+b),其中g是啟用函式(sigmoid,tanh, ReLU, …)。
二、資料集
假設有一個很大的資料庫,裡面記錄了很多天氣資料,例如,氣溫、溼度、氣壓和降雨率。
問題陳述:
一組訓練資料m_train,下雨標記為(1),不下雨標記為(0)。
一個測試資料組m_test,標記是否下雨。
每一個天氣資料包含x1=氣溫,x2=溼度,x3=氣壓。
機器學習中一個常見的預處理步驟是將資料集居中並標準化,這意味著從每個示例中減去整個numpy陣列的平均值,然後將每個示例除以整個numpy陣列的標準偏差。
通用方法(建立部分演算法)
使用深度學習來建造模型
1. 定義模型構造(例如,資料的輸入特徵)
2. 初始化引數並定義超引數(迭代次數、在神經網路中的L層的層數、隱藏層大小、學習率α)
3. 迭代迴圈(正向傳播(計算電流損耗)、計算成本函式、反向傳播(計算電流損耗)、升級引數(使用背景引數和梯度))
4. 使用訓練引數來預測標籤(初始化)
更深層次的L-層神經網路的初始化更為複雜,因為有更多的權重矩陣和偏置向量。下表展示了不同結構的各種層級。
表1 L層的權重矩陣w、偏置向量b和啟用函式z
表2 示例架構中的神經網路權重矩陣w、偏置向量b和啟用函式z
表2幫助我們為圖1中的示例神經網路架構的矩陣準備了正確的維度。
import numpy as np import matplotlib.pyplot as plt nn_architecture = [ {"layer_size": 4,"activation": "none"}, # input layer {"layer_size": 5,"activation": "relu"}, {"layer_size": 4,"activation": "relu"}, {"layer_size": 3,"activation": "relu"}, {"layer_size": 1,"activation": "sigmoid"} ] def initialize_parameters(nn_architecture, seed = 3): np.random.seed(seed) # python dictionary containingour parameters "W1", "b1", ..., "WL","bL" parameters = {} number_of_layers = len(nn_architecture) for l in range(1,number_of_layers): parameters['W' + str(l)] =np.random.randn( nn_architecture[l]["layer_size"], nn_architecture[l-1]["layer_size"] ) * 0.01 parameters['b' + str(l)] =np.zeros((nn_architecture[l]["layer_size"], 1)) return parameters
程式碼段1 引數初始化
使用小隨機數初始化引數是一種簡單的方法,但同時也保證演算法的起始值足夠好。
記住:
- 不同的初始化工具,例如Zero,Random, He or Xavier,都會導致不同的結果。
- 隨機初始化能夠確保不同的隱藏單元可以學習不同的東西(初始化所有權重為零會導致,所有層次的所有感知機都將學習相同的東西)。
- 不要初始化為太大的值
三、啟用函式
啟用函式的作用是為了增加神經網路的非線性。下例將使用sigmoid and ReLU。
Sigmoid輸出一個介於0和1之間的值,這使得它成為二進位制分類的一個很好的選擇。如果輸出小於0.5,可以將其分類為0;如果輸出大於0.5,可以將其分類為1。
def sigmoid(Z): S = 1 / (1 + np.exp(-Z)) return S def relu(Z): R = np.maximum(0, Z) return R def sigmoid_backward(dA, Z): S = sigmoid(Z) dS = S * (1 - S) return dA * dS def relu_backward(dA, Z): dZ = np.array(dA, copy=True) dZ[Z <= 0] = 0 return dZ
程式碼段2 Sigmoid和ReLU啟用函式,及其衍生物
在程式碼段2中,可以看到啟用函式及其派生的向量化程式設計實現。該程式碼將用於進一步的計算。
四、正向傳播
在正向傳播中,在層l的正向函式中,需要知道該層中的啟用函式是哪一種(sigmoid、tanh、ReLU等)。前一層的輸出值為這一層的輸入值,先計算z,再用選定的啟用函式計算。
圖3 神經網路的正向傳播
線性正向模組(對所有示例進行向量化)計算以下方程式:
方程式1 線性正向函式
def L_model_forward(X, parameters, nn_architecture): forward_cache = {} A = X number_of_layers = len(nn_architecture) for l in range(1, number_of_layers): A_prev = A W = parameters['W' + str(l)] b = parameters['b' + str(l)] activation = nn_architecture[l]["activation"] Z, A = linear_activation_forward(A_prev, W, b, activation) forward_cache['Z' + str(l)] = Z forward_cache['A' + str(l)] = A AL = A return AL, forward_cache def linear_activation_forward(A_prev, W, b, activation): if activation == "sigmoid": Z = linear_forward(A_prev, W, b) A = sigmoid(Z) elif activation == "relu": Z = linear_forward(A_prev, W, b) A = relu(Z) return Z, A def linear_forward(A, W, b): Z = np.dot(W, A) + b return Z
程式碼段3 正向傳播模型
使用“cache”(python字典包含為特定層所計算的a和z值)以在正向傳播至相應的反向傳播期間傳遞變數。它包含用於反向傳播計算導數的有用值。
五、損失函式
為了管程學習過程,需要計算代價函式的值。下面的公式用於計算成本。
方程式2 交叉熵成本
def compute_cost(AL, Y): m = Y.shape[1] # Compute loss from AL and y logprobs = np.multiply(np.log(AL), Y) + np.multiply(1 - Y, np.log(1 - AL)) # cross-entropy cost cost = - np.sum(logprobs) / m cost = np.squeeze(cost) return cost
程式碼段4 代價函式的計算
六、反向傳播
反向傳播用於計算引數的損失函式梯度。該演算法是由微分學中已知的“鏈規則”遞迴使用的。
反向傳播計算中使用的公式:
方程式3 反向傳播計算公式
鏈式法則是計算複合函式導數的公式。複合函式就是函式套函式。
方程式4 鏈規則示例
“鏈規則”在計算損失時十分重要(以方程式5為例)。
方程式5 損失函式(含替換資料)及其相對於第一權重的導數
神經網路模型反向傳播的第一步是計算最後一層損失函式相對於z的導數。方程式6由兩部分組成:方程式2損失函式的導數(關於啟用函式)和啟用函式“sigmoid”關於最後一層Z的導數。
方程式6 從4層對z的損失函式導數
方程式6的結果可用於計算方程式3的導數。
方程式7 損失函式相對於3層的導數
在進一步計算中,使用了與第三層啟用函式有關的損失函式的導數(方程式7)。
方程式8 第三層的導數
方程式7的結果和第三層活化函式“relu”的導數用於計算方程式8的導數(損失函式相對於z的導數)。然後,我們對方程式3進行了計算。
我們對方程9和10做了類似的計算。
方程式9 第二層的導數
方程式10 第一層的導數
七、總體思路
從第一層層對z的損失函式導數有助於計算(L-1)層(上一層)對損失函式的導數。結果將用於計算啟用函式的導數。
圖4 神經網路的反向傳播
def L_model_backward(AL, Y, parameters, forward_cache, nn_architecture): grads = {} number_of_layers =len(nn_architecture) m = AL.shape[1] Y = Y.reshape(AL.shape) # afterthis line, Y is the same shape as AL # Initializing thebackpropagation dAL = - (np.divide(Y, AL) -np.divide(1 - Y, 1 - AL)) dA_prev = dAL for l in reversed(range(1,number_of_layers)): dA_curr = dA_prev activation =nn_architecture[l]["activation"] W_curr = parameters['W' +str(l)] Z_curr = forward_cache['Z' +str(l)] A_prev = forward_cache['A' +str(l-1)] dA_prev, dW_curr, db_curr =linear_activation_backward(dA_curr, Z_curr, A_prev, W_curr, activation) grads["dW" +str(l)] = dW_curr grads["db" +str(l)] = db_curr return grads def linear_activation_backward(dA, Z, A_prev, W, activation): if activation =="relu": dZ = relu_backward(dA, Z) dA_prev, dW, db =linear_backward(dZ, A_prev, W) elif activation =="sigmoid": dZ = sigmoid_backward(dA, Z) dA_prev, dW, db =linear_backward(dZ, A_prev, W) return dA_prev, dW, db def linear_backward(dZ, A_prev, W): m = A_prev.shape[1] dW = np.dot(dZ, A_prev.T) / m db = np.sum(dZ, axis=1,keepdims=True) / m dA_prev = np.dot(W.T, dZ) return dA_prev, dW, db
程式碼段5 反向傳播模組
更新引數
該函式的目標是通過梯度優化來更新模型的引數。
def update_parameters(parameters, grads, learning_rate): L = len(parameters) for l in range(1, L): parameters["W" +str(l)] = parameters["W" + str(l)] - learning_rate *grads["dW" + str(l)] parameters["b" +str(l)] = parameters["b" + str(l)] - learning_rate *grads["db" + str(l)] return parameters
全模型
神經網路模型的完整實現包括在片段中提供的方法。
def L_layer_model(X, Y, nn_architecture, learning_rate = 0.0075,num_iterations = 3000, print_cost=False): np.random.seed(1) # keep track of cost costs = [] # Parameters initialization. parameters =initialize_parameters(nn_architecture) # Loop (gradient descent) for i in range(0,num_iterations): # Forward propagation:[LINEAR -> RELU]*(L-1) -> LINEAR -> SIGMOID. AL, forward_cache =L_model_forward(X, parameters, nn_architecture) # Compute cost. cost = compute_cost(AL, Y) # Backward propagation. grads = L_model_backward(AL,Y, parameters, forward_cache, nn_architecture) # Update parameters. parameters =update_parameters(parameters, grads, learning_rate) # Print the cost every 100training example if print_cost and i % 100 ==0: print("Cost afteriteration %i: %f" %(i, cost)) costs.append(cost) # plot the cost plt.plot(np.squeeze(costs)) plt.ylabel('cost') plt.xlabel('iterations (pertens)') plt.title("Learning rate=" + str(learning_rate)) plt.show() return parameters
程式碼段7 整個神經網路模型
只需要將已知的權重和系列測試資料,應用於正向傳播模型,就能預測結果。
可以修改snippet1中的nn_架構,以構建具有不同層數和隱藏層大小的神經網路。此外,準備正確實現啟用函式及其派生函式(程式碼段2)。所實現的函式可用於修改程式碼段3中的線性正向啟用方法和程式碼段5中的線性反向啟用方法。
進一步改進
如果訓練資料集不夠大,則可能面臨“過度擬合”問題。這意味著所學的網路不會概括為它從未見過的新例子。可以使用正則化方法,如L2規範化(它包括適當地修改成本函式)或退出(它在每次迭代中隨機關閉一些感知機)。
我們使用梯度下降來更新引數和最小化成本。你可以學習更多高階優化方法,這些方法可以加快學習速度,甚至可以為成本函式提供更好的最終價值,例如:
-
- 小批量梯度下降
- 動力
- Adam優化器
參考:http://www.uml.org.cn/ai/201911251.asp