【神經網絡和深度學習】吳恩達(Andrew Ng)- 第一課第二週課程編程作業

  考完試了,總算有時間開始接觸深度學習這塊內容,最近聽完了吳恩達老師在網易雲課堂的第一、二週的課程,及時在此做出總結。(PS:第一週是概念性的東西,不理解的話多次回看就好了,總結從第二週的課程開始)


一、綜述

  首先,這篇博客是學完第二週的課程之後的總結,是以第二週的編程作業的邏輯順序來寫的,並非課程輔助教程之類的文章,內容可能與課程內容順序不符,希望本着課程輔導教程的讀者諒解。
  因此本篇文章行文順序如下圖:行文順序

二、準備工作

2.1 分析問題

  本次實驗要做的是簡單基於logistic regression的二分類,判斷:貓或不是貓。即使用訓練集和優化器得出最優的w和b,以此最優的w和b來測試(這段似乎就是上圖的意思……看不看都行)。

2.2 處理數據

  注意我們首先需要下載到本次實驗的數據以及讀取數據方法,並且將數據放在項目目錄下新建的datasets文件夾內。其中pythonIr_utils.py文件內的方法是用來讀取數據集的。

# 讀取數據

# train_set_x_orig :訓練集裏的圖片(本訓練集有209張64x64的圖像)。
# train_set_y_orig :訓練集的圖像對應的分類值(0表示不是貓,1表示是貓)。
# test_set_x_orig :測試集裏面的圖像數據(本訓練集有50張64x64的圖像)。
# test_set_y_orig : 測試集的圖像對應的分類值(0表示不是貓,1表示是貓)。
# classes : 保存的是以bytes類型保存的兩個字符串數據,數據爲:[b’non-cat’ b’cat’]。
def load_dataset():
    train_dataset = h5py.File('datasets/train_catvnoncat.h5', "r")
    train_set_x_orig = np.array(train_dataset["train_set_x"][:])  # your train set features
    train_set_y_orig = np.array(train_dataset["train_set_y"][:])  # your train set labels

    test_dataset = h5py.File('datasets/test_catvnoncat.h5', "r")
    test_set_x_orig = np.array(test_dataset["test_set_x"][:])  # your test set features
    test_set_y_orig = np.array(test_dataset["test_set_y"][:])  # your test set labels

    classes = np.array(test_dataset["list_classes"][:])  # the list of classes
    
    train_set_y_orig = train_set_y_orig.reshape((1, train_set_y_orig.shape[0]))
    test_set_y_orig = test_set_y_orig.reshape((1, test_set_y_orig.shape[0]))
    
    return train_set_x_orig, train_set_y_orig, test_set_x_orig, test_set_y_orig, classes

  根據以上的load_dataset()的返回值,我們可以得到本次實驗所需的相關數據,我們可以測試輸出相關數據的信息。

# 處理數據 測試數據格式

train_set_x_orig, train_set_y_orig, test_set_x_orig, test_set_y_orig, classes = load_dataset()

# 訓練集圖片數量,train_set_y_orig (1,209)
num_train = train_set_y_orig.shape[1]
# 測試集圖片數量,test_set_y_orig (1,50)
num_test = test_set_y_orig.shape[1]
# 訓練集圖片維度 64,64,3
num_px = train_set_x_orig.shape[1]

  訓練集和測試集的輸入數據均爲64 * 64 * 3的圖片,即長64px、寬64px、三通道(RGB)的圖片。

# 處理數據 降維處理

# 訓練集 測試集 輸入數據降維並轉置
# .reshape(,-1)代表我也不知道是多少列,你自己算
train_set_x_flatten = train_set_x_orig.reshape(train_set_x_orig.shape[0], -1).T  # (64 * 64 * 3, 209)
test_set_x_flatten = test_set_x_orig.reshape(test_set_x_orig.shape[0], -1).T  # (64 * 64 * 3, 50)

