參考:https://blog.csdn.net/u013733326/article/details/79702148
希望大家直接到上面的網址去查看代碼,下面是本人的筆記
建立一個帶有隱藏層的神經網絡
導入一些軟件包
numpy:是用Python進行科學計算的基本軟件包。
sklearn:為數據挖掘和數據分析提供的簡單高效的工具。
matplotlib :是一個用於在Python中繪制圖表的庫。
testCases:提供了一些測試示例來評估函數的正確性,參見下載的資料或者在底部查看它的代碼。
planar_utils :提供了在這個任務中使用的各種有用的功能,參見下載的資料或者在底部查看它的代碼。
1.導入數據:
numpy.linspace(start, stop[, num=50[, endpoint=True[, retstep=False[, dtype=None]]]]])
返回在指定范圍內的均勻間隔的數字(組成的數組),也即返回一個等差數列,參數說明:
- start - 起始點
- stop - 結束點
- num - 元素個數,默認為50,
- endpoint - 是否包含stop數值,默認為True,包含stop值;若為False,則不包含stop值
- retstep - 返回值形式,默認為False,返回等差數列組,若為True,則返回結果(array([`samples`]),`step`),
- dtype - 返回結果的數據類型,默認無,若無,則參考輸入數據類型。
舉例說明:
c = np.linspace(1,15,5,endpoint= False,retstep = False) print(c) #[ 1. 3.8 6.6 9.4 12.2] d = np.linspace(1,15,5,retstep = True) print(d) #(array([ 1. , 4.5, 8. , 11.5, 15. ]), 3.5)
代碼:
import numpy as np import matplotlib.pyplot as plt import sklearn import sklearn.datasets import sklearn.linear_model #要使用的數據集, 下面的代碼會將一個花的圖案的2類數據集加載到變量X和Y中。 def load_planar_dataset(): np.random.seed(1) m = 400 # number of examples N = int(m/2) # number of points per class D = 2 # dimensionality X = np.zeros((m,D)) # m=400個點,每個點使用(x,y)表示,所以D=2,因此生成一個400*2的數據矩陣 Y = np.zeros((m,1), dtype='uint8') # 標簽向量,表明每個點的類型,0為紅色,1位藍色 a = 4 # maximum ray of the flower for j in range(2): ix = range(N*j,N*(j+1)) #(0,200)和(200,400) t = np.linspace(j*3.12,(j+1)*3.12,N) + np.random.randn(N)*0.2 # theta r = a*np.sin(4*t) + np.random.randn(N)*0.2 # radius X[ix] = np.c_[r*np.sin(t), r*np.cos(t)] Y[ix] = j X = X.T Y = Y.T return X, Y X, Y = load_planar_dataset() plt.scatter(X[0, :], X[1, :], c=np.squeeze(Y), s=40, cmap=plt.cm.Spectral)#繪制散點圖
返回:
該神經網絡的目標是建立一個模型來適應這些數據,將藍色的點和紅色的點分隔開來
- X:一個numpy的矩陣,包含了這些數據點的數值
- Y:一個numpy的向量,對應着的是X的標簽【0 | 1】(紅色:0 , 藍色 :1)
2.如果試着使用logistic回歸來進行分類
在構建完整的神經網絡之前,先讓我們看看邏輯回歸在這個問題上的表現如何,我們可以使用sklearn的內置函數來做到這一點, 運行下面的代碼來訓練數據集上的邏輯回歸分類器。
clf = sklearn.linear_model.LogisticRegressionCV()
clf.fit(X.T,Y.T)
會有警告:
/anaconda3/lib/python3.7/site-packages/sklearn/utils/validation.py:761: DataConversionWarning: A column-vector y was passed when a 1d array was expected. Please change the shape of y to (n_samples, ), for example using ravel(). y = column_or_1d(y, warn=True) /anaconda3/lib/python3.7/site-packages/sklearn/model_selection/_split.py:2053: FutureWarning: You should specify a value for 'cv' instead of relying on the default value. The default value will change from 3 to 5 in version 0.22. warnings.warn(CV_WARNING, FutureWarning)
返回:
LogisticRegressionCV(Cs=10, class_weight=None, cv='warn', dual=False, fit_intercept=True, intercept_scaling=1.0, max_iter=100, multi_class='warn', n_jobs=None, penalty='l2', random_state=None, refit=True, scoring=None, solver='lbfgs', tol=0.0001, verbose=0)
然后把邏輯回歸分類器的分類繪制出來:
def plot_decision_boundary(model, X, y): #用於繪制logistic的線形分類線 # Set min and max values and give it some padding x_min, x_max = X[0, :].min() - 1, X[0, :].max() + 1 y_min, y_max = X[1, :].min() - 1, X[1, :].max() + 1 h = 0.01 # Generate a grid of points with distance h between them xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h)) # Predict the function value for the whole grid Z = model(np.c_[xx.ravel(), yy.ravel()]) Z = Z.reshape(xx.shape) # Plot the contour and training examples plt.contourf(xx, yy, Z, cmap=plt.cm.Spectral) plt.ylabel('x2') plt.xlabel('x1') plt.scatter(X[0, :], X[1, :], c=np.squeeze(y), cmap=plt.cm.Spectral) plot_decision_boundary(lambda x: clf.predict(x), X, Y) #繪制決策邊界 plt.title("Logistic Regression") #圖標題 LR_predictions = clf.predict(X.T) #預測結果 print ("邏輯回歸的准確性: %d " % float((np.dot(Y, LR_predictions) + np.dot(1 - Y,1 - LR_predictions)) / float(Y.size) * 100) + "% " + "(正確標記的數據點所占的百分比)")
返回:
邏輯回歸的准確性: 47 % (正確標記的數據點所占的百分比)
圖示為:
准確性只有47%的原因是數據集不是線性可分的,所以邏輯回歸表現不佳,現在我們正式開始構建帶有一層隱藏層的神經網絡。
3.定義神經網絡結構
隱藏層的神經元設置為4
def layer_sizes(X , Y): """ 參數: X - 輸入數據集,維度為(輸入的數量,訓練/測試的數量) Y - 標簽,維度為(輸出的數量,訓練/測試數量) 返回: n_x - 輸入層的數量 n_h - 隱藏層的數量 n_y - 輸出層的數量 """ n_x = X.shape[0] #輸入層 n_h = 4 #,隱藏層,硬編碼為4 n_y = Y.shape[0] #輸出層 return (n_x,n_h,n_y) #測試layer_sizes print("=========================測試layer_sizes=========================") def layer_sizes_test_case(): np.random.seed(1) X_assess = np.random.randn(5, 3) #5個輸入,訓練/測試的數量為3 Y_assess = np.random.randn(2, 3) #2個輸出,訓練/測試的數量為3 return X_assess, Y_assess X_asses , Y_asses = layer_sizes_test_case() (n_x,n_h,n_y) = layer_sizes(X_asses,Y_asses) print("輸入層的節點數量為: n_x = " + str(n_x)) print("隱藏層的節點數量為: n_h = " + str(n_h)) print("輸出層的節點數量為: n_y = " + str(n_y))
返回:
=========================測試layer_sizes========================= 輸入層的節點數量為: n_x = 5 隱藏層的節點數量為: n_h = 4 輸出層的節點數量為: n_y = 2
4.初始化模型參數
進行隨機初始化權重矩陣:
np.random.randn(a,b)* 0.01
來隨機初始化一個維度為(a,b)的矩陣。
將b初始化為零
np.random.seed(2)的作用是用於保證你得到的隨機參數和作者的一樣
def initialize_parameters( n_x , n_h ,n_y): """ 參數: n_x - 輸入層節點的數量 n_h - 隱藏層節點的數量 n_y - 輸出層節點的數量 返回: parameters - 包含參數的字典: W1 - 權重矩陣,維度為(n_h,n_x) b1 - 偏向量,維度為(n_h,1) W2 - 權重矩陣,維度為(n_y,n_h) b2 - 偏向量,維度為(n_y,1) """ np.random.seed(2) #指定一個隨機種子,以便你的輸出與我們的一樣。 W1 = np.random.randn(n_h,n_x) * 0.01 b1 = np.zeros(shape=(n_h, 1)) W2 = np.random.randn(n_y,n_h) * 0.01 b2 = np.zeros(shape=(n_y, 1)) #使用斷言確保我的數據格式是正確的 assert(W1.shape == ( n_h , n_x )) assert(b1.shape == ( n_h , 1 )) assert(W2.shape == ( n_y , n_h )) assert(b2.shape == ( n_y , 1 )) parameters = {"W1" : W1, "b1" : b1, "W2" : W2, "b2" : b2 } return parameters #測試initialize_parameters print("=========================測試initialize_parameters=========================") def initialize_parameters_test_case(): n_x, n_h, n_y = 2, 4, 1 return n_x, n_h, n_y n_x , n_h , n_y = initialize_parameters_test_case() parameters = initialize_parameters(n_x , n_h , n_y) print("W1 = " + str(parameters["W1"])) print("b1 = " + str(parameters["b1"])) print("W2 = " + str(parameters["W2"])) print("b2 = " + str(parameters["b2"]))
返回:
=========================測試initialize_parameters========================= W1 = [[-0.00416758 -0.00056267] [-0.02136196 0.01640271] [-0.01793436 -0.00841747] [ 0.00502881 -0.01245288]] b1 = [[0.] [0.] [0.] [0.]] W2 = [[-0.01057952 -0.00909008 0.00551454 0.02292208]] b2 = [[0.]]
5.前向傳播
步驟如下:
- 使用字典類型的parameters(它是initialize_parameters() 的輸出)檢索每個參數。
- 實現向前傳播, 計算Z[1],A[1],Z[2]和 A[2](訓練集里面所有例子的預測向量)。
- 反向傳播所需的值存儲在“cache”中,cache將作為反向傳播函數的輸入。
隱藏層使用np.tanh()函數,輸出層使用sigmoid()函數用於二分類,得到是紅點還是藍點
np.random.seed(1)的作用是用於保證后面操作中隨機生成的X,Y都相等
def forward_propagation( X , parameters ): """ 參數: X - 維度為(n_x,m)的輸入數據。 parameters - 初始化函數(initialize_parameters)的輸出 返回: A2 - 使用sigmoid()函數計算的第二次激活后的數值 cache - 包含“Z1”,“A1”,“Z2”和“A2”的字典類型變量 """ W1 = parameters["W1"] b1 = parameters["b1"] W2 = parameters["W2"] b2 = parameters["b2"] #前向傳播計算A2 Z1 = np.dot(W1 , X) + b1 A1 = np.tanh(Z1) Z2 = np.dot(W2 , A1) + b2 A2 = sigmoid(Z2) #使用斷言確保我的數據格式是正確的 assert(A2.shape == (1,X.shape[1])) cache = {"Z1": Z1, "A1": A1, "Z2": Z2, "A2": A2} return (A2, cache) #測試forward_propagation print("=========================測試forward_propagation=========================") def forward_propagation_test_case(): np.random.seed(1) X_assess = np.random.randn(2, 3) #兩個輸入,訓練/測試數據有3個 parameters = {'W1': np.array([[-0.00416758, -0.00056267], #將之前測試初始化參數時得到的值作為參數 [-0.02136196, 0.01640271], [-0.01793436, -0.00841747], [ 0.00502881, -0.01245288]]), 'W2': np.array([[-0.01057952, -0.00909008, 0.00551454, 0.02292208]]), 'b1': np.array([[ 0.], [ 0.], [ 0.], [ 0.]]), 'b2': np.array([[ 0.]])} return X_assess, parameters X_assess, parameters = forward_propagation_test_case() A2, cache = forward_propagation(X_assess, parameters) print(A2) print(cache) print(np.mean(cache["Z1"]), np.mean(cache["A1"]), np.mean(cache["Z2"]), np.mean(cache["A2"]))
返回:
=========================測試forward_propagation========================= [[0.5002307 0.49985831 0.50023963]] {'Z1': array([[-0.00616586, 0.0020626 , 0.0034962 ], [-0.05229879, 0.02726335, -0.02646869], [-0.02009991, 0.00368692, 0.02884556], [ 0.02153007, -0.01385322, 0.02600471]]), 'A1': array([[-0.00616578, 0.0020626 , 0.00349619], [-0.05225116, 0.02725659, -0.02646251], [-0.02009721, 0.0036869 , 0.02883756], [ 0.02152675, -0.01385234, 0.02599885]]), 'Z2': array([[ 0.00092281, -0.00056678, 0.00095853]]), 'A2': array([[0.5002307 , 0.49985831, 0.50023963]])} -0.0004997557777419902 -0.000496963353231779 0.00043818745095914653 0.500109546852431
6.計算成本
def compute_cost(A2,Y): """ 計算方程(6)中給出的交叉熵成本, 參數: A2 - 使用sigmoid()函數計算的第二次激活后的數值 Y - "True"標簽向量,維度為(1,數量) 返回: 成本 - 交叉熵成本給出方程(13) """ m = Y.shape[1] #計算成本,向量化 logprobs = np.multiply(np.log(A2), Y) + np.multiply((1 - Y), np.log(1 - A2)) cost = - np.sum(logprobs) / m cost = float(np.squeeze(cost)) assert(isinstance(cost,float)) return cost #測試compute_cost print("=========================測試compute_cost=========================") def compute_cost_test_case(): np.random.seed(1) Y_assess = np.random.randn(1, 3) #隨機生成與上面相同的輸出Y的值,與真正的輸出A2對比得到成本 a2 = (np.array([[ 0.5002307 , 0.49985831, 0.50023963]])) #這個是上面運行前向傳播時得到的A2的值 return a2, Y_assess A2 , Y_assess = compute_cost_test_case() print("cost = " + str(compute_cost(A2,Y_assess)))
返回:
=========================測試compute_cost========================= cost = 0.6929198937761266
使用正向傳播期間計算的cache,現在可以利用它實現反向傳播。
成本計算只計算最后一層的輸出,所以使用的是A2
7.后向傳播
梯度下降公式:
代碼:
def backward_propagation(parameters,cache,X,Y): """ 使用上述說明搭建反向傳播函數。 參數: parameters - 包含我們的參數的一個字典類型的變量。 cache - 包含“Z1”,“A1”,“Z2”和“A2”的字典類型的變量。 X - 輸入數據,維度為(2,數量) Y - “True”標簽,維度為(1,數量) 返回: grads - 包含W和b的導數一個字典類型的變量。 """ m = X.shape[1] W1 = parameters["W1"] W2 = parameters["W2"] A1 = cache["A1"] A2 = cache["A2"] dZ2= A2 - Y dW2 = (1 / m) * np.dot(dZ2, A1.T) db2 = (1 / m) * np.sum(dZ2, axis=1, keepdims=True) dZ1 = np.multiply(np.dot(W2.T, dZ2), 1 - np.power(A1, 2)) dW1 = (1 / m) * np.dot(dZ1, X.T) db1 = (1 / m) * np.sum(dZ1, axis=1, keepdims=True) grads = {"dW1": dW1, "db1": db1, "dW2": dW2, "db2": db2 } return grads #測試backward_propagation print("=========================測試backward_propagation=========================") def backward_propagation_test_case(): np.random.seed(1) X_assess = np.random.randn(2, 3) #隨機生成與上面相同的輸入X Y_assess = np.random.randn(1, 3) #隨機生成與上面相同的輸出Y parameters = {'W1': np.array([[-0.00416758, -0.00056267], #將之前測試初始化參數時得到的值作為參數 [-0.02136196, 0.01640271], [-0.01793436, -0.00841747], [ 0.00502881, -0.01245288]]), 'W2': np.array([[-0.01057952, -0.00909008, 0.00551454, 0.02292208]]), 'b1': np.array([[ 0.], [ 0.], [ 0.], [ 0.]]), 'b2': np.array([[ 0.]])} cache = {'A1': np.array([[-0.00616578, 0.0020626 , 0.00349619], #之前前向傳播時存儲在cache中的值 [-0.05225116, 0.02725659, -0.02646251], [-0.02009721, 0.0036869 , 0.02883756], [ 0.02152675, -0.01385234, 0.02599885]]), 'A2': np.array([[ 0.5002307 , 0.49985831, 0.50023963]]), 'Z1': np.array([[-0.00616586, 0.0020626 , 0.0034962 ], [-0.05229879, 0.02726335, -0.02646869], [-0.02009991, 0.00368692, 0.02884556], [ 0.02153007, -0.01385322, 0.02600471]]), 'Z2': np.array([[ 0.00092281, -0.00056678, 0.00095853]])} return parameters, cache, X_assess, Y_assess parameters, cache, X_assess, Y_assess = backward_propagation_test_case() grads = backward_propagation(parameters, cache, X_assess, Y_assess) print ("dW1 = "+ str(grads["dW1"])) print ("db1 = "+ str(grads["db1"])) print ("dW2 = "+ str(grads["dW2"])) print ("db2 = "+ str(grads["db2"]))
返回:
=========================測試backward_propagation========================= dW1 = [[ 0.01018708 -0.00708701] [ 0.00873447 -0.0060768 ] [-0.00530847 0.00369379] [-0.02206365 0.01535126]] db1 = [[-0.00069728] [-0.00060606] [ 0.000364 ] [ 0.00151207]] dW2 = [[ 0.00363613 0.03153604 0.01162914 -0.01318316]] db2 = [[0.06589489]]
反向傳播完成了,我們開始對參數進行更新
8.更新參數w,b
我們需要使用(dW1, db1, dW2, db2)來更新(W1, b1, W2, b2)
更新算法如下:
θ=θ−α(∂J/∂θ)
- α:學習速率
- θ :參數
def update_parameters(parameters,grads,learning_rate=1.2): """ 使用上面給出的梯度下降更新規則更新參數 參數: parameters - 包含參數的字典類型的變量。 grads - 包含導數值的字典類型的變量。 learning_rate - 學習速率 返回: parameters - 包含更新參數的字典類型的變量。 """ W1,W2 = parameters["W1"],parameters["W2"] b1,b2 = parameters["b1"],parameters["b2"] dW1,dW2 = grads["dW1"],grads["dW2"] db1,db2 = grads["db1"],grads["db2"] W1 = W1 - learning_rate * dW1 b1 = b1 - learning_rate * db1 W2 = W2 - learning_rate * dW2 b2 = b2 - learning_rate * db2 parameters = {"W1": W1, "b1": b1, "W2": W2, "b2": b2} return parameters #測試update_parameters print("=========================測試update_parameters=========================") def update_parameters_test_case(): parameters = {'W1': np.array([[-0.00615039, 0.0169021 ], #將之前測試初始化參數時得到的值作為參數 [-0.02311792, 0.03137121], [-0.0169217 , -0.01752545], [ 0.00935436, -0.05018221]]), 'W2': np.array([[-0.0104319 , -0.04019007, 0.01607211, 0.04440255]]), 'b1': np.array([[ -8.97523455e-07], [ 8.15562092e-06], [ 6.04810633e-07], [ -2.54560700e-06]]), 'b2': np.array([[ 9.14954378e-05]])} grads = {'dW1': np.array([[ 0.00023322, -0.00205423], [ 0.00082222, -0.00700776], [-0.00031831, 0.0028636 ], [-0.00092857, 0.00809933]]), 'dW2': np.array([[ -1.75740039e-05, 3.70231337e-03, -1.25683095e-03, -2.55715317e-03]]), 'db1': np.array([[ 1.05570087e-07], [ -3.81814487e-06], [ -1.90155145e-07], [ 5.46467802e-07]]), 'db2': np.array([[ -1.08923140e-05]])} return parameters, grads parameters, grads = update_parameters_test_case() parameters = update_parameters(parameters, grads) print("W1 = " + str(parameters["W1"])) print("b1 = " + str(parameters["b1"])) print("W2 = " + str(parameters["W2"])) print("b2 = " + str(parameters["b2"]))
9.整合上面的所有函數
使神經網絡模型以正確的順序使用先前的函數功能,整合在nn_model()函數中
def nn_model(X,Y,n_h,num_iterations,print_cost=False): """ 參數: X - 數據集,維度為(2,示例數) Y - 標簽,維度為(1,示例數) n_h - 隱藏層的數量 num_iterations - 梯度下降循環中的迭代次數 print_cost - 如果為True,則每1000次迭代打印一次成本數值 返回: parameters - 模型學習的參數,它們可以用來進行預測。 """ np.random.seed(3) #指定隨機種子 n_x = layer_sizes(X, Y)[0] n_y = layer_sizes(X, Y)[2] parameters = initialize_parameters(n_x,n_h,n_y) W1 = parameters["W1"] b1 = parameters["b1"] W2 = parameters["W2"] b2 = parameters["b2"] for i in range(num_iterations): #多次迭代,更新w,b,求出相應的成本函數 A2 , cache = forward_propagation(X,parameters) #先前向傳播,得到結果A2以及將相應的數據存儲到cache中 cost = compute_cost(A2,Y,parameters) #根據得到的結果A2和真正的結果Y去計算成本 grads = backward_propagation(parameters,cache,X,Y) #然后計算相應參數的梯度 parameters = update_parameters(parameters,grads,learning_rate = 0.5) #根據梯度去更新參數 if print_cost: if i%1000 == 0: #1000次迭代后輸出一次成本查看成本的變化 print("第 ",i," 次循環,成本為:"+str(cost)) return parameters
10.預測
使用上面訓練好的參數w,b對測試集進行預測
輸出y大於0.5則為藍色,小於0.5則為紅色
def predict(parameters,X): """ 使用學習的參數,為X中的每個示例預測一個類 參數: parameters - 包含參數的字典類型的變量,這是訓練好的參數 X - 輸入數據(n_x,m) 返回 predictions - 我們模型預測的向量(紅色:0 /藍色:1) """ #使用訓練好的參數去前向傳播獲取預測值 A2 , cache = forward_propagation(X,parameters) predictions = np.round(A2) return predictions
11.整個代碼運行
如果直接將整個函數放入jupyter notebook中總會報錯:
ValueError: 'c' argument has 1 elements, which is not acceptable for use with 'x' with size 400, 'y' with size 400.
即使把planar_utils.py文件中的plot_decision_boundary()函數中的語句改成:
plt.scatter(X[0, :], X[1, :], c=np.squeeze(y), cmap=plt.cm.Spectral)
也不行,后面拆開導入就成功了:
import numpy as np import matplotlib.pyplot as plt from testCases import * import sklearn import sklearn.datasets import sklearn.linear_model def plot_decision_boundary(model, X, y): #用於繪制logistic的線形分類線 # Set min and max values and give it some padding x_min, x_max = X[0, :].min() - 1, X[0, :].max() + 1 y_min, y_max = X[1, :].min() - 1, X[1, :].max() + 1 h = 0.01 # Generate a grid of points with distance h between them xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h)) # Predict the function value for the whole grid Z = model(np.c_[xx.ravel(), yy.ravel()]) Z = Z.reshape(xx.shape) # Plot the contour and training examples plt.contourf(xx, yy, Z, cmap=plt.cm.Spectral) plt.ylabel('x2') plt.xlabel('x1') plt.scatter(X[0, :], X[1, :], c=np.squeeze(y), cmap=plt.cm.Spectral)
from planar_utils import sigmoid, load_planar_dataset, load_extra_datasets np.random.seed(1) #設置一個固定的隨機種子,以保證接下來的步驟中我們的結果是一致的。 X, Y = load_planar_dataset() #plt.scatter(X[0, :], X[1, :], c=Y, s=40, cmap=plt.cm.Spectral) #繪制散點圖 shape_X = X.shape shape_Y = Y.shape m = Y.shape[1] # 訓練集里面的數量 print ("X的維度為: " + str(shape_X)) print ("Y的維度為: " + str(shape_Y)) print ("數據集里面的數據有:" + str(m) + " 個") def layer_sizes(X , Y): """ 參數: X - 輸入數據集,維度為(輸入的數量,訓練/測試的數量) Y - 標簽,維度為(輸出的數量,訓練/測試數量) 返回: n_x - 輸入層的數量 n_h - 隱藏層的數量 n_y - 輸出層的數量 """ n_x = X.shape[0] #輸入層 n_h = 4 #,隱藏層,硬編碼為4 n_y = Y.shape[0] #輸出層 return (n_x,n_h,n_y) def initialize_parameters( n_x , n_h ,n_y): """ 參數: n_x - 輸入節點的數量 n_h - 隱藏層節點的數量 n_y - 輸出層節點的數量 返回: parameters - 包含參數的字典: W1 - 權重矩陣,維度為(n_h,n_x) b1 - 偏向量,維度為(n_h,1) W2 - 權重矩陣,維度為(n_y,n_h) b2 - 偏向量,維度為(n_y,1) """ np.random.seed(2) #指定一個隨機種子,以便你的輸出與我們的一樣。 W1 = np.random.randn(n_h,n_x) * 0.01 b1 = np.zeros(shape=(n_h, 1)) W2 = np.random.randn(n_y,n_h) * 0.01 b2 = np.zeros(shape=(n_y, 1)) #使用斷言確保我的數據格式是正確的 assert(W1.shape == ( n_h , n_x )) assert(b1.shape == ( n_h , 1 )) assert(W2.shape == ( n_y , n_h )) assert(b2.shape == ( n_y , 1 )) parameters = {"W1" : W1, "b1" : b1, "W2" : W2, "b2" : b2 } return parameters def forward_propagation( X , parameters ): """ 參數: X - 維度為(n_x,m)的輸入數據。 parameters - 初始化函數(initialize_parameters)的輸出 返回: A2 - 使用sigmoid()函數計算的第二次激活后的數值 cache - 包含“Z1”,“A1”,“Z2”和“A2”的字典類型變量 """ W1 = parameters["W1"] b1 = parameters["b1"] W2 = parameters["W2"] b2 = parameters["b2"] #前向傳播計算A2 Z1 = np.dot(W1 , X) + b1 A1 = np.tanh(Z1) Z2 = np.dot(W2 , A1) + b2 A2 = sigmoid(Z2) #使用斷言確保我的數據格式是正確的 assert(A2.shape == (1,X.shape[1])) cache = {"Z1": Z1, "A1": A1, "Z2": Z2, "A2": A2} return (A2, cache) def compute_cost(A2,Y,parameters): """ 計算方程(6)中給出的交叉熵成本, 參數: A2 - 使用sigmoid()函數計算的第二次激活后的數值 Y - "True"標簽向量,維度為(1,數量) parameters - 一個包含W1,B1,W2和B2的字典類型的變量 返回: 成本 - 交叉熵成本給出方程(13) """ m = Y.shape[1] W1 = parameters["W1"] W2 = parameters["W2"] #計算成本 logprobs = logprobs = np.multiply(np.log(A2), Y) + np.multiply((1 - Y), np.log(1 - A2)) cost = - np.sum(logprobs) / m cost = float(np.squeeze(cost)) assert(isinstance(cost,float)) return cost def backward_propagation(parameters,cache,X,Y): """ 使用上述說明搭建反向傳播函數。 參數: parameters - 包含我們的參數的一個字典類型的變量。 cache - 包含“Z1”,“A1”,“Z2”和“A2”的字典類型的變量。 X - 輸入數據,維度為(2,數量) Y - “True”標簽,維度為(1,數量) 返回: grads - 包含W和b的導數一個字典類型的變量。 """ m = X.shape[1] W1 = parameters["W1"] W2 = parameters["W2"] A1 = cache["A1"] A2 = cache["A2"] dZ2= A2 - Y dW2 = (1 / m) * np.dot(dZ2, A1.T) db2 = (1 / m) * np.sum(dZ2, axis=1, keepdims=True) dZ1 = np.multiply(np.dot(W2.T, dZ2), 1 - np.power(A1, 2)) dW1 = (1 / m) * np.dot(dZ1, X.T) db1 = (1 / m) * np.sum(dZ1, axis=1, keepdims=True) grads = {"dW1": dW1, "db1": db1, "dW2": dW2, "db2": db2 } return grads def update_parameters(parameters,grads,learning_rate=1.2): """ 使用上面給出的梯度下降更新規則更新參數 參數: parameters - 包含參數的字典類型的變量。 grads - 包含導數值的字典類型的變量。 learning_rate - 學習速率 返回: parameters - 包含更新參數的字典類型的變量。 """ W1,W2 = parameters["W1"],parameters["W2"] b1,b2 = parameters["b1"],parameters["b2"] dW1,dW2 = grads["dW1"],grads["dW2"] db1,db2 = grads["db1"],grads["db2"] W1 = W1 - learning_rate * dW1 b1 = b1 - learning_rate * db1 W2 = W2 - learning_rate * dW2 b2 = b2 - learning_rate * db2 parameters = {"W1": W1, "b1": b1, "W2": W2, "b2": b2} return parameters def nn_model(X,Y,n_h,num_iterations,print_cost=False): """ 參數: X - 數據集,維度為(2,示例數) Y - 標簽,維度為(1,示例數) n_h - 隱藏層的數量 num_iterations - 梯度下降循環中的迭代次數 print_cost - 如果為True,則每1000次迭代打印一次成本數值 返回: parameters - 模型學習的參數,它們可以用來進行預測。 """ np.random.seed(3) #指定隨機種子 n_x = layer_sizes(X, Y)[0] n_y = layer_sizes(X, Y)[2] parameters = initialize_parameters(n_x,n_h,n_y) W1 = parameters["W1"] b1 = parameters["b1"] W2 = parameters["W2"] b2 = parameters["b2"] for i in range(num_iterations): #多次迭代,更新w,b,求出相應的成本函數 A2 , cache = forward_propagation(X,parameters) #先前向傳播,得到結果A2以及將相應的數據存儲到cache中 cost = compute_cost(A2,Y,parameters) #根據得到的結果A2和真正的結果Y去計算成本 grads = backward_propagation(parameters,cache,X,Y) #然后計算相應參數的梯度 parameters = update_parameters(parameters,grads,learning_rate = 0.5) #根據梯度去更新參數 if print_cost: if i%1000 == 0: #1000次迭代后輸出一次成本查看成本的變化 print("第 ",i," 次循環,成本為:"+str(cost)) return parameters def predict(parameters,X): """ 使用學習的參數w,b,為X中的每個示例預測一個類 參數: parameters - 包含參數的字典類型的變量。 X - 輸入數據(n_x,m) 返回 predictions - 我們模型預測的向量(紅色:0 /藍色:1) """ A2 , cache = forward_propagation(X,parameters) predictions = np.round(A2) return predictions
返回:
X的維度為: (2, 400) Y的維度為: (1, 400) 數據集里面的數據有:400 個
然后運行:
parameters = nn_model(X, Y, n_h = 4, num_iterations=10000, print_cost=True) #繪制邊界 plot_decision_boundary(lambda x: predict(parameters, x.T), X, Y) plt.title("Decision Boundary for hidden layer size " + str(4)) predictions = predict(parameters, X) print ('准確率: %d' % float((np.dot(Y, predictions.T) + np.dot(1 - Y, 1 - predictions.T)) / float(Y.size) * 100) + '%')
返回:
第 0 次循環,成本為:0.6930480201239823 第 1000 次循環,成本為:0.3098018601352803 第 2000 次循環,成本為:0.2924326333792646 第 3000 次循環,成本為:0.2833492852647412 第 4000 次循環,成本為:0.27678077562979253 第 5000 次循環,成本為:0.26347155088593194 第 6000 次循環,成本為:0.2420441312994077 第 7000 次循環,成本為:0.23552486626608765 第 8000 次循環,成本為:0.23140964509854278 第 9000 次循環,成本為:0.22846408048352365 准確率: 90%
圖示為:
12.優化
1)更改隱藏層神經元數量
plt.figure(figsize=(16, 32)) hidden_layer_sizes = [1, 2, 3, 4, 5, 20, 50] #更改隱藏層數量 for i, n_h in enumerate(hidden_layer_sizes): plt.subplot(5, 2, i + 1) plt.title('Hidden Layer of size %d' % n_h) parameters = nn_model(X, Y, n_h, num_iterations=5000) plot_decision_boundary(lambda x: predict(parameters, x.T), X, Y) predictions = predict(parameters, X) accuracy = float((np.dot(Y, predictions.T) + np.dot(1 - Y, 1 - predictions.T)) / float(Y.size) * 100) print ("隱藏層的節點數量: {} ,准確率: {} %".format(n_h, accuracy))
返回:
隱藏層的節點數量: 1 ,准確率: 67.25 % 隱藏層的節點數量: 2 ,准確率: 66.5 % 隱藏層的節點數量: 3 ,准確率: 89.25 % 隱藏層的節點數量: 4 ,准確率: 90.0 % 隱藏層的節點數量: 5 ,准確率: 89.75 % 隱藏層的節點數量: 20 ,准確率: 90.0 % 隱藏層的節點數量: 50 ,准確率: 89.75 %
圖示:
較大的模型(具有更多隱藏單元)能夠更好地適應訓練集,直到最終的最大模型過度擬合數據。
最好的隱藏層大小似乎在n_h = 4附近。實際上,這里的值似乎很適合數據,而且不會引起過度擬合。
我們還將在后面學習有關正則化的知識,它允許我們使用非常大的模型(如n_h = 50),而不會出現太多過度擬合。
2)如果更改的是學習率:
將函數更改為:
def nn_model(X,Y,n_h,num_iterations,lr,print_cost=False): #添加學習率lr """ 參數: X - 數據集,維度為(2,示例數) Y - 標簽,維度為(1,示例數) n_h - 隱藏層的數量 num_iterations - 梯度下降循環中的迭代次數 print_cost - 如果為True,則每1000次迭代打印一次成本數值 返回: parameters - 模型學習的參數,它們可以用來進行預測。 """ np.random.seed(3) #指定隨機種子 n_x = layer_sizes(X, Y)[0] n_y = layer_sizes(X, Y)[2] parameters = initialize_parameters(n_x,n_h,n_y) W1 = parameters["W1"] b1 = parameters["b1"] W2 = parameters["W2"] b2 = parameters["b2"] for i in range(num_iterations): A2 , cache = forward_propagation(X,parameters) cost = compute_cost(A2,Y,parameters) grads = backward_propagation(parameters,cache,X,Y) parameters = update_parameters(parameters,grads,learning_rate = lr) #學習率根據輸入進行變化 if print_cost: if i%1000 == 0: print("第 ",i," 次循環,成本為:"+str(cost)) return parameters
測試:
plt.figure(figsize=(16, 32)) lrs = [0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9] #更改隱藏層數量 for i, lr in enumerate(lrs): plt.subplot(5, 2, i + 1) plt.title('learning rate is: %f' % lr) parameters = nn_model(X, Y, n_h = 4,num_iterations=5000,lr = lr) plot_decision_boundary(lambda x: predict(parameters, x.T), X, Y) predictions = predict(parameters, X) accuracy = float((np.dot(Y, predictions.T) + np.dot(1 - Y, 1 - predictions.T)) / float(Y.size) * 100) print ("學習率為: {} ,准確率: {} %".format(lr, accuracy))
返回:
學習率為: 0.1 ,准確率: 88.0 % 學習率為: 0.2 ,准確率: 89.25 % 學習率為: 0.3 ,准確率: 89.5 % 學習率為: 0.4 ,准確率: 89.5 % 學習率為: 0.5 ,准確率: 90.0 % 學習率為: 0.6 ,准確率: 90.25 % 學習率為: 0.7 ,准確率: 90.5 % 學習率為: 0.8 ,准確率: 90.5 % 學習率為: 0.9 ,准確率: 90.5 %
圖示為: