Python機器學習筆記:Logistic Regression

Logistic迴歸公式推導和代碼實現

1,引言

  logistic迴歸是機器學習中最經常使用最經典的分類方法之一,有人稱之爲邏輯迴歸或者邏輯斯蒂迴歸。雖然他稱爲迴歸模型,可是卻處理的是分類問題,這主要是由於它的本質是一個線性模型加上一個映射函數Sigmoid,將線性模型獲得的連續結果映射到離散型上。它經常使用於二分類問題,在多分類問題的推廣叫softmax。html

  本文首先闡述Logistic迴歸的定義,而後介紹一些最優化算法,其中包括基本的梯度上升法和一個改進的隨機梯度上升法,這些最優化算法將用於分類器的訓練,最好本文將給出一個Logistic迴歸的實例,預測一匹病馬是否能被治癒。git

  在咱們的平常生活中遇到過不少最優化問題,好比如何在最短期內從A點到達B點?如何投入最少工做量卻得到最大的效益?如何設計發動機使得油耗最少而功率最大?可見,最優化的做用十分強大,因此此處咱們介紹幾個最優化算法,並利用它們訓練出一個非線性函數用於分類。github

  如今假設有一些數據點,咱們用一條直線對這些點進行擬合(該線稱爲最佳擬合直線),這個擬合過程就稱做迴歸。利用logistic迴歸進行分類的主要思想是:根據現有數據對分類邊界線創建迴歸公式,以此進行分類,這裏的「迴歸」一詞源於最佳擬合,表示要找到最佳擬合參數集。訓練分類器時的作法就是尋找最佳擬合參數,使用的是最優化算法,下面咱們首先介紹一下這個二值型輸出分類器的數學原理。算法

2,Logistic迴歸的通常過程

  • (1)收集數據:採用任意方法收集數據
  • (2)準備數據:因爲須要進行距離計算,所以要求數據類型爲數值型。另外,結構化數據格式則最佳
  • (3)分析數據:採用任意方法對數據進行分析
  • (4)訓練算法:大部分時間將用於訓練,訓練的目的是爲了找到最佳的分類迴歸係數
  • (5)使用算法:首先,咱們須要輸入一些數據,並將其轉換成對應的結構化數值;接着,基於訓練好的迴歸係數就能夠對這些數值進行簡單的迴歸計算,斷定他們屬於哪一個類別;在這以後,咱們就能夠在輸出的類別上作一些其餘分析工做。

3,Logistic迴歸的優缺點

  優勢:計算代碼很少,易於理解和實現,計算代價不高,速度快,存儲資源低數組

  缺點:容易欠擬合,分類精度可能不高網絡

  適用數據類型:數值型和標稱型數據app

4,基於Logistic迴歸和Sigmoid函數的分類

   咱們想要的函數應該是:能接受全部的輸入,而後預測出類型。例如,在兩個類的狀況下,上述函數輸出0或1。該函數稱爲海維賽德階躍函數(Heaviside step function),或者直接稱爲單位階躍函數。然而,海維賽德階躍函數的問題在於:該函數在跳躍點上從0瞬間跳躍到1,這個瞬間跳躍過程有時很難處理。幸虧,另外一個函數也有相似的性質(能夠輸出0或者1),且數學上更易處理,這就是Sigmoid函數。Sigmoid函數具體的計算公式以下:dom

  自變量取值爲任意實數,值域[0, 1]機器學習

  圖5-1給出了Sigmoid函數在不一樣座標尺度下的兩條曲線圖。當x爲0時,Sigmoid函數值爲0.5。隨着x的增大,對應的Sigmoid值將逼近於1;而隨着x的減小,Sigmoid值將逼近於0.若是橫座標刻度足夠大,Sigmoid函數看起來很像一個階躍函數。ide

  解釋Sigmoid函數:將任意的輸入映射到了 [0, 1]區間,咱們在線性迴歸中能夠獲得一個預測值,再將該值映射到 Sigmoid函數中這樣就完成了由值到機率的轉換,也就是分類任務

  所以,爲了實現Logistic迴歸分類器,咱們能夠在每一個特徵上都乘以一個迴歸係數,而後把全部的結果值相加,將這個總和帶入Sigmoid函數中,進而獲得一個範圍在0~1之間的數值。任何大於0.5的數據被分入1類,小於0.5即被納入0類,因此,Logistic迴歸也能夠被當作是一種機率估計。

  肯定了分類器的函數形式以後,如今的問題變成了:最佳迴歸係數是多少?如何肯定其大小

5,基於最優化方法的最佳迴歸係數肯定

  Sigmoid函數的輸入記爲z,由下面公式獲得:

  若是採用向量的寫法,上述公式能夠寫成  z = wTx  ,它表示將這兩個數值向量對應元素相乘,而後所有加起來即獲得z值。

  其中的向量x是分類器的輸入數據,向量w也就是咱們要找到的最佳參數(係數),從而使得分類器儘量的準確,爲了尋找該最佳參數,須要用到最優化理論的一些知識。

  而後再看看咱們的Logistic迴歸模型的公式:

  這裏假設 W>0,Y與X各維度疊加的圖形關係,以下圖所示(x爲了方便取1維):

  下面首先學習梯度上升的最優化方法,咱們將學習到如何使用該方法求得數據集的最佳參數,接下來,展現如何繪製梯度上升法產生的決策邊界圖,該圖將梯度上升法的分類效果可視化的呈現出來,最後咱們將學習隨機梯度上升算法,以及如何對其進行修改以得到很好地結果。

    可能咱們最常聽到的是梯度降低算法,它與這裏的梯度上升算法是同樣的,只是公式中的
加法須要變成減法,梯度上升算法用來求函數的最大值,而梯度降低算法是用來求函數的最小值

6,梯度上升法 

  梯度上升法基於的思想是:要找到某函數的最大值,最好的方法是沿着該函數的梯度方向探尋,若是梯度記爲,則函數 f(x,y) 的梯度由下面式子表示:

  這個梯度意味着要沿着x的方向移動,沿着y方向移動,其中函數f(x,y)必需要在待計算的點上有定義而且可微,一個具體的函數例子見圖5-2:

   上圖中的梯度上升算法沿梯度方向移動了一步,能夠看出,梯度算子老是指向函數值增加最快的方向。這裏所說的移動方向,而未提到移動量的大小。該量值稱爲步長,記爲。用向量來表示的話,梯度算法的迭代公式以下:

  該公式將一直被迭代執行,直至達到某個中止條件爲止,好比迭代次數達到某個指定值或算法達到某個能夠容許的偏差範圍。

  基於上面的內容,咱們來看一個Logistic迴歸分類器的應用例子,從圖5-3能夠看到咱們採用的數據集。

