邏輯回歸(Logistic Regression)
6.1 分類問題
參考文檔: 6 - 1 - Classification (8 min).mkv
在這個以及接下來的幾個視頻中,開始介紹分類問題。
在分類問題中,你要預測的變量 y 是離散的值,我們將學習一種叫做邏輯回歸 (Logistic Regression) 的算法,這是目前最流行使用最廣泛的一種學習算法。
在分類問題中,我們嘗試預測的是結果是否屬於某一個類(例如正確或錯誤)。分類問題的例子有:判斷一封電子郵件是否是垃圾郵件;判斷一次金融交易是否是欺詐;之前我們也談到了腫瘤分類問題的例子,區別一個腫瘤是惡性的還是良性的。
我們從二元的分類問題開始討論。
我們將因變量(dependent variable)可能屬於的兩個類分別稱為負向類(negative class)和正向類(positive class),則因變量 ,其中 0 表示負向類,1 表示正向類。
如果我們要用線性回歸算法來解決一個分類問題,對於分類, y 取值為 0 或者1,但如果你使用的是線性回歸,那么假設函數的輸出值可能遠大於 1,或者遠小於0,即使所有訓練樣本的標簽 y 都等於 0 或 1。盡管我們知道標簽應該取值0 或者1,但是如果算法得到的值遠大於1或者遠小於0的話,就會感覺很奇怪。所以我們在接下來的要研究的算法就叫做邏輯回歸算法,這個算法的性質是:它的輸出值永遠在0到 1 之間。
順便說一下,邏輯回歸算法是分類算法,我們將它作為分類算法使用。有時候可能因為這個算法的名字中出現了“回歸”使你感到困惑,但邏輯回歸算法實際上是一種分類算法,它適用於標簽 y 取值離散的情況,如:1 0 0 1。
在接下來的視頻中,我們將開始學習邏輯回歸算法的細節。
6.2 假說表示
參考視頻: 6 - 2 - Hypothesis Representation (7 min).mkv
在這段視頻中,我要給你展示假設函數的表達式,也就是說,在分類問題中,要用什么樣的函數來表示我們的假設。此前我們說過,希望我們的分類器的輸出值在0和1之間,因此,我們希望想出一個滿足某個性質的假設函數,這個性質是它的預測值要在0和1之間。
回顧在一開始提到的乳腺癌分類問題,我們可以用線性回歸的方法求出適合數據的一條直線:
根據線性回歸模型我們只能預測連續的值,然而對於分類問題,我們需要輸出0或1,我們可以預測:
當hθ (x)>=0.5時,預測 y=1。
當hθ (x)<0.5時,預測 y=0 。
對於上圖所示的數據,這樣的一個線性模型似乎能很好地完成分類任務。假使我們又觀測到一個非常大尺寸的惡性腫瘤,將其作為實例加入到我們的訓練集中來,這將使得我們獲得一條新的直線。
這時,再使用0.5作為閥值來預測腫瘤是良性還是惡性便不合適了。可以看出,線性回歸模型,因為其預測的值可以超越[0,1]的范圍,並不適合解決這樣的問題。
我們引入一個新的模型,邏輯回歸,該模型的輸出變量范圍始終在0和1之間。 邏輯回歸模型的假設是:其中: X 代表特征向量 g 代表邏輯函數(logistic function)是一個常用的邏輯函數為S形函數(Sigmoid function),公式為:
python代碼實現:
import numpy as np def sigmoid(z): return 1 / (1 + np.exp(-z))
該函數的圖像為:
合起來,我們得到邏輯回歸模型的假設:
對模型的理解:(另一個作用在於6.3中)
h_θ (x)的作用是,對於給定的輸入變量,根據選擇的參數計算輸出變量=1的可能性(estimated probablity)即h_θ (x)=P(y=1|x;θ)
例如,如果對於給定的x,通過已經確定的參數計算得出h_θ (x)=0.7,則表示有70%的幾率y為正向類,相應地y為負向類的幾率為1-0.7=0.3。
6.3 判定邊界
參考視頻: 6 - 3 - Decision Boundary (15 min).mkv
現在講下決策邊界(decision boundary)的概念。這個概念能更好地幫助我們理解邏輯回歸的假設函數在計算什么。
在邏輯回歸中,我們預測:
當h_θ (x)>=0.5時,預測 y=1。
當h_θ (x)<0.5時,預測 y=0 。
根據上面繪制出的 S 形函數圖像,我們知道當
z=0 時 g(z)=0.5
z>0 時 g(z)>0.5
z<0 時 g(z)<0.5
又 z=θ^T x ,即:
θ^T x>=0 時,預測 y=1
θ^T x<0 時,預測 y=0 (此處為最簡單的模型)
現在假設我們有一個模型:
並且參數θ 是向量[-3 1 1]。 則當-3+x_1+x_2≥0,即x_1+x_2≥3時,模型將預測 y=1。 我們可以繪制直線x_1+x_2=3,這條線便是我們模型的分界線,將預測為1的區域和預測為 0的區域分隔開。
假使我們的數據呈現這樣的分布情況,怎樣的模型才能適合呢?
因為需要用曲線才能分隔 y=0 的區域和 y=1 的區域,我們需要二次方特征: 是[-1 0 0 1 1],則我們得到的判定邊界恰好是圓點在原點且半徑為1的圓形。
我們可以用非常復雜的模型來適應非常復雜形狀的判定邊界。
6.4 代價函數
參考視頻: 6 - 4 - Cost Function (11 min).mkv
在這段視頻中,我們要介紹如何擬合邏輯回歸模型的參數θ。具體來說,我要定義用來擬合參數的優化目標或者叫代價函數,這便是監督學習問題中的邏輯回歸模型的擬合問題。
對於線性回歸模型,我們定義的代價函數是所有模型誤差的平方和。理論上來說,我們也可以對邏輯回歸模型沿用這個定義,但是問題在於,當我們將帶入到這樣定義了的代價函數中時,我們得到的代價函數將是一個非凸函數(non-convexfunction)。
這意味着我們的代價函數有許多局部最小值,這將影響梯度下降算法尋找全局最小值。
線性回歸的代價函數為:
我們重新定義邏輯回歸的代價函數為:,其中
(對於該式本質上是最大似然求得)
h_θ (x)與 Cost(h_θ (x),y)之間的關系如下圖所示:
這樣構建的Cost(h_θ (x),y)函數的特點是:當實際的 y=1 且h_θ (x)也為 1 時誤差為 0,當 y=1 但h_θ (x)不為1時誤差隨着h_θ (x)變小而變大;當實際的 y=0 且h_θ (x)也為 0 時代價為 0,當y=0 但h_θ (x)不為 0時誤差隨着 h_θ (x)的變大而變大。
將構建的 Cost(h_θ (x),y)簡化如下:
帶入代價函數得到:
Python代碼實現:
import numpy as np def cost(theta, X, y): theta = np.matrix(theta) X = np.matrix(X) y = np.matrix(y) first = np.multiply(-y, np.log(sigmoid(X* theta.T))) # sigmoid即邏輯斯諦模型 second = np.multiply((1 - y), np.log(1 - sigmoid(X* theta.T))) return np.sum(first - second) / (len(X))
在得到這樣一個代價函數以后,我們便可以用梯度下降算法來求得能使代價函數最小的參數了。算法為:
求導后得到:
在這個視頻中,我們定義了單訓練樣本的代價函數,凸性分析的內容是超出這門課的范圍的,但是可以證明我們所選的代價值函數會給我們一個凸優化問題。代價函數J(θ)會是一個凸函數,並且沒有局部最優值。
推導過程:
注:雖然得到的梯度下降算法表面上看上去與線性回歸的梯度下降算法一樣,但是這里的與線性回歸中不同,所以實際上是不一樣的。另外,在運行梯度下降算法之前,進行特征縮放依舊是非常必要的。
一些梯度下降算法之外的選擇: 除了梯度下降算法以外,還有一些常被用來令代價函數最小的算法,這些算法更加復雜和優越,而且通常不需要人工選擇學習率,通常比梯度下降算法要更加快速。這些算法有:共軛梯度(Conjugate Gradient),局部優化法(Broyden fletcher goldfarb shann,BFGS)和有限內存局部優化法(LBFGS) ,fminunc是 matlab和octave 中都帶的一個最小值優化函數,使用時我們需要提供代價函數和每個參數的求導,下面是 octave 中使用 fminunc 函數的代碼示例:
function [jVal, gradient] = costFunction(theta)
jVal = [...code to compute J(theta)...];
gradient = [...code to compute derivative of J(theta)...];
end
options = optimset('GradObj', 'on', 'MaxIter', '100');
initialTheta = zeros(2,1);
[optTheta, functionVal, exitFlag] = fminunc(@costFunction, initialTheta, options);
在下一個視頻中,我們會把單訓練樣本的代價函數的這些理念進一步發展,然后給出整個訓練集的代價函數的定義,我們還會找到一種比我們目前用的更簡單的寫法,基於這些推導出的結果,我們將應用梯度下降法得到我們的邏輯回歸算法。
6.5 簡化的成本函數和梯度下降
參考視頻: 6 - 5 - Simplified Cost Function and Gradient Descent (10 min).mkv
在這段視頻中,我們將會找出一種稍微簡單一點的方法來寫代價函數,來替換我們現在用的方法。同時我們還要弄清楚如何運用梯度下降法,來擬合出邏輯回歸的參數。因此,聽了這節課,你就應該知道如何實現一個完整的邏輯回歸算法。
這就是邏輯回歸的代價函數:
這個式子可以合並成:
即,邏輯回歸的代價函數:
根據這個代價函數,為了擬合出參數,該怎么做呢?我們要試圖找盡量讓J(θ) 取得最小值的參數θ。
所以我們想要盡量減小這一項,這將我們將得到某個參數θ。
如果我們給出一個新的樣本,假如某個特征 x,我們可以用擬合訓練樣本的參數θ,來輸出對假設的預測。
另外,我們假設的輸出,實際上就是這個概率值:p(y=1|x;θ),就是關於 x以θ為參數,y=1 的概率,你可以認為我們的假設就是估計 y=1 的概率,所以,接下來就是弄清楚如何最大限度地最小化代價函數J(θ),作為一個關於θ的函數,這樣我們才能為訓練集擬合出參數θ。
最小化代價函數的方法,是使用梯度下降法(gradient descent)。這是我們的代價函數:
如果我們要最小化這個關於θ的函數值,這就是我們通常用的梯度下降法的模板。
我們要反復更新每個參數,用這個式子來更新,就是用它自己減去學習率 α 乘以后面的微分項。求導后得到:
現在,如果你把這個更新規則和我們之前用在線性回歸上的進行比較的話,你會驚訝地發現,這個式子正是我們用來做線性回歸梯度下降的。
那么,線性回歸和邏輯回歸是同一個算法嗎?要回答這個問題,我們要觀察邏輯回歸看看發生了哪些變化。實際上,假設的定義發生了變化。
對於線性回歸假設函數:
而現在邏輯函數假設函數:
因此,即使更新參數的規則看起來基本相同,但由於假設的定義發生了變化,所以邏輯函數的梯度下降,跟線性回歸的梯度下降實際上是兩個完全不同的東西。
在先前的視頻中,當我們在談論線性回歸的梯度下降法時,我們談到了如何監控梯度下降法以確保其收斂,我通常也把同樣的方法用在邏輯回歸中,來監測梯度下降,以確保它正常收斂。
當使用梯度下降法來實現邏輯回歸時,我們有這些不同的參數θ,就是θ_0 θ_1 θ_2 一直到θ_n,我們需要用這個表達式來更新這些參數。我們還可以使用 for循環來更新這些參數值,用 for i=1 to n,或者 for i=1 to n+1。當然,不用 for循環也是可以的,理想情況下,我們更提倡使用向量化的實現,可以把所有這些 n個參數同時更新。
最后還有一點,我們之前在談線性回歸時講到的特征縮放,我們看到了特征縮放是如何提高梯度下降的收斂速度的,這個特征縮放的方法,也適用於邏輯回歸。如果你的特征范圍差距很大的話,那么應用特征縮放的方法,同樣也可以讓邏輯回歸中,梯度下降收斂更快。
就是這樣,現在你知道如何實現邏輯回歸,這是一種非常強大,甚至可能世界上使用最廣泛的一種分類算法。
6.6 高級優化
參考視頻: 6 - 6 - Advanced Optimization (14 min).mkv
在上一個視頻中,我們討論了用梯度下降的方法最小化邏輯回歸中代價函數J(θ)。在本次視頻中,我會教你們一些高級優化算法和一些高級的優化概念,利用這些方法,我們就能夠使通過梯度下降,進行邏輯回歸的速度大大提高,而這也將使算法更加適合解決大型的機器學習問題,比如,我們有數目龐大的特征量。 現在我們換個角度來看什么是梯度下降,我們有個代價函數J(θ),而我們想要使其最小化,那么我們需要做的是編寫代碼,當輸入參數 θ 時,它們會計算出兩樣東西:J(θ) 以及J 等於 0、1直到 n 時的偏導數項。
假設我們已經完成了可以實現這兩件事的代碼,那么梯度下降所做的就是反復執行這些更新。
另一種考慮梯度下降的思路是:我們需要寫出代碼來計算J(θ) 和這些偏導數,然后把這些插入到梯度下降中,然后它就可以為我們最小化這個函數。
對於梯度下降來說,我認為從技術上講,你實際並不需要編寫代碼來計算代價函數J(θ)。你只需要編寫代碼來計算導數項,但是,如果你希望代碼還要能夠監控這些J(θ) 的收斂性(調整學習率),那么我們就需要自己編寫代碼來計算代價函數J(θ)和偏導數項。所以,在寫完能夠計算這兩者的代碼之后,我們就可以使用梯度下降。
然而梯度下降並不是我們可以使用的唯一算法,還有其他一些算法,更高級、更復雜。如果我們能用這些方法來計算代價函數J(θ)和偏導數項
兩個項的話,那么這些算法就是為我們優化代價函數的不同方法,共軛梯度法 BFGS (變尺度法) 和L-BFGS (限制變尺度法) 就是其中一些更高級的優化算法,它們需要有一種方法來計算 J(θ),以及需要一種方法計算導數項,然后使用比梯度下降更復雜的算法來最小化代價函數。這三種算法的具體細節超出了本門課程的范疇。實際上你最后通常會花費很多天,或幾周時間研究這些算法,你可以專門學一門課來提高數值計算能力,不過讓我來告訴你他們的一些特性:
這三種算法有許多優點:
一個是使用這其中任何一個算法,你通常不需要手動選擇學習率 α,所以對於這些算法的一種思路是,給出計算導數項和代價函數的方法,你可以認為算法有一個智能的內部循環,而且,事實上,他們確實有一個智能的內部循環,稱為線性搜索(line search)算法,它可以自動嘗試不同的學習速率 α,並自動選擇一個好的學習速率 a,因此它甚至可以為每次迭代選擇不同的學習速率,那么你就不需要自己選擇。這些算法實際上在做更復雜的事情,而不僅僅是選擇一個好的學習率,所以它們往往最終收斂得遠遠快於梯度下降,不過關於它們到底做什么的詳細討論,已經超過了本門課程的范圍。
實際上,我過去使用這些算法已經很長一段時間了,也許超過十年了,使用得相當頻繁,而直到幾年前我才真正搞清楚共軛梯度法 BFGS 和 L-BFGS的細節。
我們實際上完全有可能成功使用這些算法,並應用於許多不同的學習問題,而不需要真正理解這些算法的內環間在做什么,如果說這些算法有缺點的話,那么我想說主要缺點是它們比梯度下降法復雜多了,特別是你最好不要使用 L-BGFS、BFGS這些算法,除非你是數值計算方面的專家。實際上,我不會建議你們編寫自己的代碼來計算數據的平方根,或者計算逆矩陣,因為對於這些算法,我還是會建議你直接使用一個軟件庫,比如說,要求一個平方根,我們所能做的就是調用一些別人已經寫好用來計算數字平方根的函數。幸運的是現在我們有Octave 和與它密切相關的 MATLAB 語言可以使用。
Octave 有一個非常理想的庫用於實現這些先進的優化算法,所以,如果你直接調用它自帶的庫,你就能得到不錯的結果。我必須指出這些算法實現得好或不好是有區別的,因此,如果你正在你的機器學習程序中使用一種不同的語言,比如如果你正在使用C、C++、Java等等,你可能會想嘗試一些不同的庫,以確保你找到一個能很好實現這些算法的庫。因為在L-BFGS或者等高線梯度的實現上,表現得好與不太好是有差別的,因此現在讓我們來說明:如何使用這些算法:
比方說,你有一個含兩個參數的問題,這兩個參數是θ_0和θ_1,因此,通過這個代價函數,你可以得到θ_1和 θ_2的值,如果你將J(θ) 最小化的話,那么它的最小值將是θ_1=5 ,θ_2=5。代價函數J(θ)的導數推出來就是這兩個表達式:
如果我們不知道最小值,但你想要代價函數找到這個最小值,是用比如梯度下降這些算法,但最好是用比它更高級的算法,你要做的就是運行一個像這樣的Octave 函數:
function [jVal, gradient]=costFunction(theta)
jVal=(theta(1)-5)^2+(theta(2)-5)^2;
gradient=zeros(2,1);
gradient(1)=2*(theta(1)-5);
gradient(2)=2*(theta(2)-5);
end
這樣就計算出這個代價函數,函數返回的第二個值是梯度值,梯度值應該是一個2×1的向量,梯度向量的兩個元素對應這里的兩個偏導數項,運行這個costFunction 函數后,你就可以調用高級的優化函數,這個函數叫 fminunc,它表示Octave 里無約束最小化函數。調用它的方式如下:
options=optimset('GradObj','on','MaxIter',100);
initialTheta=zeros(2,1);
[optTheta, functionVal, exitFlag]=fminunc(@costFunction, initialTheta, options);
你要設置幾個options,這個 options 變量作為一個數據結構可以存儲你想要的options,所以 GradObj 和On,這里設置梯度目標參數為打開(on),這意味着你現在確實要給這個算法提供一個梯度,然后設置最大迭代次數,比方說100,我們給出一個θ 的猜測初始值,它是一個2×1的向量,那么這個命令就調用fminunc,這個@符號表示指向我們剛剛定義的costFunction 函數的指針。如果你調用它,它就會使用眾多高級優化算法中的一個,當然你也可以把它當成梯度下降,只不過它能自動選擇學習速率α,你不需要自己來做。然后它會嘗試使用這些高級的優化算法,就像加強版的梯度下降法,為你找到最佳的θ值。
讓我告訴你它在 Octave 里什么樣:
所以我寫了這個關於theta的 costFunction 函數,它計算出代價函數 jval以及梯度gradient,gradient 有兩個元素,是代價函數對於theta(1) 和 theta(2)這兩個參數的偏導數。
我希望你們從這個幻燈片中學到的主要內容是:寫一個函數,它能返回代價函數值、梯度值,因此要把這個應用到邏輯回歸,或者甚至線性回歸中,你也可以把這些優化算法用於線性回歸,你需要做的就是輸入合適的代碼來計算這里的這些東西。
現在你已經知道如何使用這些高級的優化算法,有了這些算法,你就可以使用一個復雜的優化庫,它讓算法使用起來更模糊一點。因此也許稍微有點難調試,不過由於這些算法的運行速度通常遠遠超過梯度下降。
所以當我有一個很大的機器學習問題時,我會選擇這些高級算法,而不是梯度下降。有了這些概念,你就應該能將邏輯回歸和線性回歸應用於更大的問題中,這就是高級優化的概念。
在下一個視頻,我想要告訴你如何修改你已經知道的邏輯回歸算法,然后使它在多類別分類問題中也能正常運行。
6.7 多類別分類:一對多
參考視頻: 6 - 7 - Multiclass Classification_ One-vs-all (6 min).mkv
在本節視頻中,我們將談到如何使用邏輯回歸 (logistic regression)來解決多類別分類問題,具體來說,我想通過一個叫做"一對多" (one-vs-all) 的分類算法。
先看這樣一些例子。
第一個例子:假如說你現在需要一個學習算法能自動地將郵件歸類到不同的文件夾里,或者說可以自動地加上標簽,那么,你也許需要一些不同的文件夾,或者不同的標簽來完成這件事,來區分開來自工作的郵件、來自朋友的郵件、來自家人的郵件或者是有關興趣愛好的郵件,那么,我們就有了這樣一個分類問題:其類別有四個,分別用y=1、y=2、y=3、y=4 來代表。
第二個例子是有關葯物診斷的,如果一個病人因為鼻塞來到你的診所,他可能並沒有生病,用 y=1 這個類別來代表;或者患了感冒,用 y=2 來代表;或者得了流感用y=3來代表。
第三個例子:如果你正在做有關天氣的機器學習分類問題,那么你可能想要區分哪些天是晴天、多雲、雨天、或者下雪天,對上述所有的例子,y 可以取一個很小的數值,一個相對"謹慎"的數值,比如1 到3、1到4或者其它數值,以上說的都是多類分類問題,順便一提的是,對於下標是0 1 2 3,還是 1 2 3 4 都不重要,我更喜歡將分類從 1 開始標而不是0,其實怎樣標注都不會影響最后的結果。
然而對於之前的一個,二元分類問題,我們的數據看起來可能是像這樣:
對於一個多類分類問題,我們的數據集或許看起來像這樣:
我用3種不同的符號來代表3個類別,問題就是給出3個類型的數據集,我們如何得到一個學習算法來進行分類呢?
我們現在已經知道如何進行二元分類,可以使用邏輯回歸,對於直線或許你也知道,可以將數據集一分為二為正類和負類。用一對多的分類思想,我們可以將其用在多類分類問題上。
下面將介紹如何進行一對多的分類工作,有時這個方法也被稱為"一對余"方法。
現在我們有一個訓練集,好比上圖表示的有3個類別,我們用三角形表示 y=1,方框表示y=2,叉叉表示 y=3。我們下面要做的就是使用一個訓練集,將其分成3個二元分類問題。
我們先從用三角形代表的類別1開始,實際上我們可以創建一個,新的"偽"訓練集,類型2和類型3定為負類,類型1設定為正類,我們創建一個新的訓練集,如下圖所示的那樣,我們要擬合出一個合適的分類器。
這里的三角形是正樣本,而圓形代表負樣本。可以這樣想,設置三角形的值為1,圓形的值為0,下面我們來訓練一個標准的邏輯回歸分類器,這樣我們就得到一個正邊界。
為了能實現這樣的轉變,我們將多個類中的一個類標記為正向類(y=1),然后將其他所有類都標記為負向類,這個模型記作
。接着,類似地第我們選擇另一個類標記為正向類(y=2),再將其它類都標記為負向類,將這個模型記作 h_θ^((2) ) (x),依此類推。
最后我們得到一系列的模型簡記為: 其中:i=(1,2,3....k)
最后,在我們需要做預測時,我們將所有的分類機都運行一遍,然后對每一個輸入變量,都選擇最高可能性的輸出變量。
總之,我們已經把要做的做完了,現在要做的就是訓練這個邏輯回歸分類器:, 其中 i 對應每一個可能的 y=i,最后,為了做出預測,我們給出輸入一個新的 x 值,用這個做預測。我們要做的就是在我們三個分類器里面輸入 x,然后我們選擇一個讓
最大的i,即
。
你現在知道了基本的挑選分類器的方法,選擇出哪一個分類器是可信度最高效果最好的,那么就可認為得到一個正確的分類,無論i值是多少,我們都有最高的概率值,我們預測y就是那個值。這就是多類別分類問題,以及一對多的方法,通過這個小方法,你現在也可以將邏輯回歸分類器用在多類分類的問題上。
代碼
寫的賊垃圾,懶...
import numpy as np import matplotlib.pyplot as plt class logistic(): def __init__(self, alpha = 0.01, maxCycles=500, filename='C:/Users/shishenhao/Desktop/tt.txt'): self.alpha = alpha #學習率 self.maxCycles = maxCycles #迭代次數 self.filename = filename #數據地址 self.dataMat=[] #樣本數據 self.labelMat=[] #標簽數據 self.loadDataSet() def loadDataSet(self): #讀取數據 ''' 對於filename,每行前兩個值分別是X1和X2,第三個值數據對應的類別標簽 而且為了設置方便,該函數還將X0的值設置為1.0 ''' fr = open(self.filename) for line in fr.readlines(): lineArr = line.strip().split() self.dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])]) self.labelMat.append(int(lineArr[2])) def sigmoid(self, z): #模型h return 1.0/(1+np.exp(-z)) def gradAscent(self): #梯度上升求最優參數 dataMatrix = np.mat(self.dataMat) #矩陣化 labelMatrix = np.mat(self.labelMat).transpose() #矩陣化 n = np.shape(dataMatrix)[1] weight = np.ones((n,1)) #設置初始的參數,並都賦默認值為1。 for i in range(self.maxCycles): h = self.sigmoid(dataMatrix*weight) #帶入模型 error = labelMatrix - h #不進行絕對值操作,所以之后用+ weight = weight + self.alpha * dataMatrix.transpose() * error #更新參數 return weight # 隨機梯度上升算法 def stocGradAscent0(self): dataMatrix = np.mat(self.dataMat) # 矩陣化 labelMatrix = np.mat(self.labelMat).transpose() # 矩陣化 m, n = np.shape(dataMatrix) weights = np.ones((n,1)) for k in range(self.maxCycles): for i in range(m): h = self.sigmoid(np.sum(dataMatrix[i] * weights)) error = labelMatrix[i] - h weights = weights + self.alpha * dataMatrix[i].transpose() * error return weights # 改進的隨機梯度上升算法 def stocGradAscent1(self): dataMatrix = np.mat(self.dataMat) # 矩陣化 labelMatrix = np.mat(self.labelMat).transpose() # 矩陣化 m, n = np.shape(dataMatrix) weights = np.ones((n,1)) alpha = self.alpha for j in range(self.maxCycles): dataIndex = list(range(m)) for i in range(m): alpha = 4 / (1.0 + j + i) + 0.001#隨迭代次數增加,權重變化越小。 randIndex = int(np.random.uniform(0, len(dataIndex)))#隨機抽樣 h = self.sigmoid(sum(dataMatrix[randIndex] * weights)) error = labelMatrix[randIndex] - h weights = weights + alpha * dataMatrix[randIndex].transpose() * error del (dataIndex[randIndex]) #去除已經抽取的樣本 return weights def plotBestFit(self): wei = self.gradAscent() weights = wei.getA() #將matrix轉換成array dataArr = np.array(self.dataMat) n = np.shape(dataArr)[0] xcord1, ycord1, xcord2, ycord2 = [], [], [], [] for i in range(n): if int(self.labelMat[i]) == 1: xcord1.append(dataArr[i, 1]) ycord1.append(dataArr[i, 2]) else: xcord2.append(dataArr[i, 1]) ycord2.append(dataArr[i, 2]) fig = plt.figure() ax = fig.add_subplot(111) ax.scatter(xcord1, ycord1, s=30, c='red', marker='s') ax.scatter(xcord2, ycord2, s=30, c='green') x = np.arange(-3.0, 3.0, 0.1) y = (-weights[0] - weights[1] * x) / weights[2] ax.plot(x, y) plt.xlabel('X1') plt.ylabel('X2') plt.show() log = logistic() log.plotBestFit()
結果:
傳統梯度上升500次迭代
1次隨機梯度上升
迭代500次隨機梯度上升
改進梯度上升迭代500
參考資料
https://github.com/fengdu78/Coursera-ML-AndrewNg-Notes
https://www.cnblogs.com/wj-1314/p/10181876.html
https://blog.csdn.net/csqazwsxedc/article/details/69690655