1. 程式人生 > >機器學習演算法與Python實踐之(七)邏輯迴歸(Logistic Regression)

機器學習演算法與Python實踐之(七)邏輯迴歸(Logistic Regression)

Logistic regression (邏輯迴歸)是當前業界比較常用的機器學習方法,用於估計某種事物的可能性。比如某使用者購買某商品的可能性,某病人患有某種疾病的可能性,以及某廣告被使用者點選的可能性等。(注意這裡是:“可能性”,而非數學上的“概率”,logisitc迴歸的結果並非數學定義中的概率值,不可以直接當做概率值來用。該結果往往用於和其他特徵值加權求和,而非直接相乘)

  那麼它究竟是什麼樣的一個東西,又有哪些適用情況和不適用情況呢?

一. 官方定義:

  Figure 1. The logistic function, with z on the horizontal axis and ƒ

(z) on the vertical axis

  邏輯迴歸是一個學習f:X− > Y 方程或者P(Y|X)的方法,這裡Y是離散取值的,X= < X1,X2…,Xn > 是任意一個向量其中每個變數離散或者連續取值。

二. 我的解釋

  只看公式太痛苦了,分開說一下就好。Logistic Regression 有三個主要組成部分:迴歸、線性迴歸、Logsitic方程。

  1)迴歸

  Logistic Regression 是線性迴歸的一種,線性迴歸是一種迴歸。那麼迴歸是什麼呢?

  迴歸其實就是對已知公式的未知引數進行估計。大家可以簡單的理解為,在給定訓練樣本點和已知的公式後,對於一個或多個未知引數,機器會自動列舉引數的所有可能取值(對於多個引數要列舉它們的不同組合),直到找到那個最符合樣本點分佈的引數(或引數組合)。(當然,實際運算有一些優化演算法,肯定不會去列舉的)

  注意,迴歸的前提是公式已知,否則迴歸無法進行。而現實生活中哪裡有已知的公式啊(G=m*g 也是牛頓被蘋果砸了腦袋之後碰巧想出來的不是?哈哈),因此迴歸中的公式基本都是資料分析人員通過看大量資料後猜測的(其實大多數是拍腦袋想出來的,嗯…)。根據這些公式的不同,迴歸分為線性迴歸和非線性迴歸。線性迴歸中公式都是“一次”的(一元一次方程,二元一次方程…),而非線性則可以有各種形式(N元N次方程,log方程 等等)。具體的例子線上性迴歸中介紹吧。

  2)線性迴歸

  直接來一個最簡單的一元變數的例子:假設要找一個 y 和 x 之間的規律,其中 x 是鞋子價錢,y 是鞋子的銷售量。(為什麼要找這個規律呢?這樣的話可以幫助定價來賺更多的錢嘛,小學的應用題經常做的呵呵)。已知一些往年的銷售資料(x0,y0), (x1, y1), … (xn, yn)做樣本集, 並假設它們滿足線性關係:y = a*x + b (其中a,b的具體取值還不確定),線性迴歸即根據往年資料找出最佳的 a, b 取值,使 y = a * x + b 在所有樣本集上誤差最小。

  也許你會覺得…..暈!這麼簡單! 這需要哪門子的迴歸呀!我自己在草紙上畫個xy座標系,點幾個點就能畫出來!(好吧,我承認我們初中時都被這樣的畫圖題折磨過)。事實上一元變數的確很直觀,但如果是多元就難以直觀的看出來了。比如說除了鞋子的價格外,鞋子的質量,廣告的投入,店鋪所在街區的人流量都會影響銷量,我們想得到這樣的公式:sell = a*x + b*y + c*z + d*zz + e。這個時候畫圖就畫不出來了,規律也十分難找,那麼交給線性迴歸去做就好。(線性迴歸具體是怎麼做的請參考相應文獻,都是一些數學公式,對程式設計師來說,我們就把它當成一條程式命令就好)。這就是線性迴歸演算法的價值。

  需要注意的是,這裡線性迴歸能過獲得好效果的前提是y = a*x + b 至少從總體上是有道理的(因為我們認為鞋子越貴,賣的數量越少,越便宜賣的越多。另外鞋子質量、廣告投入、客流量等都有類似規律);但並不是所有型別的變數都適合用線性迴歸,比如說x不是鞋子的價格,而是鞋子的尺碼),那麼無論迴歸出什麼樣的(a,b),錯誤率都會極高(因為事實上尺碼太大或尺碼太小都會減少銷量)。總之:如果我們的公式假設是錯的,任何迴歸都得不到好結果。

  3)Logistic方程

  上面我們的sell是一個具體的實數值,然而很多情況下,我們需要回歸產生一個類似概率值的0~1之間的數值(比如某一雙鞋子今天能否賣出去?或者某一個廣告能否被使用者點選? 我們希望得到這個數值來幫助決策鞋子上不上架,以及廣告展不展示)。這個數值必須是0~1之間,但sell顯然不滿足這個區間要求。於是引入了Logistic方程,來做歸一化。這裡再次說明,該數值並不是數學中定義的概率值。那麼既然得到的並不是概率值,為什麼我們還要費這個勁把數值歸一化為0~1之間呢?歸一化的好處在於數值具備可比性和收斂的邊界,這樣當你在其上繼續運算時(比如你不僅僅是關心鞋子的銷量,而是要對鞋子賣出的可能、當地治安情況、當地運輸成本 等多個要素之間加權求和,用綜合的加和結果決策是否在此地開鞋店時),歸一化能夠保證此次得到的結果不會因為邊界 太大/太小 導致 覆蓋其他feature 或 被其他feature覆蓋。(舉個極端的例子,如果鞋子銷量最低為100,但最好時能賣無限多個,而當地治安狀況是用0~1之間的數值表述的,如果兩者直接求和治安狀況就完全被忽略了)這是用logistic迴歸而非直接線性迴歸的主要原因。到了這裡,也許你已經開始意識到,沒錯,Logistic Regression 就是一個被 Logistic 方程歸一化後的線性迴歸,僅此而已。

  至於所以用 Logistic 而不用其它,是因為這種歸一化的方法往往比較合理(人家都說自己叫Logistic 了嘛,呵呵),能夠打壓過大和過小的結果(往往是噪音),以保證主流的結果不至於被忽視。具體的公式及圖形見本文的一、官方定義部分。其中 f(X) 就是我們上面例子中的 sell 的實數值了,而 y 就是得到的0~1之間的賣出可能性數值了。