梯度上升法的公式推導(LR 損失函數)

  在LR中,應用極大似然估計法估計模型參數,因爲Sigmoid函數的特性,咱們能夠作以下的假設:

  上式即爲在已知樣本X和參數θ的狀況下。樣本X屬性正類(y=1)和負類(y=0)的條件機率,將兩個公式合併成一個,以下:

  假定樣本與樣本之間相互獨立,那麼整個樣本集生成的機率即爲全部樣本生成機率的乘積(也就是n個獨立樣本出現的似然函數以下):

  爲了簡化問題,咱們對整個表達式求對數(即爲LR 損失函數)

  知足似然函數(θ)的最大的θ值即時咱們須要求解的模型。

  那麼梯度上升法就像爬坡同樣,一點一點逼近極值,而上升這個動做用數學公式表達即爲:

  其中,α 爲步長。

  回到Logistic迴歸問題,咱們一樣對函數求偏導。

對這個公式進行分解,先看:

  咱們能夠看到,對函數求偏導,分解爲三部分,而後咱們對這三部分分佈求導。

其中:

再由:

可得:

接下來:

最後:

綜合三部分即獲得:

  若是上面鏈式分解很差理解的話,能夠看下面直接求導(結果是同樣的):

   注意上面是將梯度上升求最大值,轉換爲梯度降低了,本質沒變。

所以梯度迭代公式爲:

 

 若是爲梯度降低,咱們注意符號的變化,以下:

7,訓練算法:使用梯度上升找到最佳參數

   上圖有100個樣本點,每一個點包含兩個數值型特徵:X1和X2,在此數據集上,咱們將經過使用梯度上升法找到最佳迴歸係數,也就是擬合出Logistic迴歸模型的最佳參數。

  因此咱們的目標:創建分類器,求解出theta參數

  設定閾值,根據閾值判斷結果

  梯度上升法的僞代碼以下:

每一個迴歸係數初始化爲1

重複R次:
    
    計算整個數據集的梯度

    使用alpha * gradient 更新迴歸係數的向量

    返回迴歸係數

  testSet.txt的文件內容請去個人GitHub下載。地址:https://github.com/LeBron-Jian/MachineLearningNote

  下面具體實現梯度上升算法的代碼:

#_*_coding:utf-8_*_
from numpy import *

# 讀取數據
def loadDataSet(filename):
    '''
        對於testSet.txt,每行前兩個值分別是X1和X2,第三個值數據對應的類別標籤
        並且爲了設置方便,該函數還將X0的值設置爲1.0
        :return:
        '''
    dataMat = []
    labelMat = []
    fr = open(filename)
    for line in fr.readlines():
        lineArr = line.strip().split()
        dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])])
        labelMat.append(int(lineArr[2]))
    return dataMat,labelMat

def sigmoid(inX):
    return 1.0/(1+exp(-inX))

def gradAscent(dataMatIn,classLabels):
    '''
        :param dataMatIn: 是一個2維Numpy數組,每列分別表明每一個不一樣的特徵
        每行則表明每一個訓練樣本。
        :param classLabels: 是類別標籤,是一個1*100的行向量,爲了便於矩陣運算,須要將行向量
        轉換爲列向量,就是矩陣的轉置,再將其賦值與labelMat。
        :return:
        '''
    dataMatrix = mat(dataMatIn)
    labelMat = mat(classLabels).transpose()
    # labelMat = mat(classLabels).T
    m,n = shape(dataMatrix)
    # alpha是向目標移動的步長
    alpha = 0.001
    # 迭代次數
    maxCycles = 500
    weights = ones((n,1))
    for k in range(maxCycles):
        h = sigmoid(dataMatrix*weights)
        error = (labelMat-h)
        weights = weights + alpha*dataMatrix.transpose()*error
    return weights

  注意上面代碼(爲何將x0的值設置爲1呢,主要是咱們要使用矩陣進行運算,能夠看下圖比較明顯)

  測試結果以下:

if __name__  == '__main__':
    filename = 'testSet.txt'
    dataArr,labelMat = loadDataSet(filename)
    weights_res = gradAscent(dataArr,labelMat)
    print(weights_res)
    
'''
[[ 4.12414349]
 [ 0.48007329]
 [-0.6168482 ]]
 '''

  上面已經解出了一組迴歸係數,它肯定了不一樣類別數據之間的分割線,那麼怎樣畫出該分割線,從而使得優化的過程便於理解呢?下面代碼來解決這個問題。

  畫出數據集和Logistic迴歸最佳擬合直線的函數代碼:

def plotBestFit(wei):
    import matplotlib.pyplot as plt
    weights = wei.getA()
    dataMat,labelMat = loadDataSet(filename)
    dataArr = array(dataMat)
    n = shape(dataArr)[0]
    xcord1 = []
    ycord1 = []
    xcord2 = []
    ycord2 = []
    for i in range(n):
        if int(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 = 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()

  輸出的結果和代碼以下圖所示:

if __name__  == '__main__':
    filename = 'testSet.txt'
    dataArr,labelMat = loadDataSet(filename)
    weights_res = gradAscent(dataArr,labelMat)
    print(weights_res)
    plotBestFit(weights_res)

梯度上升算法在500次迭代後獲得的Logistic迴歸最佳擬合直線

  這個分類結果至關不錯,從圖上看,只錯分了四個點。可是,儘管例子簡單且數據集很小,這個方法卻須要大量的計算(300次乘法),所以下一節將對算法稍做改進,從而使它能夠用在真實數據集上。

8,訓練算法:隨機梯度上升

   梯度上升算法在每次更新迴歸係數時都需遍歷整個數據集,該方法在處理100個左右的數據集尚可,可是如有數十億樣本和成千上萬的特徵,那麼該方法的計算複雜度就過高了。一種改進方法是一次僅用一個樣本點來更新迴歸係數,該方法稱爲隨機梯度上升算法。因爲能夠在新樣本到來時對分類器進行增量式更新,於是隨機梯度上升算法是一個在線學習算法,與「在線學習」相對應,一次處理全部數據被稱做是「批處理」。

  隨機梯度上升算法能夠寫成以下的僞代碼:

全部迴歸係數初始化爲1

對數據集中每一個樣本
    
    計算該樣本的梯度

    使用alpha*gradient 更新迴歸係數值

返回迴歸係數值

  如下是隨機梯度上升算法的實現代碼:

# 隨機梯度上升算法
def stocGradAscent0(dataMatrix,classLabels):
    m,n = shape(dataMatrix)
    alpha = 0.01
    weights = ones(n)
    for i in range(m):
        h = sigmoid(sum(dataMatrix[i]*weights))
        error = classLabels[i] - h
        weights = weights + alpha*error*dataMatrix[i]
    return weights

  實現的代碼以下:

if __name__  == '__main__':
    filename = 'testSet.txt'
    dataArr,labelMat = loadDataSet(filename)
    weights_res = stocGradAscent0(array(dataArr),labelMat)
    print(weights_res)
    plotBestFit(weights_res)

  

圖5-5  隨機梯度上升算法在上述數據集上的執行結果,最佳擬合直線並不是最佳分類線

  能夠看出,擬合出來的直線效果還不錯,但並不像,可是不像上面那個完美,這裏的分類器錯分了三分之一的樣本。

  直接比較結果兩個結果是不公平的,後者的結果是在整個數據集上迭代了500次才獲得的。一個判斷優化算法優劣的可靠方法是看它是否收斂,也就是說參數是否達到了穩定值,是否還會不斷地變化?對此,咱們在上面的的隨機梯度算法上作了些修改,使其在整個數據集上運行200次,最終繪製的三個迴歸係數的變化狀況以下圖所示:

  上圖展現了隨機梯度上升算法在200次迭代過程當中迴歸係數的變化狀況,其中的係數2,也就是圖5-5中的X2只通過了50次迭代就達到了穩定值,但係數1和0則須要更屢次的迭代。另外值得注意的是,在大的波動中止後,還有一些小的週期性波動。不難理解,產生這種現象的緣由是存在一些不能正確分類的樣本點(數據集並不是現象可分),在每次迭代時會引起係數的劇烈改變。咱們指望算法能避免來回波動,從而收斂到某個值。另外,收斂速度也須要加快。

  改進的隨機梯度上升算法代碼以下:

# 改進的隨機梯度上升算法
def stocGradAscent1(dataMatrix,classLabels,numIter=150):
    m,n = shape(dataMatrix)
    weights = ones(n)
    for j in range(numIter):
        dataIndex = list(range(m))
        for i in range(m):
            alpha = 4/(1.0+j+i)+0.01
            randIndex = int(random.uniform(0,len(dataIndex)))
            h = sigmoid(sum(dataMatrix[randIndex]*weights))
            error = classLabels[randIndex] - h
            weights = weights + alpha *error*dataMatrix[randIndex]
            del(dataIndex[randIndex])
    return weights

  上述代碼大致上與以前的隨機梯度上升算法一致,修改了兩處,一處是alpha在每次迭代的時候都會調整,這會環節以前的數據波動或者高頻波動。另外,雖然alpha會隨着迭代次數不斷減小,但永遠不會減小到0,。必須這樣作的緣由是爲了保證在屢次迭代以後新數據仍然具備必定的影響。若是要處理的問題是動態變化的,那麼能夠適當增長常數項,來確保新的值得到更大的迴歸係數。另一點值得注意的是,在下降alpha的函數中,alpha每次減小1/(j+1),其中j是迭代次數,i是樣本點的下標,這樣當j<max(i)的時候,alpha就不是嚴格降低的,避免參數的嚴格降低也常見於模擬退火算法等其餘優化算法中。程序的第二個改進地方就是經過隨機選取樣原本更新迴歸係數,這種方法將減小週期性的波動,而且改進的算法還增長一個迭代次數做爲第三個參數,若是該參數沒有給定的話,算法將默認迭代150次,若是給定,那麼算法將按照新的參數值進行迭代。

  與stocGradAscent1()相似,下圖顯示了每次迭代時各個迴歸係數的變化狀況。

  比較5-7和5-6能夠看到兩點不一樣,第一點是,圖5-7中的係數沒有像5-6裏那樣出現週期性的波動,這歸功於stocGradAscent1()裏的樣本隨機選擇機制,第二點是5-7的水平軸比5-6的短了不少,這是因爲stocGradAscent1()能夠收斂的更快,此次咱們僅僅對數據集作了20次遍歷,以前是500次。

  下面看看在同一個數據集上的分類效果,將程序運行能夠看到:

if __name__  == '__main__':
    filename = 'testSet.txt'
    dataArr,labelMat = loadDataSet(filename)
    weights_res = stocGradAscent1(array(dataArr),labelMat)
    print(weights_res)
    plotBestFit(weights_res)

  該分割線達到了與GradientAscent()差很少的效果,可是所使用的計算量更少。

  默認的迭代次數是150次,可是咱們經過stocGradAscent()的第三個參數來對此進行修改,例如:

weights_res = stocGradAscent1(array(dataArr),labelMat,500)

  迄今爲止咱們分析了迴歸係數的變化狀況,可是尚未達到目的,,即完成具體的分類任務,下面咱們將使用隨機梯度上升算法來解決病馬的生死預測問題。,

9,示例:從疝氣病症預測病馬的死亡率

  本次將使用logistic迴歸來預測患有疝氣的馬的存活問題,這裏的數據包含了368個樣本和28個特徵。(疝氣指的是馬胃腸痛的術語,然而這種病不必定源於馬 的腸胃問題,其餘問題也能夠引起疝氣)該數據集包含了醫院檢測馬疝氣病的一些指標,有的指標比較主觀,有的指標難以預測,例如馬的疼痛級別

使用Logistic迴歸估計馬疝氣病的死亡率的流程

(1)收集數據:使用給定數據文件

(2)準備數據:用Python解析文本文件並填充缺失值

(3)分析數據:可視化並觀察數據

(4)訓練算法:使用優化算法,找到最佳的係數

(5)測試算法:爲了量化迴歸的結果,須要觀察錯誤率,根據錯誤率決定是否回退到訓練階段,經過改變迭代的次數和步長等參數來獲得更好的迴歸係數

(6)使用算法:實現一個簡單的命令行程序來收集馬的症狀並輸出預測結果

準備數據:處理數據中的缺失值

  數據中的缺失值是個很是棘手的問題,有不多文獻都致力於解決這個問題。那麼,數據缺乏究竟帶來了什麼問題?假設有100個樣本和20個特徵,這些數據都是機器收集回來的,若機器上的某個傳感器損壞致使一個特徵無效時該怎麼辦?此時是否要扔掉整個數據?這種狀況下,另外19個特徵怎麼辦?它們是否還可用?答案是確定的。由於有時候數據至關昂貴,扔掉和從新獲取都是不可取的,因此必須採起一些方法來解決這個問題。

  下面給出了一些可選的作法:

  • 使用可用特徵的均值來填補缺失值;
  • 使用特徵值來填充缺失值,如-1
  • 忽略有缺失值的樣本
  • 使用類似樣本的均值填補缺失值
  • 使用另外的機器學習算法預測缺失值

   如今,咱們對下一節要用的數據集進行預處理,使其能夠順利地使用分類算法。在預處理階段須要作兩件事:第一,全部的缺失值必須用一個實數值來替換,由於咱們使用的Numpy數據類型不容許包含缺失值。這裏選擇實數0來替換,由於咱們使用的Numpy數據類型不容許包括缺失值,這裏選擇實數0來替換全部缺失值,剛好能適應於Logistic迴歸。這樣作的直覺在於,咱們須要的是一個在更新時不會影響係數的值,迴歸係數的更新公式以下:

weights = weights + alpha *error*dataMatrix[randIndex]

  若是dataMatirx的某特徵對應值爲0,那麼該特徵的係數將不作更新,即:

weights = weights

  另外,因爲sigmoid(0) = 0.5 ,即對它結果的預測不具備任何傾向性,所以上述作法也不會對偏差項形成任何影響,基於上述緣由,將缺失值用0代替既能夠保留現有數據,也不須要對優化算法進行修改,此外,數據集中的特徵值通常不取0,所以在某種意義上說它也知足「特殊值」這個要求。

  預處理中作的第二件事,是若是在測試數據集中發現了一條數據的類別標籤以及缺失,那麼咱們的簡單作法是將該條數據丟棄。這是由於類別標籤與特徵不一樣,很難肯定採用某個合適的值來替換,採用Logistic迴歸進行預處理以後保存兩個文件,horseColicTest.txt和horseColicTraining.txt。若是想對於原始數據和預處理以後的數據作個比較。

  咱們有一個「乾淨」可用的數據集和一個不錯的優化算法,下面將這些部分融合在一塊兒訓練出一個分類器,而後利用該分類器來預測病馬的生死問題。

  horseColicTest.txt的數據 horseColicTraining.txt的數據  請去個人GitHub下載:https://github.com/LeBron-Jian/MachineLearningNote

 完整代碼:

#-*_coding:utf-8_*_
import math
from numpy import *

# Sigmoid函數的計算
def sigmoid(inX):
    return 1.0/(1+exp(-inX))

# 改進的隨機梯度上升算法
def stocGradAscent1(dataMatrix,classLabels,numIter=150):
    m,n = shape(dataMatrix)
    weights = ones(n)
    for j in range(numIter):
        dataIndex = list(range(m))
        for i in range(m):
            alpha = 4/(1.0+j+i)+0.01
            randIndex = int(random.uniform(0,len(dataIndex)))
            h = sigmoid(sum(dataMatrix[randIndex]*weights))
            error = classLabels[randIndex] - h
            weights = weights + alpha *error*dataMatrix[randIndex]
            del(dataIndex[randIndex])
    return weights


# Logistic迴歸分類函數
def  classifyVetor(inX,weights):
    prob = sigmoid(sum(inX*weights))
    if prob > 0.5:
        return 1.0
    else:
        return 0.0

def colicTest(filetrain,filetest):
    frTrain = open(filetrain)
    frTest = open(filetest)
    trainingSet = []
    trainingLabeles = []
    for line in frTrain.readlines():
        currLine = line.strip().split('\t')
        lineArr = []
        for i in range(21):
            lineArr.append(float(currLine[i]))
        trainingSet.append(lineArr)
        trainingLabeles.append(float(currLine[21]))
    trainWeights = stocGradAscent1(array(trainingSet),trainingLabeles,500)
    errorCount = 0
    numTestVec = 0
    for line in frTest.readlines():
        numTestVec += 1.0
        currLine = line.strip().split('\t')
        lineArr =[]
        for i in range(21):
            lineArr.append(float(currLine[i]))
        if int(classifyVetor(array(lineArr),trainWeights)) != int(currLine[21]):
            errorCount += 1
    errorRate = (float(errorCount)/numTestVec)
    print('the error rate of this test is  %f'%errorRate)
    return errorRate

def multTest(filetrain,filetest):
    numTests = 6
    errorSum = 1.0
    for k in range(numTests):
        errorSum += colicTest(filetrain,filetest)
    print('after %d iterations  the average error rate is %f'%(numTests,errorSum/float(numTests)))


if __name__ == '__main__':
    filetrain = 'horseColicTraining.txt'
    filetest = 'horseColicTest.txt'
    multTest(filetrain,filetest)

  運行結果:

the error rate of this test is  0.373134
the error rate of this test is  0.358209
the error rate of this test is  0.402985
the error rate of this test is  0.432836
the error rate of this test is  0.462687
the error rate of this test is  0.343284
after 6 iterations  the average error rate is 0.562189

  從結果來看,6次迭代以後的平均錯誤率爲0.56,事實上,這個結果還不錯,由於數據有30%的數據缺失,固然若是調整colicTest()中的迭代次數和stochGradAscent1()中的步長,平均錯誤率就能夠降到20%左右。

10,使用梯度降低求解邏輯迴歸

  這裏再補充一個示例,咱們將創建一個邏輯迴歸模型來預測一個學生是否被大學錄取。假設你是一個大學習的管理員,你想根據兩次考試的結果來決定每一個申請人的錄取機會。你又之前的申請人的歷史數據,你能夠用它做爲邏輯迴歸的訓練集。對於每個培訓例子,你有兩個考試的申請人的分數和錄取決定。爲了作到這一點,咱們將創建一個分類模型,根據考試成績估計入學機率。

  數據和代碼請參考個人GitHub:https://github.com/LeBron-Jian/MachineLearningNote

  首先,拿到數據,並查看部分數據:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

path = 'LogiReg_data.txt'
pdData = pd.read_csv(path, header=None, names=['Exam 1', 'Exam 2', 'Admitted'])
print(pdData.head(10))

   部分數據以下:

   拿到數據後,咱們將其按照結果分爲錄取和不被錄取的集合,並展現:

positive = pdData[pdData['Admitted'] == 1]
negative = pdData[pdData['Admitted'] == 0]
fig, ax = plt.subplots(figsize=(5, 5))
ax.scatter(positive['Exam 1'], positive['Exam 2'], s=30, c='b', marker='o', label='Admitted')
ax.scatter(negative['Exam 1'], negative['Exam 2'], s=30, c='r', marker='x', label='Not Admitted')
ax.legend()
ax.set_xlabel('Exam 1 Score')
ax.set_ylabel('Exam 2 Score')

   圖示以下:

   下面使用邏輯迴歸來創建分類器,咱們須要求解出三個參數 theta0,  theta1,  theta2。 而後設定閾值,根據閾值判斷錄取結果。

  要完成的模塊函數:

  • Sigmoid:映射到機率的函數
  • model:返回預測結果值
  • cost:根據參數計算損失
  • gradient:計算每一個參數的梯度方法
  • descent:進行參數更新
  • accuracy:計算精度

  首先,完成Sigmoid函數:

def sigmoid(z):
    return 1 / (1 + np.exp(-z))

# 若是想查看Sigmoid函數,能夠運行下面代碼
nums = np.arange(-10, 10, step=1)
fig, ax = plt.subplots(figsize=(12, 4))
ax.plot(nums, sigmoid(nums), 'r')

   而後對數據集進行劃分,分爲訓練數據和標籤,其中咱們添加了一列,而且初始化了theta值

# 增長一行1  在第0列
pdData.insert(0, 'Ones', 1)
print(pdData.head())
# 設置X和Y  即訓練數據和訓練變量
orig_data = pdData.values
cols = orig_data.shape[1]  # (100, 4)
X = orig_data[:, 0:cols-1]   # 等價於 np.matrix(X.values)
Y = orig_data[:, cols-1:cols]  # 等價於 np.matrox(data.iloc[:, 3:4].value

# 傳遞numpu矩陣 並初始化參數矩陣 theta
theta = np.zeros([1, 3])  # [[0. 0. 0.]]

   咱們能夠看一下添加了一列後,整體的數據:

   其餘的數據都可以本身打印。

  下面是損失函數:

   代碼整理以下:

def cost(X, y, theta):
    left = np.multiply(-y, np.log(model(X, theta)))
    right = np.multiply(1-y, np.log(1 - model(X, theta)))
    return np.sum(left - right) / (len(X))

   下面是計算梯度,梯度函數以下(計算梯度就是咱們對theta求偏導):

   代碼可寫成下面方式:

def gradient(X, y, theta):
    grad = np.zeros(theta.shape)
    error = (model(X, theta)-y).ravel()
    for j in range(len(theta.ravel())):  # for each parmeter
        term = np.multiply(error, X[:, j])
        grad[0, j] = np.sum(term) / len(X)
    return grad

   下面比較三種不一樣梯度降低方法,三種梯度降低分別是 批量梯度降低,隨機梯度降低,小批量梯度降低。以下圖:

 

   咱們的方法爲1,根據迭代次數中止  2,根據損失函數中止  3,根據梯度降低中止

  (注意:咱們每次進行迭代更新的時候,都會對數據進行洗牌,一把狀況數據都會有某種規律)

# 比較三種不一樣梯度降低方法
STOP_ITER = 0
STOP_COST = 1
STOP_GRAD = 2

def stopCriterion(type, value, threshold):
    # 設定三種不一樣的中止策略
    if type == STOP_ITER:
        return value > threshold
    elif type == STOP_COST:
        return abs(value[-1]-value[-2]) < threshold
    elif type == STOP_GRAD:
        return np.linalg.norm(value) < threshold

# 洗牌
def shuffleData(data):
    np.random.shuffle(data)
    cols = data.shape[1]
    X = data[:, 0:cols-1]
    y = data[:, cols-1:]
    return X, y

def descent(data, theta, batchSize, stopType, thresh, alpha):
    # 梯度降低求解
    init_time = time.time()
    i = 0  # 迭代次數
    k = 0  # batch
    X, y = shuffleData(data)
    grad = np.zeros(theta.shape)  # 計算的梯度
    costs = [cost(X, y, theta)]  # 損失值

    while True:
        grad = gradient(X[k:k+batchSize], y[k:k+batchSize], theta)
        k += batchSize  # 取batch數量個數據
        if k>=n:
            k = 0
            X, y = shuffleData(data)  # 從新洗牌
        theta = theta = alpha*grad  # 參數更新
        costs.append(cost(X, y, theta))  # 計算新的損失
        i += 1

        if stopType == STOP_ITER:
            value = i
        elif stopType == STOP_COST:
            value = costs
        elif stopType == STOP_GRAD:
            value = grad
        if stopCriterion(stopType, value, thresh):
            break
    return theta, i-1, costs, grad, time.time()-init_time

   下面咱們定義一個功能性的函數,此函數的做用就是執行一次梯度降低,並完成更新,選擇中止策略。其中主要代碼就第一句。。。

 

def runExpe(data, theta, batchSize, stopType, thresh, alpha):
    theta, iter, costs, grad, dur = descent(data, theta, batchSize, stopType, thresh, alpha)
    name = 'Original' if (data[:, 1] > 2).sum() > 1 else 'Scaled'
    name += 'data - learning rate: {} -'.format(alpha)
    if batchSize == n:
        strDescType = 'Gradient'
    elif batchSize == 1:
        strDescType = 'Stochastic'
    else:
        strDescType = 'Min-batch({})'.format(batchSize)
    name += strDescType + 'descent - Stop: '
    if stopType == STOP_ITER:
        strStop = "{} iterations".format(thresh)
    elif stopType == STOP_COST:
        strStop = "costs change < {}".format(thresh)
    else:
        strStop = 'gradient norm < {}'.format(thresh)
    name += strStop
    print("***{}\nTheta: {} - Iter:{}-Last cost: {:03.2f}--Duration: {:03.2f}s".format(
        name, theta, iter, costs[-1], dur
    ))
    fig, ax = plt.subplots(figsize=(12, 4))
    ax.plot(np.arange(len(costs)), costs, 'r')
    ax.set_xlabel('Iterations')
    ax.set_ylabel('Cost')
    ax.set_title(name.upper() + '- Error vs. Iteration')
    return theta

   下面按照不一樣的中止策略畫圖展現:

1,設定迭代次數  5000

2,設定閾值1e-6,差很少須要 110000次迭代

3,設置閾值 0.05,差很少須要 40000次迭代

if __name__ == '__main__':
    # 設定迭代次數   選擇的梯度降低方法是基於全部樣本的
    n = 100
    # runExpe(orig_data, theta, n, STOP_ITER, thresh=5000, alpha=0.000001)
    # 根據損失值中止    設定閾值 1e-6 差很少須要110 000 次迭代
    # runExpe(orig_data, theta, n, STOP_COST, thresh=0.000001, alpha=0.001)
    # 根據梯度變換中止  設定閾值 0.05,差很少須要 40000次迭代
    # runExpe(orig_data, theta, n, STOP_GRAD, thresh=0.05, alpha=0.001)
    # 對比不一樣的梯度降低方法  stochastic descent  這個不收斂
    # runExpe(orig_data, theta, 1, STOP_ITER, thresh=5000, alpha=0.001)
    # 咱們將學習率調小,提升thresh再試試,發現收斂了
    # runExpe(orig_data, theta, 1, STOP_ITER, thresh=15000, alpha=0.000001)
    # Mine-batch descent  這個也不收斂 ,咱們將alpha調小 0.000001  發現收斂了
    runExpe(orig_data, theta, 16, STOP_ITER, thresh=15000, alpha=0.001)

   浮動仍然比較大,咱們來嘗試下對數據進行標準化,將數據按其屬下(按列進行)減去其均值,而後除以其方差。最後獲得的結果是,對每一個屬性/每列來講全部數據都彙集在 0 附加,方差值爲1。

    scaled_data = orig_data.copy()
    scaled_data[:, 1:3] = pp.scale(orig_data[:, 1:3])
    runExpe(scaled_data, theta, n, STOP_ITER, thresh=5000, alpha=0.01)

   下面兩幅圖,咱們分別將學習率設置爲 0.001  0.01

 ·  咱們發現,這裏已經降到了0.3如下,因此說作數據預處理很是重要。

    scaled_data = orig_data.copy()
    scaled_data[:, 1:3] = pp.scale(orig_data[:, 1:3])
    # runExpe(scaled_data, theta, n, STOP_ITER, thresh=5000, alpha=0.01)
    runExpe(scaled_data, theta, n, STOP_GRAD, thresh=0.02, alpha=0.01)

   咱們修改一下,根據梯度變換中止,發現更多的迭代次數會使得損失降低的更多

runExpe(scaled_data, theta, 1, STOP_GRAD, thresh=0.002/5, alpha=0.01)

   隨機梯度降低更快,可是咱們須要迭代的次數也須要更多,因此仍是用 batch的比較合適。

   精度

# 設置閾值
def predict(x, theta):
    return [1 if x >= 0.5 else 0 for x in model(X, theta)]


if __name__ == '__main__':
    # 設定迭代次數   選擇的梯度降低方法是基於全部樣本的
    n = 100
    scaled_data = orig_data.copy()
    scaled_X = scaled_data[:, :3]
    y = scaled_data[:, 3]
    predictions = predict(scaled_X, theta)
    correct = [1 if ((a == 1 and b == 1) or (a == 0 and b == 0)) else 0 for a, b in zip(predictions, y)]
    accuracy = (sum(map(int, correct)) % len(correct))
    print('accuracy = {0}%'.format(accuracy))
    print(len(correct))

   不知道爲何,我求出來的精度不高。。。。百分之60。

11,總結

  Logistic迴歸的目的是尋找一個非線性函數Sigmoid的最佳擬合參數,求解過程能夠由最優化算法來完成。在最優化算法中,最經常使用的就是梯度上升算法,而梯度上升算法又能夠簡化爲隨機梯度上升算法。

  隨機梯度上升算法與梯度上升算法的效果至關,可是佔用更少的計算資源。此外,隨機梯度上升是一個在線算法,它能夠在新數據到來時就完成參數更新,不須要從新讀取整個數據集來進行批處理運算。

  機器學習的一個重要問題就是如何處理缺失數據,這個問題沒有標準答案,取決於實際應用中的需求。

12,推廣

  前面也說了Logistic迴歸模型主要用於二分類,那麼下面說一下多分類問題中的推廣——softmax迴歸。

  softmax與Logistic迴歸的主要區別就是,Logistic處理二分類問題,只有一組權重參數θ,而softmax處理多分類問題,若是有k個類別,那麼softmax就有k組權值參數。每組權值對應一種分類,經過k組權值求解出樣本數據對應每一個類別的機率,最後取機率最大的類別做爲該數據的分類結果,它的機率函數爲:

  softmax函數常常用於神經網絡的最後一層,用於對神經網絡已經處理好的特徵進行分類。

  總結來講:邏輯迴歸真的真的很好用!

基於Sklearn構建Logistic迴歸分類器 

  下面讓咱們看一下Sklearn的Logistic迴歸分類器

  英文的Sklearn文檔地址:請點擊我

  sklearn.linear_model模塊提供了不少模型供咱們使用,好比Logistic迴歸、Lasso迴歸、貝葉斯脊迴歸等,可見須要學習的東西還有不少不少。本次,咱們使用LogisticRegressioin。

1,LogisticRegression

  讓咱們先看一下LogisticRegression這個函數,一共有14個參數

參數說明以下:

  penalty:懲罰項,str類型,可選參數爲l1和l2,默認爲l2。用於指定懲罰項中使用的規範。newton-cg、sag和lbfgs求解算法只支持L2規範。L1G規範假設的是模型的參數知足拉普拉斯分佈,L2假設的模型參數知足高斯分佈,所謂的範式就是加上對參數的約束,使得模型更不會過擬合(overfit),可是若是要說是否是加了約束就會好,這個沒有人能回答,只能說,加約束的狀況下,理論上應該能夠得到泛化能力更強的結果。
  dual:對偶或原始方法,bool類型,默認爲False。對偶方法只用在求解線性多核(liblinear)的L2懲罰項上。當樣本數量>樣本特徵的時候,dual一般設置爲False。
  tol:中止求解的標準,float類型,默認爲1e-4。就是求解到多少的時候,中止,認爲已經求出最優解。
  c:正則化係數λ的倒數,float類型,默認爲1.0。必須是正浮點型數。像SVM同樣,越小的數值表示越強的正則化。
  fit_intercept:是否存在截距或誤差,bool類型,默認爲True。若是使用中心化的數據,能夠考慮設置爲False,不考慮截距。注意這裏是考慮,通常仍是要考慮截距
  intercept_scaling:僅在正則化項爲」liblinear」,且fit_intercept設置爲True時有用。float類型,默認爲1。
  class_weight:用於標示分類模型中各類類型的權重,能夠是一個字典或者balanced字符串,默認爲不輸入,也就是不考慮權重,即爲None。若是選擇輸入的話,能夠選擇balanced讓類庫本身計算類型權重,或者本身輸入各個類型的權重。舉個例子,好比對於0,1的二元模型,咱們能夠定義class_weight={0:0.9,1:0.1},這樣類型0的權重爲90%,而類型1的權重爲10%。若是class_weight選擇balanced,那麼類庫會根據訓練樣本量來計算權重。某種類型樣本量越多,則權重越低,樣本量越少,則權重越高。當class_weight爲balanced時,類權重計算方法以下:n_samples / (n_classes * np.bincount(y))。n_samples爲樣本數,n_classes爲類別數量,np.bincount(y)會輸出每一個類的樣本數,例如y=[1,0,0,1,1],則np.bincount(y)=[2,3]。
    那麼class_weight有什麼做用呢? 在分類模型中,咱們常常會遇到兩類問題:

  • 1,第一種是誤分類的代價很高。好比對合法用戶和非法用戶進行分類,將非法用戶分類爲合法用戶的代價很高,咱們寧願將合法用戶分類爲非法用戶,這時能夠人工再甄別,可是卻不肯將非法用戶分類爲合法用戶。這時,咱們能夠適當提升非法用戶的權重。
  • 2,第二種是樣本是高度失衡的,好比咱們有合法用戶和非法用戶的二元樣本數據10000條,裏面合法用戶有9995條,非法用戶只有5條,若是咱們不考慮權重,則咱們能夠將全部的測試集都預測爲合法用戶,這樣預測準確率理論上有99.95%,可是卻沒有任何意義。這時,咱們能夠選擇balanced,讓類庫自動提升非法用戶樣本的權重。提升了某種分類的權重,相比不考慮權重,會有更多的樣本分類劃分到高權重的類別,從而能夠解決上面兩類問題。

  random_state:隨機數種子,int類型,可選參數,默認爲無,僅在正則化優化算法爲sag,liblinear時有用。
  solver:優化算法選擇參數,只有五個可選參數,即newton-cg,lbfgs,liblinear,sag,saga。默認爲liblinear。solver參數決定了咱們對邏輯迴歸損失函數的優化方法,有四種算法能夠選擇,分別是:

  • liblinear:使用了開源的liblinear庫實現,內部使用了座標軸降低法來迭代優化損失函數。
  • lbfgs:擬牛頓法的一種,利用損失函數二階導數矩陣即海森矩陣來迭代優化損失函數。
  • newton-cg:也是牛頓法家族的一種,利用損失函數二階導數矩陣即海森矩陣來迭代優化損失函數。
  • sag:即隨機平均梯度降低,是梯度降低法的變種,和普通梯度降低法的區別是每次迭代僅僅用一部分的樣原本計算梯度,適合於樣本數據多的時候。
  • saga:線性收斂的隨機優化算法的的變重。

總結:

  • liblinear適用於小數據集,而sag和saga適用於大數據集由於速度更快。
  • 對於多分類問題,只有newton-cg,sag,saga和lbfgs可以處理多項損失,而liblinear受限於一對剩餘(OvR)。啥意思,就是用liblinear的時候,若是是多分類問題,得先把一種類別做爲一個類別,剩餘的全部類別做爲另一個類別。一次類推,遍歷全部類別,進行分類。
  • newton-cg,sag和lbfgs這三種優化算法時都須要損失函數的一階或者二階連續導數,所以不能用於沒有連續導數的L1正則化,只能用於L2正則化。而liblinear和saga通吃L1正則化和L2正則化。
  • 同時,sag每次僅僅使用了部分樣本進行梯度迭代,因此當樣本量少的時候不要選擇它,而若是樣本量很是大,好比大於10萬,sag是第一選擇。可是sag不能用於L1正則化,因此當你有大量的樣本,又須要L1正則化的話就要本身作取捨了。要麼經過對樣本採樣來下降樣本量,要麼回到L2正則化。
  • 從上面的描述,你們可能以爲,既然newton-cg, lbfgs和sag這麼多限制,若是不是大樣本,咱們選擇liblinear不就好了嘛!錯,由於liblinear也有本身的弱點!咱們知道,邏輯迴歸有二元邏輯迴歸和多元邏輯迴歸。對於多元邏輯迴歸常見的有one-vs-rest(OvR)和many-vs-many(MvM)兩種。而MvM通常比OvR分類相對準確一些。鬱悶的是liblinear只支持OvR,不支持MvM,這樣若是咱們須要相對精確的多元邏輯迴歸時,就不能選擇liblinear了。也意味着若是咱們須要相對精確的多元邏輯迴歸不能使用L1正則化了。

  max_iter:算法收斂最大迭代次數,int類型,默認爲10。僅在正則化優化算法爲newton-cg, sag和lbfgs纔有用,算法收斂的最大迭代次數。
  multi_class:分類方式選擇參數,str類型,可選參數爲ovr和multinomial,默認爲ovr。ovr即前面提到的one-vs-rest(OvR),而multinomial即前面提到的many-vs-many(MvM)。若是是二元邏輯迴歸,ovr和multinomial並無任何區別,區別主要在多元邏輯迴歸上。

  • OvR和MvM有什麼不一樣?
  1. OvR的思想很簡單,不管你是多少元邏輯迴歸,咱們均可以看作二元邏輯迴歸。具體作法是,對於第K類的分類決策,咱們把全部第K類的樣本做爲正例,除了第K類樣本之外的全部樣本都做爲負例,而後在上面作二元邏輯迴歸,獲得第K類的分類模型。其餘類的分類模型得到以此類推。
  2. 而MvM則相對複雜,這裏舉MvM的特例one-vs-one(OvO)做講解。若是模型有T類,咱們每次在全部的T類樣本里面選擇兩類樣本出來,不妨記爲T1類和T2類,把全部的輸出爲T1和T2的樣本放在一塊兒,把T1做爲正例,T2做爲負例,進行二元邏輯迴歸,獲得模型參數。咱們一共須要T(T-1)/2次分類。
  3. 能夠看出OvR相對簡單,但分類效果相對略差(這裏指大多數樣本分佈狀況,某些樣本分佈下OvR可能更好)。而MvM分類相對精確,可是分類速度沒有OvR快。若是選擇了ovr,則4種損失函數的優化方法liblinear,newton-cg,lbfgs和sag均可以選擇。可是若是選擇了multinomial,則只能選擇newton-cg, lbfgs和sag了。

  verbose:日誌冗長度,int類型。默認爲0。就是不輸出訓練過程,1的時候偶爾輸出結果,大於1,對於每一個子模型都輸出。
  warm_start:熱啓動參數,bool類型。默認爲False。若是爲True,則下一次訓練是以追加樹的形式進行(從新使用上一次的調用做爲初始化)。
  n_jobs:並行數。int類型,默認爲1。1的時候,用CPU的一個內核運行程序,2的時候,用CPU的2個內核運行程序。爲-1的時候,用全部CPU的內核運行程序。

除此以外,LogisticRegression也有一些方法供咱們使用:

有一些方法和MultinomialNB的方法都是相似的,所以再也不累述。

 2,編寫代碼

  瞭解了基本知識點,咱們就能夠編寫Sklearn分類器的代碼了,代碼以下:

#_*_ codingLutf-8_*_

from sklearn.linear_model import LogisticRegression

def colicSklearn(filetrain,filetest):
    frTrain = open(filetrain)
    frTest = open(filetest)
    trainingSet = []
    trainingLabels = []
    testSet = []
    testLabels = []
    for line in frTrain.readlines():
        currLine = line.strip().split('\t')
        lineArr = []
        for i in range(len(currLine)-1):
            lineArr.append(float(currLine[i]))
        trainingSet.append(lineArr)
        trainingLabels.append(float(currLine[-1]))
    for line in frTest.readlines():
        currLine = line.strip().split('\t')
        lineArr = []
        for i in range(len(currLine)-1):
            lineArr.append(float(currLine[i]))
        testSet.append(lineArr)
        testLabels.append(float(currLine[-1]))
    classifier = LogisticRegression(solver='liblinear',max_iter=20).fit(trainingSet,trainingLabels)
    test_accurcy = classifier.score(testSet,testLabels)*100
    print("正確率爲%s%%"%test_accurcy)

if __name__ == '__main__':
    filetrain = 'horseColicTraining.txt'
    filetest = 'horseColicTest.txt'
    colicSklearn(filetrain,filetest)

  執行結果以下:

正確率爲73.13432835820896%

  能夠看到,正確率又搞了,更改solver參數,好比設置爲sag,使用隨機平均梯度降低算法,看一看效果,你會發現:正確率高了,可是發出了警告,發出警告是由於算法尚未收斂,更改迭代次數便可。

 當咱們迭代到5000的時候,就不會報錯了,因此說,對於小數據集,sag算法須要迭代上千次才收斂,而liblinear只須要不到10次。

  因此,咱們須要根據數據集狀況,選擇最優化算法。

3,sklearn利用LR模型進行三分類的原理及其代碼

  首先,LR將線性模型利用Sigmoid函數進一步作了非線性映射。將分類超平面兩側的正負樣本點經過壓縮函數轉化成了以 0.5 爲分類的兩類:類別0 和類別1。

  在上面咱們講述了線性邊界與LR分佈函數(即Sigmoid函數)的映射對應關係;一樣,對於非線性斷定邊界Sigmoid函數也使用,以下:

  而後,再去考慮如何去得到模型參數,就是斷定邊界的參數怎麼得到,這裏是利用MLE進行求解的,具體求解過程能夠百度。

  代碼以下:

#_*_coding:utf-8_*_
import numpy as np
import matplotlib.pyplot as plt

def sigmoid(z):
    return 1.0/(1.0 + np.exp(-z))

z = np.arange(-7, 7, 0.1)
print(z)
phi_z = sigmoid(z)

def cost_1(z):
    return -np.log(sigmoid(z))

def cost_0(z):
    return -np.log(1-sigmoid(z))


z = np.arange(-10, 10, 0.1)
phi_z = sigmoid(z)

c1 = [cost_1(x) for x in z]
plt.plot(phi_z, c1, label='J(w) if y=1')

c0 = [cost_0(x) for x in z]
plt.plot(phi_z, c0, linestyle='--', label='J(w) if y=0')

plt.ylim(0.0, 5.1)
plt.xlim([0, 1])
plt.xlabel('$\phi$(z)')
plt.ylabel('J(w)')
plt.legend(loc='best')
plt.tight_layout()
# plt.savefig('./figures/log_cost.png', dpi=300)
plt.show()

  結果以下:

  最後,考慮LR進行三分類(多分類)時,是特徵的線性組合和Sigmoid函數符合的函數進行機率計算和分類的。

  代碼:

from IPython.display import Image

# Added version check for recent scikit-learn 0.18 checks
from distutils.version import LooseVersion as Version
from sklearn import __version__ as sklearn_version

from sklearn import datasets
import numpy as np

iris = datasets.load_iris()
# http://scikit-learn.org/stable/auto_examples/datasets/plot_iris_dataset.html
X = iris.data[:, [2, 3]]
print(X.shape)
y = iris.target  # 取species列,類別

if Version(sklearn_version) < '0.18':
    from sklearn.cross_validation import train_test_split
else:
    from sklearn.model_selection import train_test_split

# train_test_split方法分割數據集
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.3, random_state=0)

