機器學習實戰5--Logistic迴歸

前面的分類都是基於標籤是離散值進行的,這裏迴歸是針對標籤是連續值進行的。
假設現在有一些數據點,我們用一條直線對這些點進行擬合,這個擬合過程就是迴歸,該線就是最佳擬合直線。主要思想:根據現有數據對分類邊界線建立迴歸公式,以此進行分類。迴歸就是最佳擬合,找到最佳擬合參數集,訓練分類器的做法就是尋找最佳擬合參數,使用的是最優化算法。
找到分類迴歸係數就可以了。
1:基於logistic迴歸和Sigmoid函數的分類
基於Logistic迴歸的函數應能接受所有的輸入然後預測出類別。sigmoid函數類似於單位階躍函數,
這裏寫圖片描述
當x=0時,函數值爲0.5,x大於0,逐漸逼近於1;x小於0,逐漸逼近於0。
爲了實現sigmoid函迴歸分類器,在每個特徵上都乘以一個迴歸係數,然後所有的結果值相加,將這個總和代入sigmoid函數中,進而得到一個範圍在0-1之間的數值,任何大於0.5的數據被歸入1類,小於0.5的被歸入0類。Logistic迴歸也可以被看作是一種概率估計。
這裏寫圖片描述
g(z)的曲線是
這裏寫圖片描述

此時就可以對標籤y進行分類了,
這裏寫圖片描述
其中θTx=0 即θ0+θ1*x1+θ2*x2=0 稱爲決策邊界即boundarydecision。

2:線性迴歸的cost function是依據最小二乘法是最小化觀察值和估計值之間的差平方和。
這裏寫圖片描述
但是對於Logistic迴歸,我們的cost function不能最小化觀察值和估計值之間的差平方和,J(θ)爲非凸函數,這樣就會存在很多局部極值點,無法通過梯度迭代得到最終的參數,重新定義一種cost function:
這裏寫圖片描述
這個函數的意思是當y=1時,估計值爲1;當y=0時,估計值爲0;這樣就是預測準確了,但是若預測錯誤,就估計值會無窮大。
將上面的函數寫在一起:
這裏寫圖片描述
這樣總體損失函數就是:
這裏寫圖片描述

3:sigmoid函數的輸入記爲z,由下面公式得出:
z=w0x0+w1x1+w2x2+w3x3+…+wnxn,
寫成向量的形式:z=wTx;向量x是分類器的輸入數據,向量w是要找到的最佳參數,從而使得分類器儘可能精確。
梯度最優化方法:
要找到某函數的最大值,最好的方法是沿着該函數的梯度方向探尋。f(x,y)的梯度由下式表示:
這裏寫圖片描述
求和後得到:
這裏寫圖片描述
對於隨機化的梯度迭代每次只使用一個樣本進行參數更新,就爲:
這裏寫圖片描述
下面的代碼就是基於這個公式的。
我們知道hθ(x)≥0.5<後面簡用h>,此時y=1, 小於0.5,y=0. 那麼我們就用h作爲y=1發生的概率,那麼當y=0時,h<0.5,此時不能用h作爲y=0的概率,<因爲最大似然的思想使已有的數據發生的概率最大化,小於0.5太小了>,我們可以用1-h作爲y=0的概率,這樣就可以作爲y=0的概率了,,然後只需要最大化聯合概率密度函數就可以了。
這樣聯合概率密度函數就可以寫成:
這裏寫圖片描述
再轉換成對數似然函數,就和上面給出的似然函數一致了。
這裏寫圖片描述

4:梯度上升算法沿梯度方向移動,梯度算子總是指向函數值增長最快的方向。梯度上升優化算法的實現:

#-*- coding:utf-8 -*-
from numpy import *
#計算代價不高,易於理解和實現,容易欠擬合,分類精度不高,數據類型爲數值型和標稱型數據
def loadDataSet():
    dataMat = []; labelMat = []
    fr = open('testSet.txt')#打開文本
    for line in fr.readlines():#逐行讀取,並保存成矩陣,x0爲1
        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):#
    dataMatrix = mat(dataMatIn)#轉換成numpy矩陣數據類型
    labelMat = mat(classLabels).transpose()#把標籤轉換成行向量
    m,n = shape(dataMatrix)#m是行,n是列
    alpha = 0.001#向目標移動的步長
    maxCycles = 500#迭代次數
    weights = ones((n,1))#初始化矩陣向量,n行一列的單位矩陣
    for k in range(maxCycles):#每一次迭代
        h = sigmoid(dataMatrix*weights)#把z=wx帶入函數,求出一個列向量,代表計算出的標籤
        error = (labelMat - h)#與實際的標籤進行比較,得誤差
        weights = weights + alpha * dataMatrix.transpose() * error#迴歸係數爲原來的迴歸係數加上步長乘以數據集行列變換後乘以誤差列向量,得w1,w2,w3三個迴歸係數
    return weights

這裏寫圖片描述
給出一組數據,就可以得到迴歸係數。實現Logistic迴歸。
5:確定了迴歸係數,得到不同類別數據之間的分隔線。畫出該分隔線。

def plotBestFit(weights):#畫出迴歸線
    import matplotlib.pyplot as plt#
    dataMat,labelMat = loadDataSet()#導入數據集
    dataArr = array(dataMat)#轉換成array陣列
    n = shape(dataArr)[0]#得到行數
    xcord1 = []; ycord1 = []#設置數據點的x,y座標
    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)#x的取值範圍和步長間隔
    y = (-weights[0]-weights[1]*x)/weights[2]#針對x和迴歸係數的y的取值
    ax.plot(x,y)
    plt.xlabel('X1');plt.ylabel('X2');
    plt.show()

這裏寫圖片描述
這裏寫圖片描述