三. Logistic Regression的適用性

  1) 可用於概率預測,也可用於分類。

  並不是所有的機器學習方法都可以做可能性概率預測(比如 SVM 就不行,它只能得到1或者-1)。可能性預測的好處是結果又可比性:比如我們得到不同廣告被點選的可能性後,就可以展現點選可能性最大的 N 個。這樣以來,哪怕得到的可能性都很高,或者可能性都很低,我們都能取最優的topN。當用於分類問題時,僅需要設定一個閾值即可,可能性高於閾值是一類,低於閾值是另一類。

  2) 僅能用於線性問題

  只有在 feature 和 target 是線性關係時,才能用 Logistic Regression(不像SVM那樣可以應對非線性問題)。這有兩點指導意義,一方面當預先知道模型非線性時,果斷不使用 Logistic Regression; 另一方面,在使用 Logistic Regression 時注意選擇和 target 呈線性關係的 feature。

  3) 各feature之間不需要滿足條件獨立假設,但各個feature的貢獻是獨立計算的。

  邏輯迴歸不像樸素貝葉斯一樣需要滿足條件獨立假設(因為它沒有求後驗概率)。但每個feature的貢獻是獨立計算的,即LR是不會自動幫你 combine 不同的 features 產生新 feature 的 (時刻不能抱有這種幻想,那是決策樹,LSA,pLSA,LDA或者你自己要乾的事情)。舉個例子,如果你需要TF*IDF這樣的feature,就必須明確的給出來,若僅僅分別給出兩維 TF 和 IDF 是不夠的,那樣只會得到類似 a*TF + b*IDF 的結果,而不會有 c*TF*IDF 的效果。

 

機器學習演算法與Python實踐這個系列主要是參考《機器學習實戰》這本書。因為自己想學習Python,然後也想對一些機器學習演算法加深下了解,所以就想通過Python來實現幾個比較常用的機器學習演算法。恰好遇見這本同樣定位的書籍,所以就參考這本書的過程來學習了。

       這節學習的是邏輯迴歸(Logistic Regression),也算進入了比較正統的機器學習演算法。啥叫正統呢?我概念裡面機器學習演算法一般是這樣一個步驟:

