背景:使用Logistic回歸來預測患有疝氣病的馬的存活問題,這里的數據包括368個樣本和28個特征,疝氣病是描述馬胃腸痛的術語,然而,這種病並不一定源自馬的胃腸問題,其他問題也可能引發疝氣病,該數據集中包含了醫院檢測馬疝氣病的一些指標,有的指標比較主觀,有的指標難以測量,例如馬的疼痛級別。另外,除了部分指標主觀和難以測量之外,該數據還存在一個問題,數據集中有30%的值是缺失的。
1、准備數據:處理數據中的缺失值
數據中的缺失值是一個非常棘手的問題,那么數據缺失究竟帶來了多少問題?假設有100個樣本和20個特征,這些數據都是機器收集回來的,若機器上的某個傳感器損壞導致一個特征無效時該怎么辦?此時是否扔掉整個數據集?這種情況下,另外19個特征怎么辦?它們是否還可用?答案是肯定的。因為有時數據相當昂貴,扔掉和重新獲取都是不可取的,所以必須采用一些方法來解決這個問題。
下面給出了一些可選的做法:
(1)使用可用特征的均值來填補缺失值;
(2)使用特征值來填補缺失值,如-1;
(3)忽略有缺失值的樣本;
(4)使用相似樣本的均值填補缺少值;
(5)使用另外的機器學習算法預測缺失值。
現在我們要對下一節要用的數據集進行預處理,使其可以順利地使用分類算法。在預處理階段需要做兩件事:(1)所有的缺失值必須用一個實數值來替換,因為我們使用的Numpy數據類型不允許包含缺失值,這里選擇實數0來替換所有缺失值,恰好能適用於Logistic回歸。另外,由於Sigmoid(0)=0.5,即它對結果的預測不具有任何的傾向性,因此上述做法不會對誤差項造成任何影響。基於上述原因,將缺失值用0代替既可以保留現有數據,也不需要對優化算法進行修改。回歸系數的更新公式如下:
weights=weights+alpha*error*dataMatrix[randIndex]
如果dataMatrix的某特征對應值為0,那么該特征的系數將不做更新,即:
weights=weights
(2)如果在測試數據集中發現一條數據的類別標簽已經缺失,那么我們的簡單做法是將該條數據丟棄。這是因為類別標簽與特征不同,很難確定采用某個合適的值來替換。
原始的數據經過預處理之后保存成兩個文件:horseColicTest.txt和horseColicTraining.txt。如果想對原始數據和預處理后的數據做個比較,可以在http://archive.ics.uci.edu/ml/datasets/Horse+Colic瀏覽這些數據。


2、訓練算法:改進的隨機梯度上升算法
建立LogRegres.py文件,編寫以下代碼:
#改進的隨機梯度上升函數
#!/usr/bin/python
# -*- coding: utf-8 -*-
from numpy import *
def sigmoid(inX):
return 1.0/(1+exp(-inX))
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.01 #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
3、測試算法:用Logistic回歸進行分類
使用Logistic回歸方法進行分類並不需要做很多工作,所需要做的只是把測試集上的每個特征向量乘以最優化方法得來的回歸系數,再將該乘積結果求和,最后輸入到Sigmoid函數中即可,如果對應的Sigmoid值大於0.5,就預測類別標簽為1,否則為0。
下面看看實際運行效果,將下面代碼添加到LogRegres.py文件中
#Logistic回歸分類函數
def classifyVector(inX,weights): #判斷分類
prob=sigmoid(sum(inX*weights))
if prob>0.5:
return 1.0
else:
return 0.0
def colicTest(): #導入數據
frTrain=open('horseColicTraining.txt')
frTest=open('horseColicTest.txt')
trainingSet=[];trainingLabels=[]
for line in frTrain.readlines():
currLine=line.strip().split('\t')
lineArr=[]
for i in range(21):
lineArr.append(float(currLine[i]))
trainingSet.append(lineArr)
trainingLabels.append(float(currLine[21]))
trainWeights=stocGradAscent1(array(trainingSet),trainingLabels,500) #計算回歸系數向量
errorCount=0;numTestVec=0.0
for line in frTest.readlines(): #導入測試集並計算分類錯誤率
numTestVec+=1.0
currLine=line.strip().split('\t')
lineArr=[]
for i in range(21):
lineArr.append(float(currLine[i]))
if int(classifyVector(array(lineArr),trainWeights))!=int(currLine[21]):
errorCount+=1
errorRate=(float(errorCount)/numTestVec)
print "the error rate of this test is: %f" %errorRate
return errorRate
def multiTest(): #調用函數colicTest()10次並求結果的平均值
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))
下面看一下實際的運行效果,在pyghon提示符下輸入:
>>>import logRegres <module 'logRegres' from 'logRegres.py'> >>> logRegres.multiTest() the error rate of this test is: 0.343284 the error rate of this test is: 0.283582 the error rate of this test is: 0.432836 the error rate of this test is: 0.358209 the error rate of this test is: 0.328358 the error rate of this test is: 0.328358 the error rate of this test is: 0.343284 the error rate of this test is: 0.373134 the error rate of this test is: 0.432836 the error rate of this test is: 0.462687 after 10 iterations the average error rate is: 0.368657
從上面的結果可以看出,10次迭代之后的平均值錯誤率為37%,事實上,這個結果並不差,因為有30%的數據缺失。當然,如果調整colicTest()中的迭代次數和stocGradAscent1()中的步長,平均錯誤率可以降到20%左右。
