1. 程式人生 > >Logistic Regression及python實現

Logistic Regression及python實現

本文所有程式碼都是基於python3.6的,資料及原始碼下載:傳送門

引言

本次分享,我們將介紹一個經典的二分類演算法——邏輯迴歸。邏輯迴歸雖然不在十大資料探勘演算法之列,但是這個演算法是機器學習從統計學領域借鑑的一種演算法,其演算法最經典之處是使用到了最優化演算法。關於線性迴歸與邏輯迴歸的詳細介紹與數學推導,在我前面翻譯的CS229講義裡都有提到:CS229 Lecture Note 1(監督學習、線性迴歸)CS229 Part2 分類與邏輯迴歸。此處就不做詳細介紹了。
利用邏輯迴歸進行分類的主要思想是:根據現有資料對分類邊界線建立迴歸公式,以此進行分類。這裡的“迴歸”一詞源於最佳擬合,表示要找到最佳擬合引數,使用的是最優化演算法。

邏輯函式

邏輯迴歸,需要一個函式能接受所有的輸入然後預測出類別。於是就有了邏輯函式,邏輯函式又叫Sigmoid函式,是統計學家為了描述生態學中人口增長特性,提出的一種方法。Sigmoid函式是一個S形狀的曲線,能夠接受任意實數並將其對映到近似0,1處。圖形如下如所示:
Sigmoid
公式如下所示:

σ(z)=11+ez
確定了分類函式為Sigmoid函式後,接下來我們我們就需要確定邏輯迴歸的第二部分了“迴歸係數”——即最佳擬合引數。

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

前面我們提到了分類函式Sigmoid能夠將任意實數輸入對映到0,1上。那麼我們需要確定的是,這個任意實數輸入,該如何表示呢?根據CS229裡面的說明,我們利用一個線性函式表示,將Sigmoid函式的輸入記為z,z由下面的公式給出:

z=w0x0+w1x1+w2x2+...+wnxn
採用向量的寫法,上述公式就可以寫成z=wTx

梯度上升法

這裡我們使用的第一個優化演算法是梯度上升演算法。如果大家看了我前面翻譯的講義的話,看到這裡不免會心生疑問,為什麼在講線性迴歸時,使用的是梯度下降演算法,但是在講邏輯迴歸時使用的就是梯度上升演算法了。首先我們需要明確的一點是,梯度上升演算法求得是函式的最大值,梯度下降演算法求得是函式的最小值。線上性迴歸中,我們的目標函式是最小二乘:
LMS
;而邏輯迴歸中,我們的目標函式是似然函式:
likehood
很明顯,最小化最小二乘與最大化似然函式本質上是等價的。具體推導大家可以去看CS229講義。那麼這裡我們的梯度上升演算法的迭代公式如下所示:

w:=w+αwf(w)
這個公式將一直被迭代執行,直到達到某個停止條件為止,比如說迭代次數達到某個指定值或演算法達到某個可以允許的誤差範圍內。

利用梯度上升找到最佳引數

我們來看一個邏輯迴歸分類器的應用例子。採用的資料集如下圖所示:
logistic_train
梯度上升的虛擬碼如下所示:
gradient
程式碼如下:

def gradAscent(dataMatIn, classLabels):
    dataMatrix = mat(dataMatIn)             #convert to NumPy matrix
    labelMat = mat(classLabels).transpose() #convert to NumPy matrix
    m,n = shape(dataMatrix)
    alpha = 0.001                           #alpha固定預設設為0.001相當於learning rate
    maxCycles = 500                         #迭代結束條件
    weights = ones((n,1))
    for k in range(maxCycles):              #heavy on matrix operations
        h = sigmoid(dataMatrix*weights)     #matrix mult
        error = (labelMat - h)              #vector subtraction
        weights = weights + alpha * dataMatrix.transpose()* error #matrix mult
    return weights

分析資料,視覺化

我們利用梯度上升演算法,求到最佳迴歸係數,然後將回歸方程即決策邊界可視化出來,如下所示:
logvisual
從圖上可以看出分類的效果不錯。但是眾所周知在實際應用中,梯度上升演算法又叫作批量梯度上升,因此在每次迭代更新的過程中都是載入整個陣列進行計算,所以當資料量很大時,這個計算量就真的大了。因為為了優化這個演算法,有人提出了一個解決方法:一次僅用一個樣本點來更新迴歸係數。這個方法被稱為隨機梯度上升法,與上面普通批量梯度上升演算法對應的是這個演算法是線上學習演算法,可以在新樣本來臨時對分類器進行增量式更新。

隨機梯度上升演算法

虛擬碼如下圖所示:
randomgradient
隨機梯度上升演算法的程式碼如下所示:

def stocGradAscent0(dataMatrix, classLabels):
    m,n = shape(dataMatrix)
    alpha = 0.01
    weights = ones(n)   #initialize to all ones
    for i in range(m):
        h = sigmoid(sum(dataMatrix[i]*weights))
        error = classLabels[i] - h
        weights = weights + alpha * error * dataMatrix[i]
    return weights

分析資料,視覺化

randomg0
大家會發現,效果並不是很好,雖然說這裡只遍歷了一次資料集。這裡我們將資料集也遍歷500次看看具體效果:
randomg2
如上圖所示,這個效果看起來與上面的批量處理沒什麼差別,很明顯從數學角度看,計算量都是一樣的,並沒有體現隨機梯度上升演算法的優異性。
大家看了這個程式碼可能也會有疑問了,不是說是隨機梯度上升麼,那麼這個程式碼裡的“隨機性”在哪裡體現呢?

隨機梯度上升改進版

在實際應用中,通過每次迭代調整alpha來緩解資料高頻波動。並且通過隨機選取樣本來更新迴歸係數,減少週期性波動。
實際程式碼如下所示:

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

分析資料,視覺化

分類效果如圖:
randomg1
這樣分類效果不僅和批量處理差不多,而且收斂速度更快,迭代次數更少!

總結

邏輯迴歸演算法的目的是尋找一個非線性函式Sigmoid的最佳擬合引數,在求解過程中用最優化演算法完成。這個演算法本質上是一個線性迴歸,或者是特殊的線性迴歸。所以處理不好一些特徵相關的應用場景。邏輯迴歸在一些推薦場景中有些應用,但是效果一般吧,但是邏輯迴歸簡單快速,而且背後的概率原理經得起推敲,所以還是很受歡迎的。