這個方法錯誤率較小,但計算量卻很大,需要300次乘法。
6:隨機梯度上升:梯度上升算法在每次更新迴歸係數時都需要遍歷整個數據集,該方法計算量太大了。改進爲隨機梯度上升法:
一次僅用一個樣本點來更新迴歸係數,在新樣本到來時對分類器進行增量式更新,實現一個在線學習算法,不是一次處理所有數據的批處理。

#隨機梯度上升算法,區別是這個的h和error是數值,而之前的是向量,這個的數據類型是numpy數組,沒有矩陣的轉換過程,可以在新數據到來時就完成參數更新,不需要讀取整個數據集進行批處理,佔用更少的資源,是在線算法
def stocGradAscent0(dataMatrix,classLabels):
    m,n = shape(dataMatrix)#得到數據集的行列數量
    alpha = 0.01#步長
    weights = ones(n)#單位行向量,行數=數據集列數
    for j in range(200):#200次迭代
        for i in range(m):#對每一行
            h = sigmoid(sum(dataMatrix[i]*weights))#求出這一行向量乘以迴歸係數,得到一個數值,帶入函數中
            error = classLabels[i] - h#分別與每行的標籤相比較,求誤差
            weights = weights + alpha*error*dataMatrix[i]#迴歸係數等於原來的加上步長乘以誤差乘以一行向量。
    return weights

這裏寫圖片描述
這裏寫圖片描述
一個判斷優化算法的優劣的可靠方法是看它是否收斂,也就是說參數是否達到了穩定值,是否還會不斷變化。

7:隨機上升算法在參數大的波動停止後,還有一些小的週期性波動。產生這種現象的原因存在一些不能正確分類的樣本點(數據集並非線性可分),在每次迭代時會引發係數的劇烈改變。我們期望算法能避免來回波動,從而收斂於某個值,另外收斂速度也要加快。

def stocGradAscent1(dataMatrix,classLabels,numIter=150):#第三個參數是迭代次數,默認是150次,用了兩種機制,步長動態減少和樣本隨機選擇,將會收斂很快,沒有高頻波動。
    m,n = shape(dataMatrix)
    weights = ones(n)
    for j in range(numIter):    
        dataIndex = range(m)
        for i in range(m):
            alpha = 4/(1.0+j+i)+0.01#這個步長在每次迭代的時候都會調整減小,但因爲有常數項,則不會減小到0,保證在多次迭代後新數據仍有影響,學習下模擬退火算法
            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

模擬退火算法避免參數的嚴格下降。
隨機採集樣本來更新迴歸參數,減少週期性波動,每次隨機從列表中選出一個值,然後從列表中刪除該值(再進行下一次迭代)。
這裏寫圖片描述
這裏寫圖片描述

8:實例:從疝氣病症預測病馬的死亡率。
在給定的數據中,用python解析文本文件並填充缺失值,因爲有些數據是缺失的,比如一個向量裏沒有全部的數據,某個傳感器損壞導致一個特徵無效等等:
使用可用特徵的均值來填補缺失值;
使用特殊值來填補缺失值如-1;
忽略有缺失的樣本;
使用相似樣本的均值填補缺失值;
使用另外的機器學習算法預測缺失值。
迴歸係數的更新公式:
weights = weights + error * dataMatrix[randIndex],
如果dataMatrix的某特徵值爲0,那麼該特徵的係數將不做更新。
測試算法:把測試集上每個特徵向量乘以最優化方法得來的迴歸係數,再將該乘積結果求和,最後輸入到sigmoid函數中即可。如果對應的Sigmoid值大於0.5就預測類別標籤爲1,否則爲0。

def classifyVector(inX,weights):#分類函數,給出20個特徵和一個標籤,包括迴歸係數,將會得到分類值
    prob = sigmoid(sum(inX*weights))
    if prob > 0.5:return 1.0
    else:return 0.0

def colicTest():#如何處理缺失數據是個難題
    frTrain = open('horseColicTraining.txt')#打開這個文本
    frTest = open('horseColicTest.txt')
    trainingSet = [];trainingLabels = []
    for line in frTrain.readlines():#逐行讀取並把每個特徵值列表化
        currLine = line.strip().split('\t')
        lineArr = []
        for i in range(21):
            lineArr.append(float(currLine[i]))
        trainingSet.append(lineArr)
        trainingLabels.append(float(currLine[21]))
    trainWeights = stocGradAscent1(array(trainingSet),trainingLabels,500)#進行訓練
    errorCount = 0;numTestVec = 0.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(classifyVector(array(lineArr),trainWeights)) != int(currLine[21]):
            errorCount += 1
    errorRate = (float(errorCount)/numTestVec)
    print "the error rate of this test is: %f" %errorRate
    return errorRate
#分類時不需做太多工作,把測試集的特徵向量乘以最優化方法得來的迴歸係數,求和,輸入到函數中大於0.5就把標籤設爲1
def multiTest():#調用10次colicTest並求結果的平均值
    numTests = 10;errorSum = 0.0
    for k in range(numTests):
        errorSum += colicTest()
    print "after %d iterrations the average error rate is:%f" %(numTests,errorSum/float(numTests))

這裏寫圖片描述

9:總結:Logistic迴歸的目的是尋找一個非線性函數Sigmoid的最佳擬合參數,求解過程可以由最優化算法來完成。在最優化算法中,最常用的就是梯度上升算法,可以簡化爲隨機梯度上升算法,性能相當,但佔用更少的計算資源。此外隨機梯度上升是一個在線算法,它可以在新數據到來時就完成參數更新,而不需要重新讀取整個數據集來進行批處理運算。
優點:計算代價不高,易於理解和實現。
缺點:容易欠擬合,分類精度可能不高。
適用數據類型:數值型和標稱型數據。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章