基本概念
本文例子


$$({x^{(1)}},{y^{(1)}}),({x^{(2)}},{y^{(2)}}),......,({x^{(m)}},{y^{(m)}})$$
實現方案


由函數圖像可知,sigmoid函數有幾個很好的性質:
求參數w和b
使用梯度下降法來求得參數w和b,使得成本函數的最小化
參考學習:http://www.cnblogs.com/pinard/p/5970503.html
在微積分里面,對多元函數的參數求∂偏導數,把求得的各個參數的偏導數以向量的形式寫出來,就是梯度。比如函數f(x,y), 分別對x,y求偏導數,求得的梯度向量就是${(\frac{{\partial f}}{{\partial x}},\frac{{\partial f}}{{\partial y}})^T}$,簡稱grad f(x,y)或者$\nabla f(x,y)$。對於在點(x0,y0)的具體梯度向量就是${(\frac{{\partial f}}{{\partial x_0}},\frac{{\partial f}}{{\partial y_0}})^T}$,或者$\nabla f(x_0,y_0)$,如果是3個參數的向量梯度,就是${(\frac{{\partial f}}{{\partial x}},\frac{{\partial f}}{{\partial y}},\frac{{\partial f}}{{\partial z}})^T}$,以此類推。
那么這個梯度向量求出來有什么意義呢?他的意義從幾何意義上講,就是函數變化增加最快的地方。具體來說,對於函數f(x,y),在點(x0,y0),沿着梯度向量的方向就是$\nabla f(x_0,y_0)$的方向是f(x,y)增加最快的地方。或者說,沿着梯度向量的方向,更加容易找到函數的最大值。反過來說,沿着梯度向量相反的方向,也就是 $-\nabla f(x_0,y_0)$的方向,梯度減少最快,也就是更加容易找到函數的最小值。
在機器學習算法中,在最小化損失函數時,可以通過梯度下降法來一步步的迭代求解,得到最小化的損失函數,和模型參數值(此處即是w和b)。
在空間坐標中以w,b為軸畫出損失函數J(w,b)的三維圖像,可知這個函數為一個凸函數。為了找到合適的參數,先將w和b賦一個初始值,正如圖中的小紅點。在losgistic回歸中,幾乎任何初始化方法都有效,通常將參數初始化為零。隨機初始化也起作用,但通常不會在losgistic回歸中這樣做,因為這個成本函數是凸的,無論初始化的值是多少,總會到達同一個點或大致相同的點(最優解)。梯度下降就是從起始點開始,試圖在最陡峭的下降方向下坡,以便盡可能快地下坡到達最低點,這個下坡的方向便是此點的梯度值。
在二維圖像中來看,順着導數的方向,下降速度最快,用數學公式表達即是:
$$w: = w - a\frac{{\partial J(w,b)}}{{\partial w}}$$
$$b: = b - a\frac{{\partial J(w,b)}}{{\partial b}}$$
上述兩式表示隨時更新w與b的值。
待解決問題
Python實現
總代碼與輸出
#logistic_regression.py #導入用到的包 import numpy as np import matplotlib.pyplot as plt import h5py import scipy from PIL import Image from scipy import ndimage #導入數據 def load_dataset(): train_dataset = h5py.File("train_cat.h5","r") #讀取訓練數據,共209張圖片 test_dataset = h5py.File("test_cat.h5", "r") #讀取測試數據,共50張圖片 train_set_x_orig = np.array(train_dataset["train_set_x"][:]) #原始訓練集(209*64*64*3) train_set_y_orig = np.array(train_dataset["train_set_y"][:]) #原始訓練集的標簽集(y=0非貓,y=1是貓)(209*1) test_set_x_orig = np.array(test_dataset["test_set_x"][:]) #原始測試集(50*64*64*3 test_set_y_orig = np.array(test_dataset["test_set_y"][:]) #原始測試集的標簽集(y=0非貓,y=1是貓)(50*1) train_set_y_orig = train_set_y_orig.reshape((1,train_set_y_orig.shape[0])) #原始訓練集的標簽集設為(1*209) test_set_y_orig = test_set_y_orig.reshape((1,test_set_y_orig.shape[0])) #原始測試集的標簽集設為(1*50) classes = np.array(test_dataset["list_classes"][:]) return train_set_x_orig, train_set_y_orig, test_set_x_orig, test_set_y_orig, classes #classes = [b'non-cat' b'cat'] #顯示圖片 def image_show(index,dataset): index = index if dataset == "train": plt.imshow(train_set_x_orig[index]) print ("y = " + str(train_set_y[:, index]) + ", 它是一張" + classes[np.squeeze(train_set_y[:, index])].decode("utf-8") + "' 圖片。") elif dataset == "load_dataset_test": plt.imshow(test_set_x_orig[index]) print ("y = " + str(test_set_y[:, index]) + ", 它是一張" + classes[np.squeeze(test_set_y[:, index])].decode("utf-8") + "' 圖片。") #sigmoid函數 def sigmoid(z): s = 1.0/(1+np.exp(-z)) return s #初始化參數w,b def initialize_with_zeros(dim): w = np.zeros((dim,1)) #w為一個dim*1矩陣 b = 0 return w, b #計算Y_hat,成本函數J以及dw,db def propagate(w, b, X, Y): m = X.shape[1] #樣本個數 Y_hat = sigmoid(np.dot(w.T,X)+b) cost = -(np.sum(np.dot(Y,np.log(Y_hat).T)+np.dot((1-Y),np.log(1-Y_hat).T)))/m #成本函數 dw = (np.dot(X,(Y_hat-Y).T))/m db = (np.sum(Y_hat-Y))/m cost = np.squeeze(cost) #壓縮維度 grads = {"dw": dw, "db": db} #梯度 return grads, cost #梯度下降找出最優解 def optimize(w, b, X, Y, num_iterations, learning_rate, print_cost = False):#num_iterations-梯度下降次數 learning_rate-學習率,即參數ɑ 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: #每100次記錄一次成本值 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 #預測出結果 def predict(w, b, X): m = X.shape[1] #樣本個數 Y_prediction = np.zeros((1,m)) #初始化預測輸出 w = w.reshape(X.shape[0], 1) #轉置參數向量w Y_hat = sigmoid(np.dot(w.T,X)+b) #最終得到的參數代入方程 for i in range(Y_hat.shape[1]): if Y_hat[:,i]>0.5: Y_prediction[:,i] = 1 else: Y_prediction[:,i] = 0 return Y_prediction #建立整個預測模型 def model(X_train, Y_train, X_test, Y_test, num_iterations = 2000, learning_rate = 0.5, print_cost = False): #num_iterations-梯度下降次數 learning_rate-學習率,即參數ɑ w, b = initialize_with_zeros(X_train.shape[0]) #初始化參數w,b parameters, grads, costs = optimize(w, b, X_train, Y_train, num_iterations, learning_rate, print_cost) #梯度下降找到最優參數 w = parameters["w"] #獲得最終參數 b = parameters["b"] #獲得最終參數 Y_prediction_train = predict(w, b, X_train) #訓練集的預測結果 Y_prediction_test = predict(w, b, X_test) #測試集的預測結果 train_accuracy = 100 - np.mean(np.abs(Y_prediction_train - Y_train)) * 100 #訓練集識別准確度……abs——絕對值……mean——求均值 test_accuracy = 100 - np.mean(np.abs(Y_prediction_test - Y_test)) * 100 #測試集識別准確度 print("訓練集識別准確度: {} %".format(train_accuracy)) print("測試集識別准確度: {} %".format(test_accuracy)) d = {"costs": costs, "Y_prediction_test": Y_prediction_test, "Y_prediction_train" : Y_prediction_train, "w" : w, "b" : b, "learning_rate" : learning_rate, "num_iterations": num_iterations} return d #初始化數據 train_set_x_orig, train_set_y, test_set_x_orig, test_set_y, classes = load_dataset() m_train = train_set_x_orig.shape[0] #訓練集中樣本個數 m_test = test_set_x_orig.shape[0] #測試集總樣本個數 num_px = test_set_x_orig.shape[1] #圖片的像素大小 train_set_x_flatten = train_set_x_orig.reshape(train_set_x_orig.shape[0],-1).T #原始訓練集的設為(12288*209) test_set_x_flatten = test_set_x_orig.reshape(test_set_x_orig.shape[0],-1).T #原始測試集設為(12288*50) train_set_x = train_set_x_flatten/255. #將訓練集矩陣標准化 test_set_x = test_set_x_flatten/255. #將測試集矩陣標准化 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() #學習率不同時的學習曲線 learning_rates = [0.01, 0.001, 0.0001] models = {} for i in learning_rates: print ("學習率: " + str(i)) models[str(i)] = model(train_set_x, train_set_y, test_set_x, test_set_y, 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()
out:
解釋:
以上輸出表示的是梯度下降次數為2000次,每次的下降步長分別為0.01,,0.001,0.0001的測試結果。
由測試結果可知:
1、循環n次后的成本值最小可以達到為0,而成本值表示的是預測值與真實值之間的差異程度,差異越小越好,所以達到為0 是最理想的結果。
2、梯度下降的次數與下降步長需要協調改進,如同你要走一段固定的距離到達一個位置(即達到成本值最小,但具體不知道多少),步數多了,步子就要變小,步數少了,步子就要變大,但其實我們總有可能還沒有到達那個位置或者超過了那個位置。以上測試中只減小了步長,很大可能是成本值還沒有達到最小值。
3、訓練集的准確度是可以達到100%的,因為我們是通過訓練集來確定出算法參數的,也即是說訓練集的數據必然是接近100%擬合算法的,但測試集不是,測試集是我們確定了算法之后,再將經過算法得出的的預測值與真實值進行比較得到的結果。
函數匯總
- load_dataset():return train_set_x_orig, train_set_y_orig, test_set_x_orig, test_set_y_orig, classes #導入數據
- image_show(index,dataset):return null #顯示圖片
- sigmoid(z):return s #sigmoid函數
- initialize_with_zeros(dim):return w, b ##初始化參數w,b
- propagate(w, b, X, Y):return grads, cost ##計算Y_hat,成本函數J以及dw,db
- optimize(w, b, X, Y, num_iterations, learning_rate, print_cost = False):return params, grads, costs #梯度下降找出最優解
- predict(w, b, X):return Y_prediction #預測出結果
- model(X_train, Y_train, X_test, Y_test, num_iterations = 2000, learning_rate = 0.5, print_cost = False):return d #建立整個預測模型
整個算法大概的流程:導入數據load_dataset()——通過訓練數據計算$\hat y$propagate(w, b, X, Y),該函數會返回w和b的梯度值與成本函數cost公式——通過梯度下降法找出w和b的最優解optimize( ),返回最終的w和b參數——再把w和b參數賦給函數predict(),得出最終的預測算法
以上流程部分嵌套,最后整合在model()中.
部分代碼分塊解釋
數據導入load_dataset():(加入了測試代碼)
#logistic_regression.py #導入用到的包 import numpy as np import matplotlib.pyplot as plt import h5py import scipy from PIL import Image from scipy import ndimage #導入數據 def load_dataset(): train_dataset = h5py.File("train_cat.h5","r") #讀取訓練數據,共209張圖片 test_dataset = h5py.File("test_cat.h5", "r") #讀取測試數據,共50張圖片 train_set_x_orig = np.array(train_dataset["train_set_x"][:]) #原始訓練集(209*64*64*3) train_set_y_orig = np.array(train_dataset["train_set_y"][:]) #原始訓練集的標簽集(y=0非貓,y=1是貓)(209*1) test_set_x_orig = np.array(test_dataset["test_set_x"][:]) #原始測試集(50*64*64*3 test_set_y_orig = np.array(test_dataset["test_set_y"][:]) #原始測試集的標簽集(y=0非貓,y=1是貓)(50*1) train_set_y_orig = train_set_y_orig.reshape((1,train_set_y_orig.shape[0])) #原始訓練集的標簽集設為(1*209) test_set_y_orig = test_set_y_orig.reshape((1,test_set_y_orig.shape[0])) #原始測試集的標簽集設為(1*50) classes = np.array(test_dataset["list_classes"][:]) return train_set_x_orig, train_set_y_orig, test_set_x_orig, test_set_y_orig, classes #初始化數據 train_set_x_orig, train_set_y, test_set_x_orig, test_set_y, classes = load_dataset() #classes = [b'non-cat' b'cat'] #print("train_set_x_orig = " + str(train_set_x_orig)) #print("train_set_y = " + str(train_set_y)) #print("test_set_x_orig = " + str(test_set_x_orig)) #print("test_set_y = " + str(test_set_y)) print("classes = " + str(classes)) #print("type(train_set_x_orig) = " + str(type(train_set_x_orig))) #print("type(train_set_y) = " + str(type(train_set_y)))
測試:
print("type(train_set_y) = " + str(type(train_set_y)))————輸出:type(train_set_y) = <class 'numpy.ndarray'>:輸出的數據是數組形式
如下(圖片數據的保存形式,即.h5文件內容):
print("classes = " + str(classes))————輸出: classes = [b'non-cat' b'cat']
將數據還原為圖片:
#logistic_regression.py #導入用到的包 import numpy as np import matplotlib.pyplot as plt import h5py import scipy from PIL import Image from scipy import ndimage #導入數據 def load_dataset(): train_dataset = h5py.File("train_cat.h5","r") #讀取訓練數據,共209張圖片 test_dataset = h5py.File("test_cat.h5", "r") #讀取測試數據,共50張圖片 train_set_x_orig = np.array(train_dataset["train_set_x"][:]) #原始訓練集(209*64*64*3) train_set_y_orig = np.array(train_dataset["train_set_y"][:]) #原始訓練集的標簽集(y=0非貓,y=1是貓)(209*1) test_set_x_orig = np.array(test_dataset["test_set_x"][:]) #原始測試集(50*64*64*3 test_set_y_orig = np.array(test_dataset["test_set_y"][:]) #原始測試集的標簽集(y=0非貓,y=1是貓)(50*1) train_set_y_orig = train_set_y_orig.reshape((1,train_set_y_orig.shape[0])) #原始訓練集的標簽集設為(1*209) test_set_y_orig = test_set_y_orig.reshape((1,test_set_y_orig.shape[0])) #原始測試集的標簽集設為(1*50) classes = np.array(test_dataset["list_classes"][:]) return train_set_x_orig, train_set_y_orig, test_set_x_orig, test_set_y_orig, classes #顯示圖片 def image_show(index,dataset): index = index if dataset == "train": plt.imshow(train_set_x_orig[index]) print ("y = " + str(train_set_y[:, index]) + ", 它是一張" + classes[np.squeeze(train_set_y[:, index])].decode("utf-8") + "' 圖片。") elif dataset == "load_dataset_test": plt.imshow(test_set_x_orig[index]) print ("y = " + str(test_set_y[:, index]) + ", 它是一張" + classes[np.squeeze(test_set_y[:, index])].decode("utf-8") + "' 圖片。") #初始化數據 train_set_x_orig, train_set_y, test_set_x_orig, test_set_y, classes = load_dataset() for i in range(0,50): plt.subplot(5,10,i+1) print(i+1) image_show(i,"load_dataset_test") plt.show() ''' for i in range(0,50): plt.subplot(5,10,i+1) print(i+1) image_show(i,"train") plt.show() '''
輸出:
關於最優解
來看看梯度下降的一個直觀的解釋。比如我們在一座大山上的某處位置,由於我們不知道怎么下山,於是決定走一步算一步,也就是在每走到一個位置的時候,求解當前位置的梯度,沿着梯度的負方向,也就是當前最陡峭的位置向下走一步,然后繼續求解當前位置梯度,向這一步所在位置沿着最陡峭最易下山的位置走一步。這樣一步步的走下去,一直走到覺得我們已經到了山腳。當然這樣走下去,有可能我們不能走到山腳,而是到了某一個局部的山峰低處。
從上面的解釋可以看出,梯度下降不一定能夠找到全局的最優解,有可能是一個局部最優解。當然,如果損失函數是凸函數,梯度下降法得到的解就一定是全局最優解。
來源:http://www.cnblogs.com/pinard/p/5970503.html
關於sigmoid函數中e的參數對算法結果的影響
不同a值情況下的圖像



