邏輯回歸(Logistic Regression)
概述
假設(shè)現(xiàn)在有一些數(shù)據(jù)點(diǎn)缘琅,用一條直線對(duì)這些點(diǎn)進(jìn)行擬合(該線稱(chēng)為最佳擬合直線),這個(gè)擬合過(guò)程就稱(chēng)為回歸肘迎。
利用邏輯回歸進(jìn)行分類(lèi)的主要思想是:根據(jù)現(xiàn)有數(shù)據(jù)對(duì)分類(lèi)邊界線建立回歸公式甥温,以此進(jìn)行分類(lèi)。
算法流程
收集數(shù)據(jù):采用任意方法收集數(shù)據(jù)
準(zhǔn)備數(shù)據(jù):由于需要進(jìn)行距離計(jì)算妓布,因此要求數(shù)據(jù)類(lèi)型為數(shù)值型姻蚓。另外,結(jié)構(gòu)化數(shù)據(jù)格式則最佳
分析數(shù)據(jù):采用任意方法對(duì)數(shù)據(jù)進(jìn)行分析
訓(xùn)練算法:大部分時(shí)間將用于訓(xùn)練匣沼,訓(xùn)練的目的是為了找到最佳的分類(lèi)回歸系數(shù)
測(cè)試算法:一旦訓(xùn)練步驟完成狰挡,分類(lèi)將會(huì)很快
使用算法:首先,需要輸入一些數(shù)據(jù)释涛,并將其轉(zhuǎn)換成對(duì)應(yīng)的結(jié)構(gòu)化數(shù)值加叁;接著,基于訓(xùn)練好的回歸系數(shù)就可以對(duì)這些數(shù)值進(jìn)行簡(jiǎn)單的回歸計(jì)算唇撬,判定它們屬于哪個(gè)類(lèi)別它匕;在這之后,就可以在輸出的類(lèi)別上做一些其他分析工作
基于邏輯回歸和Sigmoid函數(shù)的分類(lèi)
邏輯回歸
優(yōu)點(diǎn):計(jì)算代價(jià)不高窖认,易于理解和實(shí)現(xiàn)
缺點(diǎn):容易欠擬合超凳,分類(lèi)精度可能不高
適用數(shù)據(jù)類(lèi)型:數(shù)值型和標(biāo)稱(chēng)型數(shù)據(jù)
我們想要的函數(shù)應(yīng)該是,能接受所有的輸入然后預(yù)測(cè)出類(lèi)別耀态。例如,對(duì)于而分類(lèi)問(wèn)題暂雹,該函數(shù)應(yīng)該返回0或1首装。具有這種性質(zhì)的函數(shù)稱(chēng)為海維塞德階躍函數(shù)(Heaviside step function),或直接稱(chēng)為單位階躍函數(shù)杭跪。海維塞德階躍函數(shù)的問(wèn)題在于:該函數(shù)在跳躍點(diǎn)上從0瞬間跳躍到1仙逻,這個(gè)瞬間跳躍過(guò)程有時(shí)很難處理驰吓。
Sigmoid函數(shù)是一個(gè)S型曲線,其函數(shù)形式為:
$$\sigma(z)=\frac{1}{1+e^{-z}}$$
當(dāng)輸入z等于0時(shí)系奉,Sigmoid函數(shù)值為0.5檬贰。隨著z的增大,對(duì)應(yīng)的函數(shù)值趨近于1缺亮;隨著z的減小翁涤,對(duì)應(yīng)的函數(shù)值趨近于0。
基于最優(yōu)化方法的最佳回歸系數(shù)確定
訓(xùn)練算法:適用梯度上升找到最佳參數(shù)
梯度上升法基于的思想是:要找到某函數(shù)的最大值萌踱,最好的方法是沿著該函數(shù)的梯度方向探尋葵礼。
梯度上升法的偽代碼:
每個(gè)回歸系數(shù)初始化為1
重復(fù)R次:
計(jì)算整個(gè)數(shù)據(jù)集的梯度
適用alpha x gradient 更新回歸系數(shù)的向量
返回回歸系數(shù)
import numpy as np
def loadDataSet():
'''
加載數(shù)據(jù)集
'''
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
def sigmoid(inX):
'''
S函數(shù)
'''
return 1.0 / (1 + np.exp(-inX))
def gradAscent(dataMatIn, classLabels):
'''
梯度上升算法
param dataMatIn: 特征值
param classLabels: 標(biāo)簽
'''
# 特征值矩陣
dataMatix = np.mat(dataMatIn)
# 標(biāo)簽矩陣;行向量轉(zhuǎn)置為列向量
labelMat = np.mat(classLabels).transpose()
# 獲取特征值矩陣大小
m, n = np.shape(dataMatix)
# 移動(dòng)步長(zhǎng)
alpha = 0.001
# 迭代次數(shù)
maxCycles = 500
# 回歸系數(shù)初始化為1
weights = np.ones((n, 1))
for k in range(maxCycles):
h = sigmoid(dataMatix * weights)
error = (labelMat - h)
weights = weights + alpha * dataMatix.transpose() * error
return weights
dataArr, labelMat = loadDataSet()
weights = gradAscent(dataArr, labelMat)
weights
[out]
matrix([[ 4.12414349],
[ 0.48007329],
[-0.6168482 ]])
分析數(shù)據(jù):畫(huà)出決策邊界
def plotBestFit(weights):
'''
畫(huà)出數(shù)據(jù)集和邏輯回歸最佳擬合直線
'''
import matplotlib.pyplot as plt
dataMat, labelMat = loadDataSet()
dataArr = np.array(dataMat)
n = np.shape(dataMat)[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 = np.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.show()
plotBestFit(weights.getA())
訓(xùn)練算法:隨機(jī)梯度上升
梯度上升算法每次更新歸回系數(shù)時(shí)都需要遍歷整個(gè)數(shù)據(jù)集并鸵,數(shù)據(jù)量較小時(shí)尚可鸳粉,但如果有數(shù)十億樣本和上千萬(wàn)特征,那么該方法的計(jì)算復(fù)雜度就太高了园担。一種改進(jìn)方法是以此僅用一個(gè)樣本點(diǎn)來(lái)更新回歸系數(shù)届谈,該方法稱(chēng)為隨機(jī)梯度上升算法。由于可以在新樣本到來(lái)時(shí)對(duì)分類(lèi)器進(jìn)行增量式更新弯汰,因而隨機(jī)梯度上升算法是一個(gè)在線學(xué)習(xí)算法艰山。與“在線學(xué)習(xí)”相對(duì)應(yīng),一次處理所有數(shù)據(jù)被稱(chēng)作是“批處理”蝙泼。
隨機(jī)梯度算法偽代碼:
所有回歸系數(shù)初始化為1
對(duì)數(shù)據(jù)集中每個(gè)樣本
計(jì)算該樣本的梯度
適用 alpha x gradient 更新回歸系數(shù)值
返回回歸系數(shù)值
def stocGradAscent0(dataMatrix, classLabels):
'''
隨機(jī)梯度上升算法
'''
m, n = np.shape(dataMatrix)
alpha = 0.01
weights = np.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
測(cè)試隨機(jī)梯度上升算法
從結(jié)果上來(lái)看程剥,擬合出來(lái)的直線效果還不錯(cuò),但不像前面那么完美汤踏。這里的分類(lèi)器錯(cuò)分了三分之一的樣本织鲸。
但是前面的結(jié)果時(shí)迭代了500次才得到的。
dataArr, labelMat = loadDataSet()
weights = stocGradAscent0(np.array(dataArr), labelMat)
plotBestFit(weights)
對(duì)算法進(jìn)行改進(jìn)溪胶,增加迭代次數(shù)
import random
def stocGradAscent1(dataMatrix, classLabels, numIter=150):
m, n = np.shape(dataMatrix)
weights = np.ones(n)
for j in range(numIter):
dataIndex = range(m)
for i in range(m):
# 每次迭代時(shí)更新 alpha 值
alpha = 4 / (1.0 + j + i) + 0.01
# 隨機(jī)選取更新
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
測(cè)試算法
發(fā)現(xiàn)經(jīng)過(guò)默認(rèn)150次迭代后搂擦,擬合直線與前面已經(jīng)差不多了。
dataArr, labelMat = loadDataSet()
weights = stocGradAscent1(np.array(dataArr), labelMat)
plotBestFit(weights)
示例:從疝氣病癥預(yù)測(cè)病馬的死亡率
使用邏輯回歸來(lái)預(yù)測(cè)患有疝氣病的馬的存活問(wèn)題哗脖。
測(cè)試集
訓(xùn)練集
收集數(shù)據(jù):給定數(shù)據(jù)文件瀑踢。
準(zhǔn)備數(shù)據(jù):用python解析文本文件并填充缺失值。
分析數(shù)據(jù):可視化并觀察數(shù)據(jù)才避。
訓(xùn)練算法:使用優(yōu)化算法橱夭,找到最佳的系數(shù)。
測(cè)試算法:為了量化回歸的效果桑逝,需要觀察錯(cuò)誤率棘劣。根據(jù)錯(cuò)誤率決定是否回退到訓(xùn)練階段,通過(guò)改變迭代的次數(shù)和步長(zhǎng)等參數(shù)來(lái)得到更好的回歸系數(shù)楞遏。
使用算法:實(shí)現(xiàn)一個(gè)簡(jiǎn)單的命令行程序來(lái)收集馬的癥狀
準(zhǔn)備數(shù)據(jù):處理缺失值
處理缺失值可選的做法:
使用可用特征的均值來(lái)填補(bǔ)缺失值
使用特殊值來(lái)填補(bǔ)缺失值茬暇,如-1
忽略有缺省值的樣本
使用相似樣本的均值填補(bǔ)缺失值
使用另外的機(jī)器學(xué)習(xí)算法預(yù)測(cè)缺失值
這里選擇實(shí)數(shù)0來(lái)替換所有缺失值首昔,因?yàn)槭褂肗umPy數(shù)據(jù)類(lèi)型不允許包含缺失值,而0恰好能適用于邏輯回歸糙俗±掌妫回歸系數(shù)的更新公式如下:
$weights = weights + alpha * error * dataMatrix[randindex]$
如果dataMatrix的某特征對(duì)應(yīng)值為0,那么該特征的系數(shù)不做更新巧骚,即:
$weights = weights$
另外赊颠,由于sigmoid(0) = 0.5,即它對(duì)結(jié)果的預(yù)測(cè)不具有任何傾向性网缝,因此選擇實(shí)數(shù)0作為缺失值也不會(huì)對(duì)誤差項(xiàng)造成影響巨税。
測(cè)試算法:用邏輯回歸進(jìn)行分類(lèi)
def classifyVector(inX, weights):
'''
邏輯回歸分類(lèi)函數(shù)
parameter inX: 特征向量
parameter weights: 回歸系數(shù)
'''
prob = sigmoid(sum(inX*weights))
if prob > 0.5:
return 1.0
else:
return 0.0
def colicTest():
'''
打開(kāi)測(cè)試集和訓(xùn)練集,并對(duì)數(shù)據(jù)進(jìn)行格式化處理
'''
frTrain = open('horseColicTraining.txt')
frTest = open('horseColicTest.txt')
trainingSet = []
trainingLabels = []
# 格式化訓(xùn)練集
for line in frTrain.readlines():
currLine = line.strip().split('\t')
lineArr = []
# 導(dǎo)入特征值粉臊,有21個(gè)特征
for i in range(21):
lineArr.append(float(currLine[i]))
trainingSet.append(lineArr)
# 導(dǎo)入類(lèi)別標(biāo)簽草添,類(lèi)別標(biāo)簽為最后一項(xiàng)
trainingLabels.append(float(currLine[21]))
# 使用隨機(jī)梯度上升算法計(jì)算回歸系數(shù)向量
trainWeights = stocGradAscent1(np.array(trainingSet), trainingLabels, 500)
errorCount = 0
numTestVec = 0.0
# 格式化測(cè)試集
for line in frTest.readlines():
numTestVec += 1.0
currLine = line.strip().split('\t')
lineArr = []
# 導(dǎo)入特征值
for i in range(21):
lineArr.append(float(currLine[i]))
# 使用訓(xùn)練集計(jì)算出的回歸系數(shù)對(duì)測(cè)試集進(jìn)行分類(lèi),并比對(duì)測(cè)試集的類(lèi)別標(biāo)簽扼仲,計(jì)算錯(cuò)誤數(shù)量
if int(classifyVector(np.array(lineArr), trainWeights)) != int(currLine[21]):
errorCount += 1
# 錯(cuò)誤率
errorRate = (float(errorCount)/numTestVec)
print 'the error rate of this test is: %f' % errorRate
return errorRate
def multiTest():
'''
調(diào)用colicTest函數(shù)10次远寸,取錯(cuò)誤率平均值
'''
numTests = 10
errorSum = 0.0
for k in range(numTests):
errorSum += colicTest()
print 'after %d iterations the average error rate is: %f' % (numTests, errorSum/float(numTests))
函數(shù)測(cè)試
10次迭代后,平均錯(cuò)誤率為35%屠凶。這個(gè)結(jié)果并不差驰后,因?yàn)橛?0%的缺失值。
如果調(diào)整colicTest()中的迭代次數(shù)和stocGradAscent1()中的步長(zhǎng)矗愧,平均錯(cuò)誤率還可以下降灶芝。
multiTest()
[out]
the error rate of this test is: 0.328358
the error rate of this test is: 0.358209
the error rate of this test is: 0.283582
the error rate of this test is: 0.313433
the error rate of this test is: 0.373134
the error rate of this test is: 0.373134
the error rate of this test is: 0.358209
the error rate of this test is: 0.343284
the error rate of this test is: 0.432836
the error rate of this test is: 0.388060
after 10 iterations the average error rate is: 0.355224
小結(jié)
邏輯回歸的目的是尋找一個(gè)非線性函數(shù)Signmoid的最佳擬合參數(shù),求解過(guò)程可以由最優(yōu)化算法來(lái)完成唉韭。在最優(yōu)化算法中夜涕,最常用的是梯度上升算法,而梯度上升算法又可以簡(jiǎn)化為隨機(jī)梯度上升算法属愤。
隨機(jī)梯度上升算法與梯度上升算法的效果相當(dāng)女器,但占用更少的計(jì)算機(jī)資源。此外住诸,隨機(jī)梯度上升算法是一個(gè)在線算法驾胆,它可以在新數(shù)據(jù)到來(lái)時(shí)完成參數(shù)更新,而不需要重新讀取整個(gè)數(shù)據(jù)集來(lái)進(jìn)行批處理運(yùn)算贱呐。