# 標籤
train_set_y_label = train_set_y_orig.reshape(train_set_y_orig.shape[0], -1)  # (1, 209)
test_set_y_label = test_set_y_orig.reshape(test_set_y_orig.shape[0], -1)  # (1, 50)

爲了方便處理,對於輸入數據,每一個圖片使用一列進行表示;對於標籤,每一個圖片對應的標籤使用一列進行記錄,因此我們對輸入數據和標籤進行了降維處理

# 處理數據 居中和標準化
train_set_x = train_set_x_flatten / 255
test_set_x = test_set_x_flatten / 255

對輸入數據進行居中和標準化,因爲RGB數據中不存在比255大的數據,因此對於每個數據除以255,使得標準化的數據位於[0, 1]之間。


處理數據過程如圖:
處理數據過程

三、分析網絡

  我們需要建立一個Logistic Pregression,全部實驗的過程如下圖:全部實驗的過程

3.1 公式及含義

本實驗中所需要的數學公式:
(1)z(i)=wTx(i)+bz^{(i)} = w^T x^{(i)} + b \tag{1}
(2)y^(i)=a(i)=sigmoid(z(i))=σ(z(i))=11+ez(i)\hat{y}^{(i)} = a^{(i)} = sigmoid(z^{(i)}) = \sigma(z^{(i)}) = \frac{1}{1+e^{-z^{(i)}}} \tag{2}
(3)L(a(i),yi)=(y(i)log(a(i))+(1y(i))log(1a(i)))L(a^{(i)},y^{i}) = -\Big(y^{(i)}log(a^{(i)}) + (1-y^{(i)})log(1-a^{(i)})\Big) \tag{3}
(4)J(w,b)=1mi=1mL(a(i),y(i))J(w,b) = \frac{1}{m}\sum_{i=1}^mL(a^{(i)},y^{(i)}) \tag{4}
由以上公式可以推得:
(5)J(w,b)w=1m(X(AY)T)\frac{\partial J(w,b)}{\partial w} = \frac{1}{m}(X ·(A - Y)^{T}) \tag{5}
(6)J(w,b)b=1mi=1m(AY)\frac{\partial J(w,b)}{\partial b} = \frac{1}{m}\sum_{i=1}^m(A - Y) \tag{6}
其中ww是權重,bb是偏置值 ;y=sigmoid(x)y = sigmoid(x)是激勵函數; z(i)z^{(i)}是沒有被激活的;y^(i)\hat y^{(i)}是激活後的預測值;yiy^{i}是標籤,也就是真實值;L(a(i),yi)L(a^{(i)},y^{i})是損失函數;J(w,b)J(w,b)是代價函數。

激勵函數(Activation Function)

  確保我們需要的預測值在[0,1][0,1]之間

損失函數(Loss Function)

  定義在單個訓練樣本上的,也就是就算一個樣本的誤差。比如我們想要分類,就是預測的類別和實際類別的區別,是一個樣本的,用LL表示。

代價函數(Cost Function)

  定義在整個訓練集上面的,也就是所有樣本的誤差的總和的平均,也就是損失函數的總和的平均,有沒有這個平均其實不會影響最後的參數的求解結果,用JJ表示。

四、構建網絡

  做了那麼多準備,終於到了激動人心的構建網絡的過程了。(從這裏開始是實際的代碼部分)
  在此之前先定義一些需要用到的函數:

# 構建神經網絡
# 激勵函數,確保我們需要的預測值在[0, 1]
def sigmoid(z):
    s = 1 / (1 + np.exp(-z))
    return s

4.1 初始化w,bw,b

# 構建神經網絡
# 爲w創建一個維度爲(dim,1)的0向量,並將b初始化爲0,dim 是特徵的維度(數量)
def initialize_with_zeros(dim):
    # 返回w和b,w爲(dim,1) b是標量
    w = np.zeros(shape=(dim, 1))
    b = 0

    # 吳恩達老師再三強調過,要確保數據的正確性
    assert(w.shape == (dim, 1))
    assert(isinstance(b, float) or isinstance(b, int))
    return w, b
  

