簡介

Logistic迴歸的目的是尋找一個非線性函式Sigmoid的最佳擬合引數,一般使用梯度上升演算法。
對於有n個屬性的train資料集(X1,X2,...Xn),我們尋找一組迴歸係數(W0,W1,W2,...,Wn)使得函式:
sigmoid(W0+W1*X1+W2*X2+...+Wn*Xn)
最佳擬合train資料集的labels。

演算法描述

初始化迴歸係數(一般初始化為1)
Repeat(N):
    計算資料集的梯度
    使用alpha*gradient更新迴歸係數
返回迴歸係數

Logistic迴歸梯度上升優化演算法

#定義sigmoid函式
def sigmoid(inX):
    return 1.0/(1+exp(-inX))

#梯度上升優化演算法    
def gradAscent(dataMatIn , classLabels , iterNum):
    dataMatrix = mat(dataMatIn)
    labelMat = mat(classLabels).transpose()
    m,n = shape(dataMatrix)
    alpha = 0.001
    weights = ones((n,1))
    for k in range(iterNum):
        h = sigmoid(dataMatrix*weights)
        error = (labelMat - h)
        weights = weights + alpha*dataMatrix.transpose()*error
    return weights
特別說明一下,dataMatIn是原dataSet去掉最後一列(即labels),然後在第一列加入一列1之後的矩陣。
這是因為我們的迴歸引數中有一個常數項W0,這樣統一之後就可以直接在矩陣dataMatIn上進行操作。

隨機梯度上升演算法

def stocGradAscent(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
可以看到,隨機梯度上升演算法與梯度上升演算法在程式碼上很相似,但也有一些區別:
1.梯度上升演算法的變數h和誤差error都是向量;而隨機梯度上升演算法中,它們都是數值
2.隨機梯度上升演算法沒有矩陣轉換的過程,所有變數資料都是numpy陣列。

改進的隨機梯度上升演算法

def stocGradAscent1(dataMatrix, classLabels, numIter = 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#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])
    return weights
隨機梯度上升和改進的隨機梯度上升演算法的dataMatrix傳入的都是array型別的引數。

畫出決策邊界

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(0,2.0,0.1)
    #g(X) = W0 + W1*X1 +W2*X2,圖中的X,Y分別表示X1,X2
    y = (-weights[0] - weights[1] * x) / weights[2]
    ax.plot(x,y)
    plt.xlabel('X1'); plt.ylabel('X2');
    plt.show()

結果測試

資料(來源於西瓜書)

0.697,0.46,1
0.774,0.376,1
0.634,0.264,1
0.608,0.318,1
0.556,0.215,1
0.403,0.237,1
0.481,0.149,1
0.437,0.211,1
0.666,0.091,0
0.639,0.161,0
0.657,0.198,0
0.593,0.042,0
0.719,0.103,0

測試

dataMat , labelMat = loadDataSet()
weights1 = stocGradAscent1(array(dataMat),labelMat,20000)
plotBestFit(weights1)
weights2 = gradAscent(dataMat , labelMat20000)
plotBestFit(weights2.getA())

執行結果

梯度上升演算法迭代20000次

這裡寫圖片描述

改進的隨機梯度上升演算法迭代20000次

這裡寫圖片描述

注:本文主要內容來源於Peter Harrington所著的《機器學習實戰》