from sklearn.preprocessing import StandardScaler

sc = StandardScaler()  # 初始化一個對象sc去對數據集做變換
sc.fit(X_train)  # 用對象去擬合數據集X_train,而且存下來擬合參數
X_train_std = sc.transform(X_train)
X_test_std = sc.transform(X_test)

from sklearn.linear_model import LogisticRegression


def sigmoid(z):
    return 1.0 / (1.0 + np.exp(-z))


lr = LogisticRegression(C=1000.0, random_state=0)
# http://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegression.html#sklearn.linear_model.LogisticRegression
lr.fit(X_train_std, y_train)

# 計算該預測實例點屬於各種的機率
lr.predict_proba(X_test_std[0, :].reshape(1, -1))
# Output:array([[  2.05743774e-11,   6.31620264e-02,   9.36837974e-01]])

# 驗證predict_proba的做用
c = lr.predict_proba(X_test_std[0, :].reshape(1, -1))
# c[0, 0] + c[0, 1] + c[0, 2]
# Output:0.99999999999999989

# 查看lr模型的特徵係數
lr = LogisticRegression(C=1000.0, random_state=0)
# http://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegression.html#sklearn.linear_model.LogisticRegression
lr.fit(X_train_std, y_train)
print(lr.coef_)
# Output:[[-7.34015187 -6.64685581]
#        [ 2.54373335 -2.3421979 ]
#        [ 9.46617627  6.44380858]]

