天天看點

《機器學習實戰》 (3) logistic regression(邏輯斯蒂回歸)小結(下)

在上一講中,通過數學推導,将logistics回歸的數學原理推導了一遍,并用推導得到的偏導具體表達式求了出來,同時,通過Python實作了在資料集上上的logistics回歸算法的基本應用,但是,基本的logistics回歸是對所有資料求偏導的之和,是以需要通過傳統方法求得的曲線雖然準确,但是每次實作運算量過大,是以需要對算法進行優化。

這裡提出的第一種算法是SGD,随機梯度上升算法,該算法每次運算時,需要通過在資料集中任意選出一個資料,求出該資料的偏導,來對資料進行更新。通過這一種方法可以大大加快運算速度,同時,該方法得到的資料由于新樣本來臨時,可以進行批量更新,故又是一個批量更新算法。該算法的函數如下所示

def stocGranfAscent0(dataMatrix,classLabels):#SGD函數
    m,n=np.shape(dataMatrix)#擷取資料集的長寬
    alpha=0.01#每次的步率
    weights=np.ones(n)#權重矩陣,賦初值全為1
    for i in range(m):#做m次SGD
        h=sigmoid(sum(dataMatrix[i]*weights))#每一行的資料和做激活函數運算,即輸出的類
        error=classLabels[i]-h#實際值與運算值做內插補點
        weights=weights+alpha*error*dataMatrix[i]#代入weights的疊代公式進行疊代
    return weights
           

将上面的函數載入,獲得200次運作的結果

from numpy import *
reload (logRegres)
dataArr,labelMat=logRegres.loadDataSet()
weights=logRegres.stocGranfAscent0(array(dataArr),labelMat)
logRegres.plotBestFit(weights)
           

生成的分割曲線是

《機器學習實戰》 (3) logistic regression(邏輯斯蒂回歸)小結(下)

觀察圖像不難發現圖像相對于原先全梯度上升法,準确率下降了不少,同時,運算速度也快了一些。

其實,在嘗試其他不同的疊代次數後可以發現,在經過500次疊代後的分割曲線會達到穩定值,這是因為在如果遇到沒有被正确分類的樣本點時,會在疊代時造成巨大的波動,這主要是由于每次所取的點過少導緻的,是以單次隻取一個點的做法實際上并不實際。

第二種方法相對于之前的SGD進行了進一步,首先每次疊代的步長發生了變化,把梯度上升比作下山的話,一般在斜率大的地方一般也會較陡峭,實際距離較大,而在接近底部的時候,往往會較平緩,此時每走一步的步長相對就會較小,基于這一想法,把步長從靜态的恒定值變為前期較大,後期較小,這樣既可以保證在一定的疊代次數以内就能實作找到最小值的目的,同時,還可以通過在後期減小步長實作收斂,防止在最小值附近震蕩。第二,通過随機選取參數實作參數更新疊代,其函數如下所示

def stocGranfAscent1(dataMatrix,classLabels,numIter=1500):#DSGD函數
    m,n=np.shape(dataMatrix)
    weights=np.ones(n)
    for j in range(numIter):
        dataIndex=list(range(m))#将所有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])#每計算一次權重更新後将剛剛用的m内的值删除,即不會再用 
    return weights
           

将其載入到jupyter notebook裡

reload (logRegres)
dataArr,labelMat=logRegres.loadDataSet()
weights=logRegres.stocGranfAscent1(array(dataArr),labelMat)
logRegres.plotBestFit(weights)
           

得到的圖像是

《機器學習實戰》 (3) logistic regression(邏輯斯蒂回歸)小結(下)

從圖中不難發現此時的回歸曲線分類正确率已經大大上升了。

下面,我們将随機梯度上升方法應用到病馬生死預測問題上。

這個問題是真正意義上第一次我們學習着如何通過現有的一些特征來學習如何處理殘缺值。

問題的情境是如果一頭馬患有疝病,則需要通過一些影響患病的因素來判斷患病馬的死亡率。

一個實際資料分析問題的流程分為如下幾步:

一.收集資料,首先需要提取需要的特征,根據不同的特征需要來提取資料,這一步非常重要,因為特征的選擇與收集決定了一個模型的上限,而無論采用何種算法本質上是對這些資料上限的逼近,本案例資料的訓練集和驗證集已經整理好了,是以這次不用操心。

二.整理資料,将收集到的資料進行整理,實際生活中收集到的資料往往是有殘缺的,如何對殘缺資料進行處理就是整理資料的主要工作。資料的缺失值需要根據不同的情況來進行分析,如果一個特征的資料出現缺失,常見的處理方法是:1.使用資料均值填補缺失值,2.使用特殊值來填補缺失值,3.講有缺失的樣本忽略。4.使用相似樣本來填補缺失值5.使用其他機器學習算法實作缺失值的預測。

在本實驗中,對于x與y要分别考慮,如果x出現缺失,需要通過将該缺失值以0來代替,這樣做的好處是在特征值運算中,該特征對應值相乘後為0,對實際效果無影響,同時,0帶入sigmoid函數輸出值為0.5,代表的種類恰好在0與1之間,無任何偏向性,是以不會産生影響,而對于y而言,一旦該特征的某一個資料标簽為0,則需要将該資料抛棄,這是因為标簽很難在無提示條件下進行判斷,是以将其舍棄。

這樣将之前的算法進行處理,獲得必要的資訊進行分類,具體函數如下

def classifyVector(inX,weights):
                   prob=sigmoid(sum(inX*weights))
                   if prob>0.5: return 1.0
                   else: return 0
                   
def colicTest():
    frTrain=open('machinelearning/Ch05/horseColicTraining.txt')
    frTest=open('machinelearning/Ch05/horseColicTest.txt')
    trainingSet=[]
    trainingLabels=[]
    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]))
    trainWeights = stocGranfAscent1(np.array(trainingSet), trainingLabels, 50000)        #使用改進的随即上升梯度訓練
    errorCount = 0; numTestVec = 0.0
    for line in frTest.readlines():
        numTestVec += 1.0
        currLine = line.strip().split('\t')
        lineArr =[]
        for i in range(len(currLine)-1):
            lineArr.append(float(currLine[i]))
        if int(classifyVector(np.array(lineArr), trainWeights))!= int(currLine[-1]):
            errorCount += 1
    errorRate = (float(errorCount)/numTestVec) * 100                         #錯誤率計算
    print("測試集錯誤率為: %.2f%%" % errorRate)
    return errorRate
                   
def multiTest():
    numTests=10
    errorSum=0.0
    for k in range(numTests):
        errorSum+=colicTest()
    print("after %d iteration the zverage error rate is:                  %f"%(numTests,errorSum/float(numTests)))
           

上述函數分為三部分,第一個函數是一旦某個特征sigmoid函數輸出大于0.5判定其為1類,反之為0類。第二個函數是載入測試集與訓練集,疊代500次獲得模型,再将模型在測試集上進行測試,以獲得錯誤率。第三個函數是将第二個函數重複進行三次以獲得結果的平均值。載入jupyter notebook,

reload(logRegres)
logRegres.multiTest()
           

得到結果為36.1%