4.2 正向傳播、後向傳播

# 正向傳播、後向傳播

# w - 權重
# b - 偏置值
# X - 輸入
# Y - 標籤 [是貓: 1,非貓:0]
# 根據調用,我們可以看到通過X傳入的值就是train_set_x,也就是居中和標準化之後的訓練集輸入;
# 通過Y傳入的值就是train_set_y_orig,也就是訓練集圖像對應的標籤(分類值),0表示不是貓,1表示是貓
def propagate(w, b, X, Y):
    m = X.shape[1]

    # 正向傳播
    A = sigmoid(np.dot(w.T, X) + b)
    # 用來衡量算法運行情況
    # loss function 通常用L表示
    # 損失函數(Loss function)是定義在單個訓練樣本上的,也就是就算一個樣本的誤差。
    # 比如我們想要分類,就是預測的類別和實際類別的區別,是一個樣本的,用L表示

    # cost function 通常用J表示
    # 代價函數(Cost function)是定義在整個訓練集上面的,
    # 也就是所有樣本的誤差的總和的平均,也就是損失函數的總和的平均。
    # 有沒有這個平均其實不會影響最後的參數的求解結果。
    # logistic regression 中使用 cost function 即J(w,b)
    cost = (-1 / m) * np.sum(Y * np.log(A) + (1 - Y) * (np.log(1 - A)))  # 成本函數

    # 反向傳播 
    # 根據損失函數,反向的計算每一層的z,a,w,b的偏導數,從而更新參數
    # z = w.T * x + b
    # a = sigmoid(z)
    # L(a,y) = -(ylog(a) + (1-y)log(1-a))
    # dL/dw = dL/da * da/dz * dz/ dw
    # dL/db = dL/da * da/dz * dz/ db
    dw = (1 / m) * np.dot(X, (A - Y).T)
    db = (1 / m) * np.sum(A - Y)

    # 確保數據正確
    assert(dw.shape == w.shape)
    assert(db.dtype == float)

    # 保存 dw db
    grads = {
        'dw': dw,
        'db': db
    }
    return grads, cost

4.3 梯度下降優化w,bw,b

# 通過運行梯度下降算法來優化w和b
# 根據後邊的調用,我們可以看到通過X傳入的值就是train_set_x,也就是居中和標準化之後的訓練集輸入;
# 通過Y傳入的值就是train_set_y_orig,也就是訓練集圖像對應的標籤(分類值),0表示不是貓,1表示是貓
def optimize(w, b, X, Y, num_literations, learning_rate, print_cost = False):
    costs = []
    for i in range(num_literations):
        grads, cost = propagate(w, b, X, Y)

        dw = grads['dw']
        db = grads['db']

        # 梯度下降算法來優化w和b
        w = w - learning_rate * dw
        b = b - learning_rate * db
        if i % 100 == 0:
            costs.append(cost)

        if print_cost and (i % 100 == 0):
            print("迭代次數:%i, 誤差值:%f" % (i, cost))
    params = {
        'w': w,
        'b': b
    }
    grads = {
        'dw': dw,
        'db': db
    }
    return params, grads, costs

4.4 預測

# 預測
# 使用優化好的w,b來預測訓練集或測試集數據的正確性
# 根據調用,我們可以看到輸入的X可以是train_set_x、test_set_x,即居中和標準化之後的訓練/測試集輸入
def predict(w, b, X):
    m = X.shape[1]  # 圖片數量
    Y_prediction = np.zeros((1, m))
    w = w.reshape(X.shape[0], 1)

    # 預測貓在圖片中出現的概率
    A = sigmoid(np.dot(w.T, X) + b)
    for i in range(A.shape[1]):
        if A[0, i] > 0.5:
            Y_prediction[0, i] = 1
        else:
            Y_prediction[0, i] = 0
        assert(Y_prediction.shape == (1, m))

    return Y_prediction

