python logistic回归_logistic回归介绍与源码分析
1. 介紹(由線性模型引出logistic回歸)
首先介紹一下什么是線性模型呢?
線性模型的定義如下:給定
個屬性描述的樣本 , 代表樣本在第 個屬性上的取值。線性模型的目的是學習一個函數,它可以通過屬性的線性組合來進行預測。
線性模型中的$textbf x$直觀的表達了各個屬性在預測中的重要性,具有很好的可解釋性,一些非線性模型可在線性模型的基礎上引入層級結構或高維映射得到。
線性模型可解決回歸任務和分類任務,讓我們先來看回歸任務。
- 簡單線性回歸:一個樣本僅有一個屬性
- 多元線性回歸:一個樣本有多個屬性
2. 分類任務
logistic回歸,有時也被稱為邏輯回歸,但是部分人認為邏輯回歸不準確,應該譯為對數幾率回歸或對率回歸,在此處,我們不多做解釋,僅稱其為logistic回歸。
logistic到底是什么呢?怎么來的?
2. logistic回歸的來歷
我們可以通過簡單線性回歸、復雜線性回歸等線性模型完成回歸任務,怎么講線性模型應用于分類任務中呢,尤其是二分類。(提示:可以參考對數線性回歸的原理)答案:需要找到一個單調可微函數,將線性模型的輸出和分類任務的真實標記
聯系起來。接下來,我們需要考慮這樣的單調可微函數是什么。
分析:線性模型的輸出范圍是
,二分類任務的真實標記值為 ,因此,顯而易見,我們需要一個函數,能夠將 轉化為 ,這種理想的函數應該是'單位階躍函數',單位階躍函數到底啥樣子呢,我們通過公式和函數圖像了解一下。公式:
公式的含義是當
大于0時,判定為正例;小于0判定為反例;等于0時,可以判別為正例或者反例。函數圖像如下:
圖3.2(來自西瓜書)中的紅色線代表的就是單位階躍函數,但是單調階躍函數不連續,因此不能直接用于將回歸任務變為二分類任務。
我們希望找到一個替代函數,他要求有以下性質:
次數引入一個新的概念:Sigmoid函數
形似S的函數,logistic函數是其中最重要的代表。因此logistic函數可以作為單位階躍函數的替代函數:
函數的圖像如圖3.2中黑色線所示,實際上可以理解為對數幾率函數將$z$值(線性回歸的預測值)轉換為接近0、1的值,輸出值在
處變化很大很陡,將 的展開帶入上式得:公式4實際上將線性回歸的真實結果去逼近真實標記的對數幾率,因此稱其為logistic回歸。
3. 優點
logistic回歸方法有很多優點:
4. logistic的損失函數(Loss Function)和成本函數(Cost Function)
首先介紹一下損失函數和成本函數的概念:
損失函數:即Loss Function,也叫誤差函數,用來衡量單個樣本預測輸出值和實際輸出值有多接近,用
表示。損失函數最常用的方法是均方誤差,即預測值和實際值的平方差。成本函數:即Cost Function,也叫代價函數,用來衡量整體樣本參數的總代價,用
表示。兩者之間的關系:損失函數針對單個樣本而言,成本函數針對整體樣本而言。成本函數相當于求取整體樣本的平均損失,因此可以表示為
。ps:這里的m不一定值全部的樣本數量,意義有點類似于單次更新使用樣本的數量,例如在mini-batch梯度下降中,這里的m代表mini-batch的大小。(mibi-batch梯度下降的介紹再另一篇博文中)
介紹完損失函數和成本函數的相關概念,自然而然的問題就是logistic回歸的損失函數和成本函數是什么,我們在這里直接給出,然后再進行分析:
以上分別是logistic回歸的損失函數和成本函數。
咦?是不是很奇怪,logistic回歸那么簡單,為何不使用均方誤差,而采用那么復雜的損失函數呢?
原因是在我們使用優化算法學習邏輯回歸參數時,要保證我們的優化目標,即損失函數是個凸函數(凸函數的介紹在另一篇博文中),但是在logistic回歸中使用均方誤差會導致損失函數不是凸函數,優化算法只能找到多個局部最優解,找不到全局最優解,因此不使用均方誤差作為logistic回歸的損失函數。
還有一個問題,為何使用上述的公式作為損失函數呢?
額······我盡量嘗試解答,原諒我個數學渣渣~~~(以下部分可忽略)
在本文的第二部分,我們得出了求取
的表達式,即 。在此,我們約定
表示給定訓練樣本 條件下 等于1的概率,即 ;因為logistic回歸解決的是二分類任務,因此 代表的就是 等于0的概率。整理成公式如下:
因為logictic回歸解決的是二分類問題,因此我們可以將上述公式合并成一個公式:
別問我為啥這么合并,但是不知道這么合并的原因,我們可以驗證這么合并是否準確!
- 當y=0時:
與合并之前的第二個公式一樣,木有問題!
- 當y=1時:
與合并之前的第一個公式一樣,木有問題!
結論:按照上述方式合并木有問題!!!
我們繼續~
我們的目標是最大化條件概率,為何要最大化條件概率,我的理解是對于
,它的值越大,證明它越接近真實值,我們的預測越準確。因為
函數是嚴格的單調遞增函數,因此最大化 相當于最大化 ,我們對 進行化簡:咦!是不是特別熟悉,有點像之前我們給出的損失函數了。
最有一步就是從最大化條件概率和最小化損失函數(原因是優化算法通過凸函數尋找最優解的過程)的轉換,我們現在已知最大化的條件概率,要求解損失函數,加個負號(-)就可以了。
因此,得到最終的損失函數:
得到損失函數,成本函數可以通過損失函數求得:
5. batch梯度下降法
根據數據量的大小和模型的難易程度,我們從batch梯度下降、mini-batch梯度下降、隨機梯度下降中選擇batch梯度下降法。
因為模型較為簡單,不使用如Momentum、RMSprop、Adam類似的用于加速訓練的復雜優化算法。
以上優化算法的具體介紹和如何選擇的經驗請參照另一篇博文:常見的優化算法
在此不過多贅述~
6. logistic回歸整體的公式推導
為了對logistic加深理解,且logistic模型及運算流程比較簡單,我們不使用tensorflow、pytorch等機器學習框架,因此除了logistic的正向計算,其反向傳播(BP)我們也進行手動推導及實現。
我們先進行logisitc的正向計算:
正向計算比較簡單,就是先進行多元線性回歸,然后使用sigmoid函數對線性回歸結果進行處理。
下面通過鏈式法則(這個還是可以會的哈)進行反向傳播:
因為我們使用batch梯度下降法,因此需要用到成本函數:
,這里的 代表所有樣本的數量。求導公式回顧:
因此,根據鏈式法則,首先求
:求解出
,進而求 :哈哈,最終結果是不是好簡單,繼續哈
求解
:同理,求解
:求得
,可以根據梯度下降的公式進行計算,即:以上就是logistic回歸的正向與反向推導,其實還是很簡單的,反向推導略微涉及一點數學導數知識,但是相信難不住我們。
其實我們可以以一個上帝的角度觀察正向與反向推導的過程,發現正向與反向通過成本函數,或者說損失函數連接起來,實際上確實如此,即使在很復雜的神經網絡系統中,損失函數的設計也是非常重要的,在后期我們使用tensorflow、pytorch等工具時,只需要進行提供正向的思路,以及損失函數,復雜的反向推導工具可以完全幫助我們實現,不過在學習初期,我們還是手動推倒一下比較好。
在下一部分,我們會使用python語言實現上述的推導過程,在推導過程中我們總是可以看見$sum$函數,在python預言實現時,我們可以使用向量化的技術巧妙計算,不僅可以減少代碼量,還能利用資源,并行執行節省時間。
7. 代碼實現
sigmoid函數:
要使用numpy哈,它是用Python進行科學計算的基本軟件包,我們的向量化離不開他,例如下面的函數,不僅可以以單個數作為輸入,還可以將向量、矩陣作為輸入。
def sigmoid(x):'''實現sigmoid函數:param x::return:'''a = 1 / (1 + np.exp(-x))return a獲取數據集:
def load_dataset():train_dataset = h5py.File('dataset/train_catvnoncat.h5', "r")train_set_x_orig = np.array(train_dataset["train_set_x"][:]) # your train set featurestrain_set_y_orig = np.array(train_dataset["train_set_y"][:]) # your train set labelstest_dataset = h5py.File('dataset/test_catvnoncat.h5',"r")test_set_x_orig = np.array(test_dataset["test_set_x"][:]) # your test set featurestest_set_y_orig = np.array(test_dataset["test_set_y"][:]) # your test set labelsclasses = np.array(test_dataset["list_classes"][:]) # the list of classestrain_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首先,我們根據h5py工具包獲取h5文件內的數據信息,其中train_set_x_orig的shape為(209, 64, 64, 3),209代表圖片數量,64代表64*64大小的圖片,3代表RGB三種顏色;其中train_set_y_orig的shape為(209,),代表每一個圖片的分類。
獲取到訓練集的x與y,按照同樣的方法獲取測試集的x與y。
classes代表分類的具體含義,內容為['non-cat' ,'cat']。
最后我們將訓練集與測試集的y改變形式,由(209,1)變為(1,209),代表每一列為一個樣本的y值。
處理數據集:
def process_data():# 加載數據集train_set_x_orig, train_set_y, test_set_x_orig, test_set_y, classes = load_dataset()# 分別獲取訓練集數量、測試集數量、訓練、測試集里面的圖片的寬度和高度(均為64x64)m_train = train_set_x_orig.shape[0]m_test = test_set_x_orig.shape[0]num_px = train_set_x_orig.shape[1]# 把維度為(64,64,3)的numpy數組重新構造為(64 x 64 x 3,1)的數組train_set_x_flatten = train_set_x_orig.reshape(train_set_x_orig.shape[0], -1).Ttest_set_x_flatten = test_set_x_orig.reshape(test_set_x_orig.shape[0], -1).T# 數據預處理,進行居中和標準化(歸一化)train_set_x = train_set_x_flatten / 255test_set_x = test_set_x_flatten / 255return train_set_x, test_set_x, train_set_y, test_set_y這里將訓練集和測試集的x進行shape轉換(y在之前的load_dataset已經做過了),這里使用的方法比較巧妙,首先轉換成(m,-1)的形式,-1代表自動求解-1位置的維度大小,然后進行轉置,即變成(features,m),m代表數據集的圖片數量。
初始化參數:
def initialize_with_zeros(dim):'''初始化參數:param dim::return:'''w = np.zeros((dim, 1))b = 0assert (w.shape == (dim, 1))assert (isinstance(b, float) or isinstance(b, int))return w, b正向、反向計算:
def propagate(w, b, X, Y):'''正向、反向計算:param w::param b::param X::param Y::return:'''m = X.shape[1]# 正向計算A = sigmoid(np.dot(w.T, X) + b)cost = -(1 / m) * np.sum(np.multiply(Y, np.log(A)) + np.multiply(1 - Y,np.log(1 - A)))# 反向傳播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 == ())# 保存相關參數grads = {"dw": dw,"db": db}return grads, cost使用梯度下降算法進行更新:
def optimize(w, b, X, Y, num_iterations, learning_rate,print_cost=False):'''使用梯度下降更新參數:param w::param b::param X::param Y::param num_iterations::param learning_rate::param print_cost::return:'''costs = []for i in range(num_iterations):grads, cost = propagate(w, b, X, Y)dw = grads["dw"]db = grads["db"]w = w - learning_rate * dwb = b - learning_rate * dbif i % 100 == 0:costs.append(cost)if print_cost and i % 100 == 0:print("Cost after iteration %i: %f" % (i, cost))params = {"w": w,"b": b}grads = {"dw": dw,"db": db}return params, grads, costs使用訓練好的模型進行預測:
def predict(w, b, X):'''使用模型預測:param w::param b::param X::return:'''m = X.shape[1]Y_prediction = np.zeros((1, m))w = w.reshape(X.shape[0], 1)A = 1 / (1 + np.exp(-(np.dot(w.T, X) + b)))for i in range(A.shape[1]):if A[0, i] > 0.5:Y_prediction[0, i] = 1else:Y_prediction[0, i] = 0assert (Y_prediction.shape == (1, m))return Y_prediction繪制圖:
def plt_cost(d):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()結果:
Cost after iteration 0: 0.693147 Cost after iteration 100: 0.584508 Cost after iteration 200: 0.466949 Cost after iteration 300: 0.376007 Cost after iteration 400: 0.331463 Cost after iteration 500: 0.303273 Cost after iteration 600: 0.279880 Cost after iteration 700: 0.260042 Cost after iteration 800: 0.242941 Cost after iteration 900: 0.228004 Cost after iteration 1000: 0.214820 Cost after iteration 1100: 0.203078 Cost after iteration 1200: 0.192544 Cost after iteration 1300: 0.183033 Cost after iteration 1400: 0.174399 Cost after iteration 1500: 0.166521 Cost after iteration 1600: 0.159305 Cost after iteration 1700: 0.152667 Cost after iteration 1800: 0.146542 Cost after iteration 1900: 0.140872 train accuracy: 99.04306220095694 % test accuracy: 70.0 %代碼地址:github源碼地址
github包含訓練集,以上代碼參考網上的實現,若有侵權,及時刪除~
8. 總結
logistic回歸嚴格意義上說并不屬于深度學習,因為他僅有一個隱藏層,而深度學習要求隱藏層數量大于等于2
但是logistic回歸卻是深度學習入門時很好的研究資料,通過后續的學習我們會明白,深度學習實在類似logistic回歸的基礎上一步步的增加復雜隱藏層的,但是無論怎樣變化,萬變不離其宗,因此,強烈建議手動推導并實現一次logistic回歸。
使用博客瀏覽效果更好哈:
金良的博客 | Jinliang Blog?jinliangxx.github.io總結
以上是生活随笔為你收集整理的python logistic回归_logistic回归介绍与源码分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 看漫画,学Linux内核!看完明白小企鹅
- 下一篇: 《精通Javascript+jQuery