第二週
邏輯迴歸是一個用於二分類(binary classification)的算法。
sigmoid函數
在梯度下降法中,會得到局部最優解,不能得到全局最優解
在邏輯迴歸中,使用的損失函數:
損失函數是在單個訓練樣本中定義的,它衡量的是算法在單個訓練樣本中表現如何,爲了衡量算法在全部訓練樣本上的表現如何。
我們需要定義一個算法的代價函數,算法的代價函數是對m個樣本的損失函數求和然後除以m:
損失函數只適用於像這樣的單個訓練樣本,而代價函數是參數的總代價,所以在訓練邏輯迴歸模型時候,我們需要找到合適的w和b,來讓代價函數J 的總代價降到最低。
所以這裏有很多細節,但讓我們把這些裝進一個具體的算法。同時你需要一起應用的就是邏輯迴歸和梯度下降。
我們初始化
代碼流程:
J=0;dw1=0;dw2=0;db=0;
for i = 1 to m
z(i) = wx(i)+b;
a(i) = sigmoid(z(i));
J += -[y(i)log(a(i))+(1-y(i))log(1-a(i));
dz(i) = a(i)-y(i);
dw1 += x1(i)dz(i);
dw2 += x2(i)dz(i);
db += dz(i);
J/= m;
dw1/= m;
dw2/= m;
db/= m;
w=w-alpha*dw
b=b-alpha*db
使用向量化替代顯示for循環
此處b是一個實數,自動擴展成1*m的行向量(broadcasting廣播)
梯度下降的一次迭代:
上面可被下面替換
列相加
axis=1 水平相加
python的廣播機制
一個向量的具體緯度
神經元節點先計算線性函數(z = Wx + b),再計算激活。
神經元的輸出是a = g(Wx + b),其中g是激活函數(sigmoid,tanh,ReLU,…)。
第二章編程作業-識別貓
在開始之前,我們有需要引入的庫:
numpy :是用Python進行科學計算的基本軟件包。
h5py:是與H5文件中存儲的數據集進行交互的常用軟件包。
matplotlib:是一個著名的庫,用於在Python中繪製圖表。
lr_utils :一個加載資料包裏面的數據的簡單功能的庫。
import numpy as np
import matplotlib.pyplot as plt
import h5py
from lr_utils import load_dataset
lr_utils.py
代碼如下
import numpy as np
import h5py
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 :保存的是訓練集裏面的圖像數據(本訓練集有209張64x64的圖像)。
train_set_y_orig :保存的是訓練集的圖像對應的分類值(【0 | 1】,0表示不是貓,1表示是貓)。
test_set_x_orig :保存的是測試集裏面的圖像數據(本訓練集有50張64x64的圖像)。
test_set_y_orig : 保存的是測試集的圖像對應的分類值(【0 | 1】,0表示不是貓,1表示是貓)。
classes : 保存的是以bytes類型保存的兩個字符串數據,數據爲:[b’non-cat’ b’cat’]。
train_set_x_orig,train_set_y,test_set_x_orig,test_set_y,classes=load_dataset()
index=25
plt.imshow(train_set_x_orig[index])
#查看訓練集的標籤
print("train_set_y="+str(train_set_y))
結合一下訓練集裏面的數據來看一下都加載了一些什麼東西。
#打印出當前的訓練標籤值
#使用np.squeeze的目的是壓縮維度,【未壓縮】train_set_y[:,index]的值爲[1] , 【壓縮後】np.squeeze(train_set_y[:,index])的值爲1
#print("【使用np.squeeze:" + str(np.squeeze(train_set_y[:,index])) + ",不使用np.squeeze: " + str(train_set_y[:,index]) + "】")
#只有壓縮後的值才能進行解碼操作
print("y=" + str(train_set_y[:,index]) + ", it's a " + classes[np.squeeze(train_set_y[:,index])].decode("utf-8") + "' picture")
- m_train :訓練集裏圖片的數量。
- m_test :測試集裏圖片的數量。
- num_px : 訓練、測試集裏面的圖片的寬度和高度(均爲64x64)。
train_set_x_orig 是一個維度爲(m_train,num_px,num_px,3)的數組
爲了方便,我們要把維度爲(64,64,3)的numpy數組重新構造爲(64 x 64 x 3,1)的數組,要乘以3的原因是每張圖片是由64x64像素構成的,而每個像素點由(R,G,B)三原色構成的,所以要乘以3。在此之後,我們的訓練和測試數據集是一個numpy數組,【每列代表一個平坦的圖像】 ,應該有m_train和m_test列。
當你想將形狀(a,b,c,d)的矩陣X平鋪成形狀(b * c * d,a)的矩陣X_flatten時,可以使用以下代碼:
#X_flatten = X.reshape(X.shape [0],-1).T #X.T是X的轉置
#將訓練集的維度降低並轉置。
train_set_x_flatten = train_set_x_orig.reshape(train_set_x_orig.shape[0],-1).T
#將測試集的維度降低並轉置。
test_set_x_flatten = test_set_x_orig.reshape(test_set_x_orig.shape[0], -1).T
這一段意思是指把數組變爲209行的矩陣(因爲訓練集裏有209張圖片),用-1告訴程序你幫我算,最後程序算出來時12288列,用一個T表示轉置,這就變成了12288行,209列。測試集亦如此。
爲了表示彩色圖像,必須爲每個像素指定紅色,綠色和藍色通道(RGB),因此像素值實際上是從0到255範圍內的三個數字的向量。機器學習中一個常見的預處理步驟是對數據集進行居中和標準化,這意味着可以減去每個示例中整個numpy數組的平均值,然後將每個示例除以整個numpy數組的標準偏差。但對於圖片數據集,它更簡單,更方便,幾乎可以將數據集的每一行除以255(像素通道的最大值),因爲在RGB中不存在比255大的數據,所以我們可以放心的除以255,讓標準化的數據位於[0,1]之間,現在標準化我們的數據集:
train_set_x = train_set_x_flatten / 255
test_set_x = test_set_x_flatten / 255
開始構建神經網絡
然後通過對所有訓練樣例求和來計算成本:
建立神經網絡的主要步驟是:
-
定義模型結構(例如輸入特徵的數量)
-
初始化模型的參數
-
循環:
3.1 計算當前損失(正向傳播)
3.2 計算當前梯度(反向傳播)
3.3 更新參數(梯度下降)
def sigmoid(z):
"""
參數:
z - 任何大小的標量或numpy數組。
返回:
s - sigmoid(z)
"""
s = 1 / (1 + np.exp(-z))
return s
初始化我們需要的參數w和b
def initialize_with_zeros(dim):
"""
此函數爲w創建一個維度爲(dim,1)的0向量,並將b初始化爲0。
參數:
dim - 我們想要的w矢量的大小(或者這種情況下的參數數量)
返回:
w - 維度爲(dim,1)的初始化向量。
b - 初始化的標量(對應於偏差)
"""
w = np.zeros(shape = (dim,1))
b = 0
#使用斷言來確保我要的數據是正確的
assert(w.shape == (dim, 1)) #w的維度是(dim,1)
assert(isinstance(b, float) or isinstance(b, int)) #b的類型是float或者是int
return (w , b)
初始化參數的函數已經構建好了,現在就可以執行“前向”和“後向”傳播步驟來學習參數。
我們現在要實現一個計算成本函數及其漸變的函數propagate()。
def propagate(w, b, X, Y):
"""
實現前向和後向傳播的成本函數及其梯度。
參數:
w - 權重,大小不等的數組(num_px * num_px * 3,1)
b - 偏差,一個標量
X - 矩陣類型爲(num_px * num_px * 3,訓練數量)
Y - 真正的“標籤”矢量(如果非貓則爲0,如果是貓則爲1),矩陣維度爲(1,訓練數據數量)
返回:
cost- 邏輯迴歸的負對數似然成本
dw - 相對於w的損失梯度,因此與w相同的形狀
db - 相對於b的損失梯度,因此與b的形狀相同
"""
m = X.shape[1]
#正向傳播
A = sigmoid(np.dot(w.T,X) + b) #計算激活值,請參考公式2。
cost = (- 1 / m) * np.sum(Y * np.log(A) + (1 - Y) * (np.log(1 - A))) #計算成本,請參考公式3和4。
#反向傳播
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)
cost = np.squeeze(cost)
assert(cost.shape == ())
#創建一個字典,把dw和db保存起來。
grads = {
"dw": dw,
"db": db
}
return (grads , cost)
測試
#測試一下propagate
print("====================測試propagate====================")
#初始化一些參數
w, b, X, Y = np.array([[1], [2]]), 2, np.array([[1,2], [3,4]]), np.array([[1, 0]])
grads, cost = propagate(w, b, X, Y)
print ("dw = " + str(grads["dw"]))
print ("db = " + str(grads["db"]))
print ("cost = " + str(cost))
使用漸變下降更新參數。
def optimize(w , b , X , Y , num_iterations , learning_rate , print_cost = False):
"""
此函數通過運行梯度下降算法來優化w和b
參數:
w - 權重,大小不等的數組(num_px * num_px * 3,1)
b - 偏差,一個標量
X - 維度爲(num_px * num_px * 3,訓練數據的數量)的數組。
Y - 真正的“標籤”矢量(如果非貓則爲0,如果是貓則爲1),矩陣維度爲(1,訓練數據的數量)
num_iterations - 優化循環的迭代次數
learning_rate - 梯度下降更新規則的學習率
print_cost - 每100步打印一次損失值
返回:
params - 包含權重w和偏差b的字典
grads - 包含權重和偏差相對於成本函數的梯度的字典
成本 - 優化期間計算的所有成本列表,將用於繪製學習曲線。
提示:
我們需要寫下兩個步驟並遍歷它們:
1)計算當前參數的成本和梯度,使用propagate()。
2)使用w和b的梯度下降法則更新參數。
"""
costs = []
for i in range(num_iterations):
grads, cost = propagate(w, b, X, Y)
dw = grads["dw"]
db = grads["db"]
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)
測試一下優化函數
#測試optimize
print("====================測試optimize====================")
w, b, X, Y = np.array([[1], [2]]), 2, np.array([[1,2], [3,4]]), np.array([[1, 0]])
params , grads , costs = optimize(w , b , X , Y , num_iterations=100 , learning_rate = 0.009 , print_cost = False)
print ("w = " + str(params["w"]))
print ("b = " + str(params["b"]))
print ("dw = " + str(grads["dw"]))
print ("db = " + str(grads["db"]))
整合
def model(X_train , Y_train , X_test , Y_test , num_iterations = 2000 , learning_rate = 0.5 , print_cost = False):
"""
通過調用之前實現的函數來構建邏輯迴歸模型
參數:
X_train - numpy的數組,維度爲(num_px * num_px * 3,m_train)的訓練集
Y_train - numpy的數組,維度爲(1,m_train)(矢量)的訓練標籤集
X_test - numpy的數組,維度爲(num_px * num_px * 3,m_test)的測試集
Y_test - numpy的數組,維度爲(1,m_test)的(向量)的測試標籤集
num_iterations - 表示用於優化參數的迭代次數的超參數
learning_rate - 表示optimize()更新規則中使用的學習速率的超參數
print_cost - 設置爲true以每100次迭代打印成本
返回:
d - 包含有關模型信息的字典。
"""
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
print("====================測試model====================")
#這裏加載的是真實的數據,請參見上面的代碼部分。
d = model(train_set_x, train_set_y, test_set_x, test_set_y, num_iterations = 2000, learning_rate = 0.005, print_cost = True)
繪圖
#繪製圖
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()
成本下降,它顯示參數正在被學習
爲了讓漸變下降起作用,我們必須明智地選擇學習速率。學習率α \alphaα 決定了我們更新參數的速度。如果學習率過高,我們可能會“超過”最優值。同樣,如果它太小,我們將需要太多迭代才能收斂到最佳值。這就是爲什麼使用良好調整的學習率至關重要的原因。
我們可以比較一下我們模型的學習曲線和幾種學習速率的選擇。也可以嘗試使用不同於我們初始化的learning_rates變量包含的三個值,並看一下會發生什麼。
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, test_set_x, test_set_y, num_iterations = 1500, 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()