在程序中改變a的值進行測試
以上的結果是在梯度下降次數為2000次,學習率為0.005的條件下測試的,可以看出,調整a的值是有助於改變識別准確度的。
但也可以看到,在相同條件下,只改變a的值也會改變訓練集的識別准確度,這又是為什么?
我們知道訓練集的識別准確度是可以通過梯度下降次數與學習率的調整最高達到100%的,那么再測試一下不同梯度下降次數條件下識別的准確度會有什么影響:
由以上測試可以看出,訓練集的識別准確度是可以達到100%的,但相應的卻降低了測試集的識別准確度。
我們知道訓練集的准確度可以達到100%,完全是因為算法的參數就是從中計算出來的,也就是說,訓練集的數據是近乎完全擬合算法的。但測試集不是,我們只是找了另外一些數據來對得到的這個算法進行測試,測試的過程類似於我們拿兩張貓的圖片來進行對比,一張已知是貓,一張未知,看他們的相似程度,相似程度高,就認為未知圖片是貓。只是算法換成了數據,對比的也是數據。先把數據轉換成函數,再通過這個函數來測試另外的數據。但就是這個數據轉換成函數的結果,有很多種,優劣不一,它必然會省略掉一些信息,導致另外拿來測試的數據實際上應該是符合的卻被測試為不符合。也即是再好的一個識別貓的算法,我們必然有可能拿一張不是貓的圖像讓它認為是貓,再拿一張是貓的圖像讓它認為不是貓(也即是對抗樣本模型)。
所以說,上面測試集識別准確度與訓練集准確度的關系並不會是線性的,跟算法有很大的關系,但即使如此,測試集的識別准確度也在68%-74%之間了(梯度優化足夠的前提下)。