1)對於一個問題,我們用數學語言來描述它,然後建立一個模型,例如迴歸模型或者分類模型等來描述這個問題;

2)通過最大似然、最大後驗概率或者最小化分類誤差等等建立模型的代價函式,也就是一個最優化問題。找到最優化問題的解,也就是能擬合我們的資料的最好的模型引數;

3)然後我們需要求解這個代價函式,找到最優解。這求解也就分很多種情況了:

      a)如果這個優化函式存在解析解。例如我們求最值一般是對代價函式求導,找到導數為0的點,也就是最大值或者最小值的地方了。如果代價函式能簡單求導,並且求導後為0的式子存在解析解,那麼我們就可以直接得到最優的引數了。

      b)如果式子很難求導,例如函式裡面存在隱含的變數或者變數相互間存在耦合,也就互相依賴的情況。或者求導後式子得不到解釋解,例如未知引數的個數大於已知方程組的個數等。這時候我們就需要藉助迭代演算法來一步一步找到最有解了。迭代是個很神奇的東西,它將遠大的目標(也就是找到最優的解,例如爬上山頂)記在心上,然後給自己定個短期目標(也就是每走一步,就離遠大的目標更近一點),腳踏實地,心無旁貸,像個蝸牛一樣,一步一步往上爬,支撐它的唯一信念是:只要我每一步都爬高一點,那麼積跬步,肯定能達到自己人生的巔峰,盡享山登絕頂我為峰的豪邁與忘我。

       另外需要考慮的情況是,如果代價函式是凸函式,那麼就存在全域性最優解,方圓五百里就只有一個山峰,那命中註定了,它就是你要找的唯一了。但如果是非凸的,那麼就會有很多區域性最優的解,有一望無際的山峰,人的視野是偉大的也是渺小的,你不知道哪個山峰才是最高的,可能你會被命運作弄,很無辜的陷入一個區域性最優裡面,坐井觀天,以為自己找到的就是最好的。沒想到山外有山,人外有人,光芒總在未知的遠處默默綻放。但也許命運眷戀善良的你,帶給你的總是最好的歸宿。也有很多不信命的人,覺得人定勝天的人,誓要找到最好的,否則不會罷休,永不向命運妥協,除非自己有一天累了,倒下了,也要靠剩下的一口氣,邁出一口氣能支撐的路程。好悲涼啊……哈哈。

        呃,不知道扯那去了,也不知道自己說的有沒有錯,有錯的話請大家不吝指正。那下面就進入正題吧。正如上面所述,邏輯迴歸就是這樣的一個過程:面對一個迴歸或者分類問題,建立代價函式,然後通過優化方法迭代求解出最優的模型引數,然後測試驗證我們這個求解的模型的好壞,冥冥人海,滾滾紅塵,我們是否找到了最適合的那個她。

