邏輯回歸

初探logistic回歸

優(yōu)點:計算代價低,易于實現(xiàn)。
》缺點:容易欠擬合,分類精度可能不高,只能處理二分類問題且必須線性可分。

<!- more-->

1.1 何為logistic回歸

邏輯回歸的用途十分廣泛,主要用來進(jìn)行預(yù)測,也被看做是一種概率估計。這一點可以參考吳軍老師的《數(shù)學(xué)之美》——第28章 邏輯回歸和搜索廣告,還有劉鵬老師的《計算廣告》。


計算廣告
計算廣告

數(shù)學(xué)之美
數(shù)學(xué)之美

邏輯回歸模型是將一個事件出現(xiàn)的概率適應(yīng)到一條邏輯曲線上。邏輯曲線是一條S形的曲線,其特點是開始變化快,逐漸減慢,最后飽和。邏輯自回歸的好處是它的變量是從負(fù)無窮到正無窮,而值域范圍限制在0-1之間。我們知道對應(yīng)于[0,1]之間的函數(shù)可以是一個概率函數(shù),這樣邏輯回歸函數(shù)就可以和一個概率分別聯(lián)系起來了。
強調(diào)一下回歸的定義:

假設(shè)現(xiàn)在有一些數(shù)據(jù)點,用一條直線對這些點進(jìn)行擬合的話,這個擬合的過程就是回歸。

本部分會涉及到最優(yōu)化算法,在此做一簡單的介紹:
最優(yōu)化算法有許多種,在維基上可以列出的就有如下多達(dá)15種算法:
共軛梯度法
梯度下降法
線搜索
動態(tài)規(guī)劃
協(xié)同優(yōu)化算法
坐標(biāo)下降法
差異進(jìn)化算法
序列最小優(yōu)化算法
模擬退火法
次梯度法
牛頓法
禁忌搜索法
粒子群演算法
維特比算法
遺傳算法


其中比較常用的是梯度下降法,牛頓法,共軛梯度法,還有啟發(fā)式算法(啟發(fā)式優(yōu)化方法種類繁多,包括經(jīng)典的模擬退火方法、遺傳算法、蟻群算法以及粒子群算法等等)。
最優(yōu)化算法可謂是機器學(xué)習(xí)的基石,大部分的機器學(xué)習(xí)算法的本質(zhì)都是建立優(yōu)化模型,通過最優(yōu)化方法對目標(biāo)函數(shù)(或損失函數(shù))進(jìn)行優(yōu)化,從而訓(xùn)練出最好的模型。
本文探討的logistic回歸的主要思想就是:

根據(jù)現(xiàn)有數(shù)據(jù)對分類邊界線建立回歸公式,以此進(jìn)行數(shù)據(jù)的分類。
可以注意到,我們建立的是回歸公式。“回歸”源于最佳擬合,這意味著我們要找到最佳的參數(shù)集,這個參數(shù)集需要我們通過數(shù)據(jù)訓(xùn)練得到。

在這里,我們訓(xùn)練的方法將使用梯度上升法,還有它的改進(jìn)版本,隨機梯度上升法。


梯度上升法

1.2 構(gòu)造分類器

為什么不使用海維賽德階躍函數(shù)(單位階躍函數(shù)):
在跳躍點上瞬間從0跳躍到1,這個過程太激凸...
我們用sigmoid函數(shù),溫和不刺激~


SigmoidFunction

這樣我們就有了基本的構(gòu)件模型,下一步就是函數(shù)的輸入z
我們將特征變量乘以回歸系數(shù)再全部累加得到Z:

z=w0x0 + w1x1 + w2x2 .... + wnxn

其輸出分大于0.5和小于0.5,表示兩個類別,也就實現(xiàn)了分類,確定了分類器的函數(shù)形式,接下來問題就是求最佳回歸系數(shù)。(梯度上升法,隨機梯度上升法)
主要思想:要找到某函數(shù)的最大值,最好的辦法是沿著該函數(shù)的梯度方向探尋。


梯度是矢量,總是沿著函數(shù)值上升最快的方向移動,因此我們沿著梯度方向或者反方向行進(jìn)時,就能達(dá)到一個函數(shù)的最大值(梯度上升法)或者最小值(梯度下降法),因此梯度上升算法就是不斷更新梯度值,直到梯度不再變化或者變化很小,即函數(shù)達(dá)到了最大值
梯度算法的迭代公式為(alpha為步長,即每一步移動量):


而我們常說的梯度下降法就是將其中的加號換為減號。

梯度上升法用來尋找最大值,而梯度下降法用來尋找最小值。

