機器學習Logistic迴歸

一、Logistic迴歸

       使用一條直線對一些數據點進行擬合(該線稱爲最佳擬合線),這個擬合過程叫做迴歸。利用Logistic迴歸進行分類的主要思想是:根據現有數據對分類邊界線建立迴歸公式,以此進行分類。訓練Logistic分類器的做法就是尋找最佳擬合參數,使用的是最優化算法(梯度上升算法以及改進的隨機梯度上升算法)。基於Python實現的完整Logistic代碼連接:https://download.csdn.net/download/pcb931126/10874534

二、基於Logistic迴歸和Sigmoid函數的分類

       在Logistic迴歸中我們使用Sigmoid函數替代單位階躍函數,進行運算。Sigmoid函數具體計算公式如下:
                            
                           σ(z)=11+ez\sigma(z)=\frac{1}{1+e^{-z}}

       下圖給出了Sigmoid函數在不同座標尺度下的兩條曲線圖。因此,爲了實現Logistic迴歸分類器,我們可以在在每個特徵上都乘以一個迴歸係數,然後把所有結果都相加,將總和帶入到Sigmoid函數中,進而得到一個在0~1之間的數值。任何大於0.5的數據被分入1類,小於0.5的被歸入0類。所以Logistic迴歸可以看做是一種概率估計。在這裏插入圖片描述

三、基於最優方法的最佳迴歸係數的確定

       Sigmoid函數的輸入爲zz,由下面的公式可以得出:
                    
                    z=w0x0+w1x1+w2x2+...+wnxnz=w_{0}x_{0}+w_{1}x_{1}+w_{2}x_{2}+...+w_{n}x_{n}

       如果採用向量的寫法,上述公式可以寫成z=wTxz=w^{T}x,它將表示將這兩個數值向量對應元素相乘然後全部加起來得到zz值。其中的向量xx是分類器的輸入數據,向量ww也就是我們要找到的最佳迴歸係數,從而使得分類器儘可能的精確。

3.1 梯度上升法

       梯度上升法基於的思想是:找到某函數的最大值,最好的方法是沿着該函數的梯度方向探尋。函數梯度上升的梯度算子總是指向函數值增長最快的方向,每一次移動一個步長α\alpha ,到下一個點重新計算該點的梯度。因此梯度上升法的迭代公式如下:
                          
                          w:=w+αΔwf(w))w:=w+\alpha \Delta _{w}f(w))

       該將公式將一直被迭代,直到達到某個停止條件爲止,比如迭代次數達到某個指定值或者算達到某個可以允許的誤差範圍。

3.2 訓練算法;使用梯度上升找到最佳參數

       梯度上升的僞代碼如下:
         
         每個迴歸係數初始化爲1
         重複RR次:
           計算整個數據集的梯度
           使用alphagradientalpha*gradient更新迴歸係數的向量
         返回迴歸係數

       基於python實現的代碼如下

def gradAscent(dataMatIn,classLabels):
    dataMatrix=mat(dataMatIn)                               #將數組轉轉成矩陣(100*3)
    labelMat=mat(classLabels).transpose()                   #將行向量轉置成列向量
    m,n=shape(dataMatrix)                                   #得到dataMatrix矩陣的行和列
    alpha=0.001                                             #初始化迴歸係數,向目標移動的步長
    maxCycles=500                                           #最大迭代次數
    weights=ones((n,1))                                     #初始換權重係數,有多個特徵向量,就會有多少個權重係數
    for k in range(maxCycles):
        h=sigmoid(dataMatrix*weights)                       #矩陣對應位置相乘,將得到的值作爲Sigmoid的輸入,得到類別標籤
        error=(labelMat-h)                                  #將得到的類別標籤與真實的類別標籤相減得到誤差
        weights=weights+alpha*dataMatrix.transpose()*error  #權重更新
    return  weights

       其分類結果使用mayplotlib畫出分類邊界以及原始數據分佈如下圖所示
在這裏插入圖片描述

