1. 程式人生 > >Logistic回歸模型和Python實現

Logistic回歸模型和Python實現

logistic rip ast 步長 glm 常見 gist nes sel

回歸分析是研究變量之間定量關系的一種統計學方法,具有廣泛的應用。

Logistic回歸模型

線性回歸

先從線性回歸模型開始,線性回歸是最基本的回歸模型,它使用線性函數描述兩個變量之間的關系,將連續或離散的自變量映射到連續的實數域。

模型數學形式:

技術分享

引入損失函數(loss function,也稱為錯誤函數)描述模型擬合程度:

技術分享

使J(w)最小,求解優化問題得到最佳參數。

Logistic回歸

logistic回歸(Logistic regression 或 logit regression)有時也被譯為"邏輯回歸",不過它和"邏輯"並沒有太大關系應該只是音譯。從內容來講,它最合適的名字應該是logit回歸。

logistic回歸模型更多的被用於概率分類器中。線性回歸將自變量映射到連續的實數,在很多情況下因變量的取值是在有限的區間中的,最常見的如概率問題的0-1區間。

Sigmod函數提供了一個從實數域到(0,1)的映射:

技術分享

該函數如圖:

技術分享

以數學形式給出把線性模型映射到0-1的方式:

技術分享

逆變換:

技術分享

這個變換被稱為logit變換,或許就是該模型名字的來源。

logistic回歸通常被用做概率分類器,以p=0.5作為分解線。

求解規劃模型

最小二乘法

最小二乘法通過數學推導得到全局最優解的表達式,是一種完全數學描述的方法,直接給出求解公式。

最小二乘法可以得到全局最優解,但是因涉及超大矩陣的求逆運算而難以求解。

技術分享

梯度下降(上升)法:

梯度下降法是一種典型的貪心算法,它從任意一組參數開始,向著使目標函數最小的方向調整參數,直至無法使目標函數繼續下降時,停止計算。

多元函數微積分中, 梯度指向函數值變化最快方向的向量. 梯度下降法無法保證的得到全局最優解

梯度下降法有批量梯度下降法和隨機梯度下降法兩種實現方法。

批量梯度下降(上升)法(Batch Gradient Descent/Ascent)

批量梯度下降法的算法流程:

初始化回歸系數為1
重復執行直至收斂 {
    計算整個數據集的梯度
    按照遞推公式更新回歸梯度
}
返回最優回歸系數值

將損失函數J(w)求偏導,得到J(w)的梯度。以矩陣形式給出:

技術分享

alpha是下降步長,由叠代公式:

技術分享

隨機梯度下降(上升)法(stochastic gradient Descent/Ascent)

隨機梯度下降法的算法流程:

初始化回歸系數為1
重復執行直至收斂 {
    對每一個訓練樣本{
        計算樣本的梯度
        按照遞推公式更新回歸梯度
    }
}
返回最優回歸系數值

為了加快收斂速度,做出兩個改進:

(1)在每次叠代時,調整更新步長alpha的值。隨著叠代的進行,alpha越來越小

(2)每次叠代改變樣本的順序,也就是隨機選擇樣本來更新回歸系數

Logistic 回歸的實現

訓練數據testSet.txt,包含m行n+1列:

m行代表m條數據,每條數據前n列代表n個樣本,第n+1列代表分類標簽(0或1)。

Python:

分類器被封裝在類中:

from numpy import *
import matplotlib.pyplot as plt

def sigmoid(X):
       return 1.0/(1+exp(-X))

class logRegressClassifier(object):
    
    def __init__(self):
        self.dataMat = list()
        self.labelMat = list()
        self.weights = list()

    def loadDataSet(self, filename):
        fr = open(filename)
        for line in fr.readlines():
            lineArr = line.strip().split()
            dataLine = [1.0]
            for i in lineArr:
                dataLine.append(float(i))
            label = dataLine.pop() # pop the last column referring to  label
            self.dataMat.append(dataLine)
            self.labelMat.append(int(label))
        self.dataMat = mat(self.dataMat)
        self.labelMat = mat(self.labelMat).transpose()
    
    def train(self):
        self.weights = self.stocGradAscent1()

    def batchGradAscent(self):
        m,n = shape(self.dataMat)
        alpha = 0.001
        maxCycles = 500
        weights = ones((n,1))
        for k in range(maxCycles):              #heavy on matrix operations
            h = sigmoid(self.dataMat * weights)     #matrix mult
            error = (self.labelMat - h)              #vector subtraction
            weights += alpha * self.dataMat.transpose() * error #matrix mult
        return weights

    def stocGradAscent1(self):
        m,n = shape(self.dataMat)
        alpha = 0.01
        weights = ones((n,1))   #initialize to all ones
        for i in range(m):
            h = sigmoid(sum(self.dataMat[i] * weights))
            error = self.labelMat[i] - h
            weights += (alpha * error * self.dataMat[i]).transpose()
        return weights

    def stocGradAscent2(self): 
        numIter = 2
        m,n = shape(self.dataMat)
        weights = ones((n,1))   #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(self.dataMat[randIndex] * weights) )
                error = self.labelMat[randIndex] - h
                weights += (alpha * error * self.dataMat[randIndex]).transpose()
                del(dataIndex[randIndex])
        return weights

    def classify(self, X):
        prob = sigmoid(sum( X * self.weights))
        if prob > 0.5:
            return 1.0
        else: 
            return 0.0

    def test(self):
        self.loadDataSet(‘testData.dat‘)
        weights0 = self.batchGradAscent()
        weights1 = self.stocGradAscent1()
        weights2 = self.stocGradAscent2()
        print(‘batchGradAscent:‘, weights0)
        print(‘stocGradAscent0:‘, weights1)
        print(‘stocGradAscent1:‘, weights2)

if __name__ == ‘__main__‘:
    lr = logRegressClassifier()
    lr.test()

Matlab

上述Python代碼用Matlab實現並不難(只是需要拆掉類封裝),只是Matlab的廣義線性模型工具箱提供了Logistic模型的實現。

trainData = [0 1; -1 0; 2 2; 3 3; -2 -1;-4.5 -4; 2 -1; -1 -3];
group = [1 1 0 0 1 1 0 0]‘;
testData = [5 2;3 1;-4 -3];
[testNum, attrNum] = size(testData);
testData2 = [ones(testNum,1), testData];
B = glmfit(trainData, [group ones(size(group))],‘binomial‘, ‘link‘, ‘logit‘)
p = 1.0 ./ (1 + exp(- testData2 * B))

B = glmfit(X, [Y N],‘binomial‘, ‘link‘, ‘logit‘)

X參數為特征行向量組, Y為代表預先分組的列向量,N是一個與Y同型的向量,Y(i)的在[0 N(i)]範圍內取值。

B為[1, x1, x2,...]的系數,測試數據的第一列被加上了1。

p = 1.0 ./ (1 + exp(- testData2 * B))

代入sigmoid函數求解。

Logistic回歸模型和Python實現