from numpy import *
import matplotlib.pyplot as plt
def loadDataSet():
    dataMat = []; labelMat = []
    fr = open('testSet.txt')
    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):
    dataMatrix = mat(dataMatIn)
    labelMat = mat(classLabels).transpose() #矩陣轉(zhuǎn)置
    m,n = shape(dataMatrix)
    alpha = 0.001
    maxCycles = 500
    weights = ones((n, 1))
    for k in range(maxCycles):#注意range用法,此處是從0到maxCycles而不含maxCycles
        h = sigmoid(dataMatrix*weights)
        error = (labelMat - h)
        weights = weights + alpha * dataMatrix.transpose()*error
    return weights


def plotBestFit(weights):
    dataMat, labelMat = loadDataSet()
    dataArr = array(dataMat)
    n = shape(dataArr)[0]
    xcode1 = []; ycode1 = []
    xcode2 = []; ycode2 = []
    for  i in range(n):
        if int(labelMat[i]) == 1:
            xcode1.append(dataArr[i, 1]); ycode1.append(dataArr[i, 2])
        else:
            xcode2.append(dataArr[i, 1]); ycode2.append(dataArr[i, 2])
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.scatter(xcode1, ycode1, s = 30, c = 'red', marker = 's')
    ax.scatter(xcode2, ycode2, 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()


def main():
    dataArr, labelMat =loadDataSet()
    weights = gradAscent(dataArr, labelMat)
    plotBestFit(weights.getA())#getA()將會返回矩陣的array版本



if __name__ == "__main__":
    main()

可以看到劃分的結(jié)果:


梯度上升法劃分結(jié)果

那么,該如何提升呢?
由于計算最佳擬合曲線的系數(shù)時,梯度上升法都要遍歷整個數(shù)據(jù)集,當(dāng)數(shù)據(jù)量十分巨大時,很難想象性能是多么差,于是我們引入一種在線學(xué)習(xí)方法,說白了就是增量式的更新系數(shù)。
我們叫隨機梯度上升法。

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

結(jié)果如下圖所示:


隨機梯度上升法

我們會發(fā)現(xiàn),數(shù)據(jù)的分類比較糟糕,并沒有達(dá)到理想的效果,原因是它的迭代次數(shù)遠(yuǎn)沒有梯度上升法的500次多,需要對其進(jìn)一步修改。

def stocGradAscent1(dataMatrix, classLabels, numIter=150):
    m,n = shape(dataMatrix)
    weights = ones(n)   #initialize to all ones
    for j in range(numIter):
        dataIndex = range(m)
        for i in range(m):
            alpha = 4/(1.0+j+i)+0.0001    #apha decreases with iteration, does not 
            randIndex = int(random.uniform(0,len(dataIndex)))#go to 0 because of the constant
            h = sigmoid(sum(dataMatrix[randIndex]*weights))
            error = classLabels[randIndex] - h
            weights = weights + alpha * error * dataMatrix[randIndex]
            del(dataIndex[randIndex])
    return weights

經(jīng)過修改之后再運行可得結(jié)果如下圖所示:


改進(jìn)的隨機梯度上升法

最后,我們想要預(yù)測一下病馬的死亡率,根據(jù)給出的數(shù)據(jù)文件horseColicTraining.txt和horseColicTest.txt,在看過數(shù)據(jù)之后會發(fā)現(xiàn)這是一個臟數(shù)據(jù),需要對數(shù)據(jù)進(jìn)行預(yù)處理即數(shù)據(jù)清洗,將缺失數(shù)據(jù)項的數(shù)據(jù)進(jìn)行補充,缺失標(biāo)簽的進(jìn)行剔除。

def classifyVector(inX, weights):#計算sigmoid的值,輸入為特征向量和回歸系數(shù)
    prob = sigmoid(sum(inX * weights))
    if prob > 0.5:
        return 1.0
    else:
        return 0.0


def colicTest():#用于打開測試集和訓(xùn)練集,并對數(shù)據(jù)進(jìn)行格式化處理
    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, 1000)
    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


def multiTest():#調(diào)用colicTest()10次并求平均值
    numTests = 10;
    errorSum = 0.0
    for k in range(numTests):
        errorSum += colicTest()
    print "after %d iterations the average error rate is: %f" % (numTests, errorSum / float(numTests))

def main():
    # dataArr, labelMat =loadDataSet()
    # weights = stocGradAscent1(array(dataArr), labelMat)
    # plotBestFit(weights)
    multiTest()
if __name__ == "__main__":
    main()

邏輯回歸的目的在于尋找一開始我們找到的sigmoid階躍函數(shù)的最佳擬合系數(shù),而尋找的方法使用了最優(yōu)化方法(梯度上升法/梯度下降法)。邏輯回歸主要適用于對數(shù)據(jù)進(jìn)行分類,而后在分類的基礎(chǔ)上進(jìn)行進(jìn)一步分析。
(完)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容