一、邏輯迴歸(LogisticRegression)

       Logistic regression (邏輯迴歸)是當前業界比較常用的機器學習方法,用於估計某種事物的可能性。之前在經典之作《數學之美》中也看到了它用於廣告預測,也就是根據某廣告被使用者點選的可能性,把最可能被使用者點選的廣告擺在使用者能看到的地方,然後叫他“你點我啊!”使用者點了,你就有錢收了。這就是為什麼我們的電腦現在廣告氾濫的原因了。

       還有類似的某使用者購買某商品的可能性,某病人患有某種疾病的可能性啊等等。這個世界是隨機的(當然了,人為的確定性系統除外,但也有可能有噪聲或產生錯誤的結果,只是這個錯誤發生的可能性太小了,小到千萬年不遇,小到忽略不計而已),所以萬物的發生都可以用可能性或者機率(Odds)來表達。“機率”指的是某事物發生的可能性與不發生的可能性的比值。

       Logistic regression可以用來回歸,也可以用來分類,主要是二分類。還記得上幾節講的支援向量機SVM嗎?它就是個二分類的例如,它可以將兩個不同類別的樣本給分開,思想是找到最能區分它們的那個分類超平面。但當你給一個新的樣本給它,它能夠給你的只有一個答案,你這個樣本是正類還是負類。例如你問SVM,某個女生是否喜歡你,它只會回答你喜歡或者不喜歡。這對我們來說,顯得太粗魯了,要不希望,要不絕望,這都不利於身心健康。那如果它可以告訴我,她很喜歡、有一點喜歡、不怎麼喜歡或者一點都不喜歡,你想都不用想了等等,告訴你她有49%的機率喜歡你,總比直接說她不喜歡你,來得溫柔。而且還提供了額外的資訊,她來到你的身邊你有多少希望,你得再努力多少倍,知己知彼百戰百勝,哈哈。Logistic regression就是這麼溫柔的,它給我們提供的就是你的這個樣本屬於正類的可能性是多少。

       還得來點數學。(更多的理解,請參閱參考文獻)假設我們的樣本是{x, y},y是0或者1,表示正類或者負類,x是我們的m維的樣本特徵向量。那麼這個樣本x屬於正類,也就是y=1的“概率”可以通過下面的邏輯函式來表示:

       這裡θ是模型引數,也就是迴歸係數,σ是sigmoid函式。實際上這個函式是由下面的對數機率(也就是x屬於正類的可能性和負類的可能性的比值的對數)變換得到的:

       換句話說,y也就是我們關係的變數,例如她喜不喜歡你,與多個自變數(因素)有關,例如你人品怎樣、車子是兩個輪的還是四個輪的、長得勝過潘安還是和犀利哥有得一拼、有千尺豪宅還是三寸茅廬等等,我們把這些因素表示為x1, x2,…, xm。那這個女的怎樣考量這些因素呢?最快的方式就是把這些因素的得分都加起來,最後得到的和越大,就表示越喜歡。但每個人心裡其實都有一杆稱,每個人考慮的因素不同,蘿蔔青菜,各有所愛嘛。例如這個女生更看中你的人品,人品的權值是0.6,不看重你有沒有錢,沒錢了一起努力奮鬥,那麼有沒有錢的權值是0.001等等。我們將這些對應x1, x2,…, xm的權值叫做迴歸係數,表達為θ1, θ2,…, θm。他們的加權和就是你的總得分了。請選擇你的心儀男生,非誠勿擾!哈哈。

       所以說上面的logistic迴歸就是一個線性分類模型,它與線性迴歸的不同點在於:為了將線性迴歸輸出的很大範圍的數,例如從負無窮到正無窮,壓縮到0和1之間,這樣的輸出值表達為“可能性”才能說服廣大民眾。當然了,把大值壓縮到這個範圍還有個很好的好處,就是可以消除特別冒尖的變數的影響(不知道理解的是否正確)。而實現這個偉大的功能其實就只需要平凡一舉,也就是在輸出加一個logistic函式。另外,對於二分類來說,可以簡單的認為:如果樣本x屬於正類的概率大於0.5,那麼就判定它是正類,否則就是負類。實際上,SVM的類概率就是樣本到邊界的距離,這個活實際上就讓logistic regression給幹了。

       所以說,LogisticRegression 就是一個被logistic方程歸一化後的線性迴歸,僅此而已。

好了,關於LR的八卦就聊到這。歸入到正統的機器學習框架下,模型選好了,只是模型的引數θ還是未知的,我們需要用我們收集到的資料來訓練求解得到它。那我們下一步要做的事情就是建立代價函數了。

LogisticRegression最基本的學習演算法是最大似然。啥叫最大似然,可以看看我的另一篇博文“從最大似然到EM演算法淺解”。

假設我們有n個獨立的訓練樣本{(x1, y1) ,(x2, y2),…, (xn, yn)},y={0, 1}。那每一個觀察到的樣本(xi, yi)出現的概率是:


上面為什麼是這樣呢?當y=1的時候,後面那一項是不是沒有了,那就只剩下x屬於1類的概率,當y=0的時候,第一項是不是沒有了,那就只剩下後面那個x屬於0的概率(1減去x屬於1的概率)。所以不管y是0還是1,上面得到的數,都是(x, y)出現的概率。那我們的整個樣本集,也就是n個獨立的樣本出現的似然函式為(因為每個樣本都是獨立的,所以n個樣本出現的概率就是他們各自出現的概率相乘):