# 驗證predict_proba工做原理
Zz = np.dot(lr.coef_, X_test_std[0, :].T) + lr.intercept_
np.array(sigmoid(Zz)) / sum(np.array(sigmoid(Zz)))
# Output:array([  2.05743774e-11,   6.31620264e-02,   9.36837974e-01])
# 此結果就是預測實例點各種的機率

  

 

4,使用Sklearn的Logistic迴歸算法計算鳶尾花

  對鳶尾花進行分類代碼:

from sklearn import datasets
from numpy import *
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split


def colicSklearn():
    iris = datasets.load_iris()
    X = iris.data
    Y = iris.target
    trainingSet,testSet,trainingLabels,testLabels = train_test_split(X,Y,test_size=0.25,random_state=40)
    classifier = LogisticRegression(solver='sag', max_iter=5000).fit(trainingSet, trainingLabels)
    test_accurcy = classifier.score(testSet, testLabels) * 100
    print("正確率爲%s%%" % test_accurcy)

if __name__  == '__main__':
    colicSklearn()

  結果:

正確率爲100.0%

  

5,使用Sklearn的Logistic迴歸算法預測線性迴歸函數

 代碼:

#_*_coding:utf-8_*_
'''
下面這個例子,從數據產生,到數據提取,數據標準化
模型訓練和評估來講明各個API的調用過程
'''
print(__doc__)
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
# import matplotlib as mpl
import pylab as mpl