3.3 改進算法:改進隨機梯度上升法

       梯度上升算法在每次更新迴歸係數時都需要遍歷整個數據集,檔處理商量數據集時尚可,當處理大數據集時該方法的複雜度就太高了。一種改進的隨機梯度上升的方法是一次隨機選擇一個樣本點來更新迴歸係數。由於可以在新的樣本到來時對分類器進行增量式更新,因此隨機梯度上升算法是一個在線學習算法。改進隨機上升算法的僞代碼如下所示:  
         每個迴歸係數初始化爲1
         隨機選擇數據集中的一個樣本:
           按照迭代次數調整步長
           計算該樣本的梯度
           使用alphagradientalpha*gradient更新迴歸係數的向量
           刪除該樣本的索引
         返回迴歸係數值
       基於python實現的代碼如下:

def stocGradAscent0(dataMatrix,classLabels,numIter=150):
    m,n=shape(dataMatrix)
    weightChange=[]                                        #記錄權重變換量
    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                         #alpha每次迭代時都會調整,這樣就會緩解數據波動或者高頻振動
            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])                      #將使用過的值從列表中刪除,在進行下一次迭代
            weightChange.append(weights)
    return weights,weightChange

       其分類結果使用mayplotlib畫出分類邊界以及原始數據分佈如下圖所示
在這裏插入圖片描述
        從分類結果可以看出,改進算的隨機上升梯度算法和梯度上升算法的分類效果基本相同,但是計算的複雜度大大降低,在計算大數據集的時候可以節省大量的時間。改進的隨機梯度上升方法權值更新的變化如下圖所示:                                                     在這裏插入圖片描述       由權值變化可知,改進隨機梯度上升算法的步長alphaalpha每次迭代都會調整,這回環節數據的波動或者高頻波動。並且通過隨機選擇樣本來更新迴歸係數,這將減少週期性波動。

四、Logistic完整的代碼


"""
機器學習Logistic迴歸
姓名:pcb
時間:2018.12.24
"""

from numpy import *
import matplotlib.pyplot as plt
from numpy import exp


"""
加載待分類文本函數
"""
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

"""
Sigmoid函數實現
"""
def sigmoid(inX):
    if inX>=0:
        return 1.0/(1+exp(-inX))
    else:
        return exp(inX)/(1+exp(inX))


#--------Logistic迴歸梯度上升優化算法-----------------------------------------------

"""
Logistic迴歸梯度上升優化算法
"""
def gradAscent(dataMatIn,classLabels):
    dataMatrix=mat(dataMatIn)                               #將數組轉轉成矩陣(100*3)
    labelMat=mat(classLabels).transpose()                   #將行向量轉置成列向量
    m,n=shape(dataMatrix)                                   #得到dataMatrix矩陣的行和列
    alpha=0.001                                             #初始化迴歸係數,向目標移動的步長
    maxCycles=500                                           #最大迭代次數
    weights=ones((n,1))                                     #初始換權重係數,有多個特徵向量,就會有多少個權重係數
    for k in range(maxCycles):
        h=sigmoid(dataMatrix*weights)                       #矩陣對應位置相乘,將得到的值作爲Sigmoid的輸入,得到類別標籤
        error=(labelMat-h)                                  #將得到的類別標籤與真實的類別標籤相減得到誤差
        weights=weights+alpha*dataMatrix.transpose()*error  #權重更新
    return  weights

#------------------------------------------------------------------------------


#--------Logistic迴歸改進的隨機梯度上升優化算法-----------------------------------------

#爲了減少計算複雜度,一次隨機選取一個樣本點,使用這個一個樣本點進行迴歸係數的更新
def stocGradAscent0(dataMatrix,classLabels,numIter=150):
    m,n=shape(dataMatrix)
    weightChange=[]                                        #記錄權重變換量
    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                         #alpha每次迭代時都會調整,這樣就會緩解數據波動或者高頻振動
            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])                      #將使用過的值從列表中刪除,在進行下一次迭代
            weightChange.append(weights)
    return weights,weightChange

#------------------------------------------------------------------------------

#---------示例:從疝氣病症預測病馬死亡率------------------------------------------
"""
分類函數,以迴歸係數和特徵向量作爲輸入來計算對應的Sigmoid函數值
"""
def classifyVector(inX,weights):
    prob=sigmoid(sum(inX*weights))
    if prob>0.5:
        return 1.0
    else:
        return 0.0