那最大似然法就是求模型中使得似然函式最大的係數取值θ*。這個最大似然就是我們的代價函式(cost function)了。

       OK,那代價函式有了,我們下一步要做的就是優化求解了。我們先嚐試對上面的代價函式求導,看導數為0的時候可不可以解出來,也就是有沒有解析解,有這個解的時候,就皆大歡喜了,一步到位。如果沒有就需要通過迭代了,耗時耗力。

       我們先變換下L(θ):取自然對數,然後化簡(不要看到一堆公式就害怕哦,很簡單的哦,只需要耐心一點點,自己動手推推就知道了。注:有xi的時候,表示它是第i個樣本,下面沒有做區分了,相信你的眼睛是雪亮的),得到:

       這時候,用L(θ)對θ求導,得到:


       然後我們令該導數為0,你會很失望的發現,它無法解析求解。不信你就去嘗試一下。所以沒辦法了,只能藉助高大上的迭代來搞定了。這裡選用了經典的梯度下降演算法。

二、優化求解

2.1、梯度下降(gradient descent)

        Gradient descent 又叫 steepest descent,是利用一階的梯度資訊找到函式區域性最優解的一種方法,也是機器學習裡面最簡單最常用的一種優化方法。它的思想很簡單,和我開篇說的那樣,要找最小值,我只需要每一步都往下走(也就是每一步都可以讓代價函式小一點),然後不斷的走,那肯定能走到最小值的地方,例如下圖所示:

      但,我同時也需要更快的到達最小值啊,怎麼辦呢?我們需要每一步都找下坡最快的地方,也就是每一步我走某個方向,都比走其他方法,要離最小值更近。而這個下坡最快的方向,就是梯度的負方向了。

對logistic Regression來說,梯度下降演算法新鮮出爐,如下:


      其中,引數α叫學習率,就是每一步走多遠,這個引數蠻關鍵的。如果設定的太多,那麼很容易就在最優值附加徘徊,因為你步伐太大了。例如要從廣州到上海,但是你的一步的距離就是廣州到北京那麼遠,沒有半步的說法,自己能邁那麼大步,是幸運呢?還是不幸呢?事物總有兩面性嘛,它帶來的好處是能很快的從遠離最優值的地方回到最優值附近,只是在最優值附近的時候,它有心無力了。但如果設定的太小,那收斂速度就太慢了,向蝸牛一樣,雖然會落在最優的點,但是這速度如果是猴年馬月,我們也沒這耐心啊。所以有的改進就是在這個學習率這個地方下刀子的。我開始迭代是,學習率大,慢慢的接近最優值的時候,我的學習率變小就可以了。所謂採兩者之精華啊!這個優化具體見2.3 。

       梯度下降演算法的虛擬碼如下:

################################################

初始化迴歸係數為1

重複下面步驟直到收斂{

        計算整個資料集的梯度

        使用alpha x gradient來更新迴歸係數

}

返回迴歸係數值

################################################

2.2、隨機梯度下降SGD (stochastic gradient descent)

      梯度下降演算法在每次更新迴歸係數的時候都需要遍歷整個資料集(計算整個資料集的迴歸誤差),該方法對小資料集尚可。但當遇到有數十億樣本和成千上萬的特徵時,就有點力不從心了,它的計算複雜度太高。改進的方法是一次僅用一個樣本點(的迴歸誤差)來更新迴歸係數。這個方法叫隨機梯度下降演算法。由於可以在新的樣本到來的時候對分類器進行增量的更新(假設我們已經在資料庫A上訓練好一個分類器h了,那新來一個樣本x。對非增量學習演算法來說,我們需要把x和資料庫A混在一起,組成新的資料庫B,再重新訓練新的分類器。但對增量學習演算法,我們只需要用新樣本x來更新已有分類器h的引數即可),所以它屬於線上學習演算法。與線上學習相對應,一次處理整個資料集的叫“批處理”。

        隨機梯度下降演算法的虛擬碼如下:

################################################

初始化迴歸係數為1

重複下面步驟直到收斂{

        對資料集中每個樣本

               計算該樣本的梯度

                使用alpha xgradient來更新迴歸係數

 }

返回迴歸係數值

################################################