# 設置字符集,防止中文亂碼
mpl.rcParams['font.sans-serif'] = [u'simHei']
mpl.rcParams['axes.unicode_minus'] = False

# 定義目標函數經過改函數產生對應的y
# y = 1 *x[0] + 2 *x[1] + ... (n+1) *x[n]
def l_model(x):
    params = np.arange(1, x.shape[-1] +1)
    y = np.sum(params *x) + np.random.randn(1) * 0.1
    return y

# 定義數據集
x = pd.DataFrame(np.random.rand(500,6))
# print(x)
y = x.apply(lambda x_rows:pd.Series(l_model(x_rows)),axis=1)
# print(y)

# 劃分數據集
x_train, x_test, y_train, y_test = train_test_split(x,y,test_size=0.3,random_state=2)

# 數據標準化
ss = StandardScaler()
x_train_s = ss.fit_transform(x_train)
x_test_s = ss.fit_transform(x_test)

# 輸出下元數據的標準差和平均數
print(ss.scale_)
print(ss.mean_)

# 訓練模型
lr = LinearRegression()
lr.fit(x_train_s , y_train)
# 訓練後的輸入端模型係數,若是label有兩個,即y值有兩列,那麼是一個2D的array
print(lr.coef_)
# 截距
print(lr.intercept_)