"""
用於打開測試集和訓練集,並對數據進行格式化處理的函數
"""
def coliTest():
    frTrain = open('horseColicTraining.txt')
    frTest = open('horseColicTest.txt')
    trainingSet=[];trainingLabel=[]
    for line in frTrain.readlines():
        currLine=line.strip().split('\t')
        lineArr=[]
        for i in range(21):
            lineArr.append(float(currLine[i]))
        trainingSet.append(lineArr)
        trainingLabel.append(float(currLine[21]))
    trainsWeight,trianWeightChanges=stocGradAscent0(array(trainingSet),trainingLabel,500)
    errorCount=0;numTestVec=0.0

    for line in frTest.readlines():
        numTestVec+=1.0
        currLine1=line.strip().split('\t')
        lineArr1=[]
        for i in range(21):
            lineArr1.append( float(currLine1[i]))
        if int(classifyVector(array(lineArr1),trainsWeight))!=int(currLine1[21]):
            errorCount+=1

    errorRate=float(errorCount)/numTestVec
    print('the error rate of this test is:%f'%errorRate)
    return errorRate

def multiTest():
    numTests=10;errorSum=0.0
    for k in range(numTests):
        errorSum+=coliTest()
    print('after %d iteraTions the average error rate is: %f'%(numTests,errorSum/float(numTests)))


#-----------------------------------------------------------------------------

#--------畫出數據集和Logistic迴歸最佳擬合直線的函數--------------------------------
"""
畫出數據集和Logistic迴歸最佳擬合直線的函數
"""
def plotBestFit(weights):
    dataMat,labelMat=loadDataSet()
    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.savefig('Logistic迴歸梯度上升優化算法.png')
    plt.show()

#----------------------------------------------------------------------------

#-----------權重變量X0,X1,X2的變化圖-------------------------------------------
def plotWeightChange(weightsChange):
    input_value=range(0,4000)
    m,n=shape(weightsChange)
    X0=[];X1=[];X2=[]
    for i in range(4000):
        X0.append(weightsChange[i][0])
        X1.append(weightsChange[i][1])
        X2.append(weightsChange[i][2])

    #第一張圖,關於權重X0的變化情況
    fig1=plt.figure(num=1,figsize=(10,10),dpi=400)
    ax1 = fig1.add_subplot(3,1,1)
    ax2 = fig1.add_subplot(3,1,2)
    ax3 = fig1.add_subplot(3,1,3)

    ax1.plot(input_value,X0)
    ax1.set_ylabel('X0',fontsize=15)
    ax1.set_xlim(0, 4000)                      # 設置橫軸範圍,單獨給圖1設置x軸的範圍
    ax1.set_ylim(-2, 14)                       # 設置橫軸範圍,單獨給圖1設置y軸的範圍

    #第二張圖,關於X1的變化情況
    ax2.plot(input_value,X1)
    ax2.set_ylabel('X1',fontsize=15)
    ax2.set_xlim(0, 4000)                      # 設置橫軸範圍,單獨給圖1設置x軸的範圍
    ax2.set_ylim(-3, 5)                        # 設置橫軸範圍,單獨給圖1設置y軸的範圍

    #第三張圖,關於X2的變化情況
    ax3.plot(input_value,X2)
    ax3.set_ylabel('X2',fontsize=15)
    ax3.set_xlim(0, 4000)                      # 設置橫軸範圍,單獨給圖1設置x軸的範圍
    ax3.set_ylim(-30, 10)  # 設置橫軸範圍,單獨給圖1設置y軸的範圍

    plt.savefig('Logistic迴歸改進的隨機梯度上升優化算法權重變化曲線圖.png')
    plt.show()
#----------------------------------------------------------------------------


def main():

# #1.-------對Logistic迴歸梯度上升優化算法測試並畫出擬合曲線以及分類結果圖-----------
#     dataArr,labelMat=loadDataSet()
#     weights,weightsChange=gradAscent(dataArr,labelMat)
#     print(weights)
#     plotBestFit(weights.getA())                 #getA()函數與mat()函數的功能相反,是將一個numpy矩陣轉換爲數組
# #---------------------------------------------------------------------------

# #2.------Logistic迴歸隨機梯度上升優化算法進行測試-------------------------------
#     dataArr,labelMat=loadDataSet()
#     weights1,weightsChange1=stocGradAscent0(array(dataArr),labelMat)
#     plotWeightChange(weightsChange1)
#     plotBestFit(weights1)
# #---------------------------------------------------------------------------

#3.--------示例:從疝氣病症預測病馬死亡率----------------------------------------
    multiTest()
#----------------------------------------------------------------------------

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