2.3、改進的隨機梯度下降

      評價一個優化演算法的優劣主要是看它是否收斂,也就是說引數是否達到穩定值,是否還會不斷的變化?收斂速度是否快?

       上圖展示了隨機梯度下降演算法在200次迭代中(請先看第三和第四節再回來看這裡。我們的資料庫有100個二維樣本,每個樣本都對係數調整一次,所以共有200*100=20000次調整)三個迴歸係數的變化過程。其中係數X2經過50次迭代就達到了穩定值。但係數X1和X0到100次迭代後穩定。而且可恨的是係數X1和X2還在很調皮的週期波動,迭代次數很大了,心還停不下來。產生這個現象的原因是存在一些無法正確分類的樣本點,也就是我們的資料集並非線性可分,但我們的logistic regression是線性分類模型,對非線性可分情況無能為力。然而我們的優化程式並沒能意識到這些不正常的樣本點,還一視同仁的對待,調整係數去減少對這些樣本的分類誤差,從而導致了在每次迭代時引發係數的劇烈改變。對我們來說,我們期待演算法能避免來回波動,從而快速穩定和收斂到某個值。

       對隨機梯度下降演算法,我們做兩處改進來避免上述的波動問題:

1)在每次迭代時,調整更新步長alpha的值。隨著迭代的進行,alpha越來越小,這會緩解係數的高頻波動(也就是每次迭代係數改變得太大,跳的跨度太大)。當然了,為了避免alpha隨著迭代不斷減小到接近於0(這時候,係數幾乎沒有調整,那麼迭代也沒有意義了),我們約束alpha一定大於一個稍微大點的常數項,具體見程式碼。

2)每次迭代,改變樣本的優化順序。也就是隨機選擇樣本來更新迴歸係數。這樣做可以減少週期性的波動,因為樣本順序的改變,使得每次迭代不再形成周期性。

       改進的隨機梯度下降演算法的虛擬碼如下:

################################################

初始化迴歸係數為1

重複下面步驟直到收斂{

       對隨機遍歷的資料集中的每個樣本

              隨著迭代的逐漸進行,減小alpha的值

              計算該樣本的梯度

              使用alpha x gradient來更新迴歸係數

    }

返回迴歸係數值

################################################

       比較原始的隨機梯度下降和改進後的梯度下降,可以看到兩點不同:

1)係數不再出現週期性波動。2)係數可以很快的穩定下來,也就是快速收斂。這裡只迭代了20次就收斂了。而上面的隨機梯度下降需要迭代200次才能穩定。

三、Python實現

      我使用的Python是2.7.5版本的。附加的庫有Numpy和Matplotlib。具體的安裝和配置見前面的博文。在程式碼中已經有了比較詳細的註釋了。不知道有沒有錯誤的地方,如果有,還望大家指正(每次的執行結果都有可能不同)。裡面我寫了個視覺化結果的函式,但只能在二維的資料上面使用。直接貼程式碼:

logRegression.py

  1. #################################################
  2. # logRegression: Logistic Regression
  3. # Author : zouxy
  4. # Date   : 2014-03-02
  5. # HomePage : http://blog.csdn.net/zouxy09
  6. # Email  : [email protected]
  7. #################################################
  8. from numpy import *  
  9. import matplotlib.pyplot as plt  
  10. import time  
  11. # calculate the sigmoid function
  12. def sigmoid(inX):  
  13.     return1.0 / (1 + exp(-inX))  
  14. # train a logistic regression model using some optional optimize algorithm
  15. # input: train_x is a mat datatype, each row stands for one sample
  16. #        train_y is mat datatype too, each row is the corresponding label
  17. #        opts is optimize option include step and maximum number of iterations
  18. def trainLogRegres(train_x, train_y, opts):  
  19.     # calculate training time
  20.     startTime = time.time()  
  21.     numSamples, numFeatures = shape(train_x)  
  22.     alpha = opts['alpha']; maxIter = opts['maxIter']  
  23.     weights = ones((numFeatures, 1))  
  24.     # optimize through gradient descent algorilthm
  25.     for k in range(maxIter):  
  26.         if opts['optimizeType'] == 'gradDescent'# gradient descent algorilthm
  27.             output = sigmoid(train_x * weights)  
  28.             error = train_y - output  
  29.             weights = weights + alpha * train_x.transpose() * error  
  30.         elif opts['optimizeType'] == 'stocGradDescent'# stochastic gradient descent
  31.             for i in range(numSamples):  
  32.                 output = sigmoid(train_x[i, :] * weights)  
  33.                 error = train_y[i, 0] - output  
  34.                 weights = weights + alpha * train_x[i, :].transpose() * error  
  35.         elif opts['optimizeType'] == 'smoothStocGradDescent'# smooth stochastic gradient descent
  36.             # randomly select samples to optimize for reducing cycle fluctuations 
  37.             dataIndex = range(numSamples)  
  38.             for i in range(numSamples):  
  39.                 alpha = 4.0 / (1.0 + k + i) + 0.01
  40.                 randIndex = int(random.uniform(0, len(dataIndex)))  
  41.                 output = sigmoid(train_x[randIndex, :] * weights)  
  42.                 error = train_y[randIndex, 0] - output  
  43.                 weights = weights + alpha * train_x[randIndex, :].transpose() * error  
  44.                 del(dataIndex[randIndex]) # during one interation, delete the optimized sample
  45.         else:  
  46.             raise NameError('Not support optimize method type!')  
  47.     print'Congratulations, training complete! Took %fs!' % (time.time() - startTime)  
  48.     return weights  
  49. # test your trained Logistic Regression model given test set
  50. def testLogRegres(weights, test_x, test_y):  
  51.     numSamples, numFeatures = shape(test_x)  
  52.     matchCount = 0
  53.     for i in xrange(numSamples):  
  54.         predict = sigmoid(test_x[i, :] * weights)[00] > 0.5
  55.         if predict == bool(test_y[i, 0]):  
  56.             matchCount += 1
  57.     accuracy = float(matchCount) / numSamples  
  58.     return accuracy  
  59. # show your trained logistic regression model only available with 2-D data
  60. def showLogRegres(weights, train_x, train_y):  
  61.     # notice: train_x and train_y is mat datatype
  62.     numSamples, numFeatures = shape(train_x)  
  63.     if numFeatures != 3:  
  64.         print"Sorry! I can not draw because the dimension of your data is not 2!"
  65.         return1
  66.     # draw all samples
  67.     for i in xrange(numSamples):  
  68.         if int(train_y[i, 0]) == 0:  
  69.             plt.plot(train_x[i, 1], train_x[i, 2], 'or')  
  70.         elif int(train_y[i, 0]) == 1:  
  71.             plt.plot(train_x[i, 1], train_x[i, 2], 'ob')  
  72.     # draw the classify line
  73.     min_x = min(train_x[:, 1])[00]  
  74.     max_x = max(train_x[:, 1])[00]  
  75.     weights = weights.getA()  # convert mat to array
  76.     y_min_x = float(-weights[0] - weights[1] * min_x) / weights[2]  
  77.     y_max_x = float(-weights[0] - weights[1] * max_x) / weights[2]  
  78.     plt.plot([min_x, max_x], [y_min_x, y_max_x], '-g')  
  79.     plt.xlabel('X1'); plt.ylabel('X2')  
  80.     plt.show()  

四、測試結果

        測試程式碼:

test_logRegression.py

  1. #################################################
  2. # logRegression: Logistic Regression
  3. # Author : zouxy
  4. # Date   : 2014-03-02
  5. # HomePage : http://blog.csdn.net/zouxy09
  6. # Email  : [email protected]
  7. #################################################
  8. from numpy import *  
  9. import matplotlib.pyplot as plt  
  10. import time  
  11. def loadData():  
  12.     train_x = []  
  13.     train_y = []  
  14.     fileIn = open('E:/Python/Machine Learning in Action/testSet.txt')  
  15.     for line in fileIn.readlines():  
  16.         lineArr = line.strip().split()  
  17.         train_x.append([1.0, float(lineArr[0]), float(lineArr[1])])  
  18.         train_y.append(float(lineArr[2]))  
  19.     return mat(train_x), mat(train_y).transpose()  
  20. ## step 1: load data
  21. print"step 1: load data..."
  22. train_x, train_y = loadData()  
  23. test_x = train_x; test_y = train_y  
  24. ## step 2: training...
  25. print"step 2: training..."
  26. opts = {'alpha'0.01'maxIter'20'optimizeType''smoothStocGradDescent'}  
  27. optimalWeights = trainLogRegres(train_x, train_y, opts)  
  28. ## step 3: testing
  29. print"step 3: testing..."
  30. accuracy = testLogRegres(optimalWeights, test_x, test_y)  
  31. ## step 4: show the result
  32. print"step 4: show the result..."
  33. print'The classify accuracy is: %.3f%%' % (accuracy * 100)  
  34. showLogRegre