# 用模型預測,並計算得分
y_predict = lr.predict(x_test_s)
test_accuracy = lr.score(x_test_s, y_test)

print("正確率爲%s%%" % test_accuracy)

# 預測值和實際值畫圖比較
t = np.arange(len(x_test_s))
# 建一個畫布,facecolor是背景色
plt.figure(facecolor='W')
plt.plot(t, y_test, 'r-', linewidth= 2, label = '真實值')
plt.plot(t, y_predict, 'b-', linewidth= 1, label = '預測值')
# 顯示圖例,設置圖例的位置
plt.legend(loc= 'upper left')
plt.title("線性迴歸預測真實值之間的關係", fontsize = 20)
# 加網格
plt.grid(b = True)
plt.show()

  結果展現:

下面這個例子,從數據產生,到數據提取,數據標準化
模型訓練和評估來講明各個API的調用過程


[0.28299035 0.3138177  0.29522932 0.30125841 0.28402977 0.28989605]
[0.53282546 0.51045318 0.48925819 0.5202345  0.4989613  0.54335117]
[[0.29202621 0.5787407  0.87280326 1.12786483 1.50043119 1.73271735]]
[10.38441274]

正確率爲0.9706613427158242%

  

完整代碼及其數據,請移步小編的GitHub

  傳送門:請點擊我

  若是點擊有誤:https://github.com/LeBron-Jian/MachineLearningNote

 

 

參考:https://www.cnblogs.com/bonelee/p/7253508.html

https://blog.csdn.net/c406495762/article/details/77851973

相關文章
相關標籤/搜索