考完試了,總算有時間開始接觸深度學習這塊內容,最近聽完了吳恩達老師在網易雲課堂的第一、二週的課程,及時在此做出總結。(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 公式及含義
本實驗中所需要的數學公式:
由以上公式可以推得:
其中是權重,是偏置值 ;是激勵函數; 是沒有被激活的;是激活後的預測值;是標籤,也就是真實值;是損失函數;是代價函數。
激勵函數(Activation Function)
確保我們需要的預測值在之間
損失函數(Loss Function)
定義在單個訓練樣本上的,也就是就算一個樣本的誤差。比如我們想要分類,就是預測的類別和實際類別的區別,是一個樣本的,用表示。
代價函數(Cost Function)
定義在整個訓練集上面的,也就是所有樣本的誤差的總和的平均,也就是損失函數的總和的平均,有沒有這個平均其實不會影響最後的參數的求解結果,用表示。
四、構建網絡
做了那麼多準備,終於到了激動人心的構建網絡的過程了。(從這裏開始是實際的代碼部分)
在此之前先定義一些需要用到的函數:
# 構建神經網絡
# 激勵函數,確保我們需要的預測值在[0, 1]
def sigmoid(z):
s = 1 / (1 + np.exp(-z))
return s
4.1 初始化
# 構建神經網絡
# 爲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和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()