4.5 整合與調用

#整合
def model(X_train, Y_train, X_test, Y_test, num_iterations = 2000, learning_rate = 0.5, print_cost = False):
	# 初始化
    w, b = initialize_with_zeros(X_train.shape[0])
    # 優化
    parameters, grads, costs = optimize(w, b, X_train, Y_train, num_iterations, learning_rate, print_cost)

    # 從字典中檢索參數 w 和 b
    w, b = parameters['w'], parameters['b']

    # 預測測試/訓練集
    Y_prediction_test = predict(w, b, X_test)
    Y_prediction_train = predict(w, b, X_train)

    # 打印訓練後的準確性
    print("訓練集準確性:", format(100 - np.mean(np.abs(Y_prediction_train - Y_train)) * 100), "%")
    print("測試集準確性:", format(100 - np.mean(np.abs(Y_prediction_test - Y_test)) * 100), "%")

    d = {
        "costs": costs,
        "Y_prediction_test": Y_prediction_test,
        "Y_prediciton_train": Y_prediction_train,
        "w": w,
        "b": b,
        "learning_rate": learning_rate,
        "num_iterations": num_iterations}
    return d

# 調用
d = model(train_set_x, train_set_y_orig, test_set_x, test_set_y_orig, num_iterations=2000, learning_rate=0.01, print_cost=True)
# print(d)


五、運行結果

迭代次數:0, 誤差值:0.693147
迭代次數:100, 誤差值:0.823921
迭代次數:200, 誤差值:0.418944
迭代次數:300, 誤差值:0.617350
迭代次數:400, 誤差值:0.522116
迭代次數:500, 誤差值:0.387709
迭代次數:600, 誤差值:0.236254
迭代次數:700, 誤差值:0.154222
迭代次數:800, 誤差值:0.135328
迭代次數:900, 誤差值:0.124971
迭代次數:1000, 誤差值:0.116478
迭代次數:1100, 誤差值:0.109193
迭代次數:1200, 誤差值:0.102804
迭代次數:1300, 誤差值:0.097130
迭代次數:1400, 誤差值:0.092043
迭代次數:1500, 誤差值:0.087453
迭代次數:1600, 誤差值:0.083286
迭代次數:1700, 誤差值:0.079487
迭代次數:1800, 誤差值:0.076007
迭代次數:1900, 誤差值:0.072809
訓練集準確性: 99.52153110047847 %
測試集準確性: 70.0 %

5.1 分析結果

  對於訓練集近乎100%的準確性,學姐說是因爲過擬合了,由於作者暫時水平有限,不做過多分析,現挖此坑,以後再填。

六、學習率-成本曲線圖

#繪製圖
costs = np.squeeze(d['costs'])
plt.plot(costs)
plt.ylabel('cost')
plt.xlabel('iterations (per hundreds)')
plt.title("Learning rate =" + str(d["learning_rate"]))
plt.show()

結果
  根據做出的圖像,能夠更直觀的看到學習率變化引起的成本的變化。

# 繪製多個學習率的圖像做對比
learning_rates = [0.01, 0.001, 0.0001]
models = {}
for i in learning_rates:
    print("learning rate is: " + str(i))
    models[str(i)] = model(train_set_x, train_set_y_orig, test_set_x, test_set_y_orig, num_iterations=2000, learning_rate=i, print_cost=False)
    print('\n' + "-------------------------------------------------------" + '\n')

for i in learning_rates:
    plt.plot(np.squeeze(models[str(i)]["costs"]), label=str(models[str(i)]["learning_rate"]))

plt.ylabel('cost')
plt.xlabel('iterations')

legend = plt.legend(loc='upper center', shadow=True)
frame = legend.get_frame()
frame.set_facecolor('0.90')
plt.show()

在這裏插入圖片描述

七、參考資料

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章