吳恩達課后作業學習1-week3-homework-one-hidden-layer


參考: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.69304802012398231000  次循環,成本為:0.30980186013528032000  次循環,成本為:0.29243263337926463000  次循環,成本為:0.28334928526474124000  次循環,成本為:0.276780775629792535000  次循環,成本為:0.263471550885931946000  次循環,成本為:0.24204413129940777000  次循環,成本為:0.235524866266087658000  次循環,成本為:0.231409645098542789000  次循環,成本為: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 %

圖示為:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM