寫程式學ML:Logistic迴歸演算法原理及實現(三)
2.2 利用Logistic演算法預測病馬死亡率
由於採集資料是諸多原因,採集的資料有可能不完整。但有時候資料相當昂貴,扔掉和重新獲取都是不可取的,所以必須採用一些方法來解決這個問題。
處理資料中缺失值的做法:
1> 使用可用特徵的均值來填補缺失值;
2> 使用特殊值來填補缺失值;
3> 忽略有缺失值的樣本;
4> 使用相似樣本的均值添補缺失值;
5> 使用另外的機器學習演算法預測缺失值;
對於採集到的資料進行預處理,可以使其順利地使用分類演算法。在預處理階段做兩件事:
第一,所有的缺失值必須用一個實數值來替換,因為我們使用的NumPy資料型別不允許包含缺失值。這裡選擇實數0來替換所有缺失值,恰好能適用於Logistic迴歸。這樣做的直覺在於,我們需要的是一個在更新時不會影響係數的值。迴歸係數的更新公式如下:
weights =weights + alpha * error * dataMatrix[randIndex]
如果dataMatrix的某特徵對應值為0,那麼該特徵的係數將不做更新,即:
weights =weights
另外,由於sigmoid(0) = 0.5,即它對結果的預測不具有任何傾向性,因此上述做法也不會對誤差造成任何影響。基於上述原因,將缺失值用0代替既可以保留現有資料,也不需要對優化演算法進行修改。此外,該資料集中的特徵取值一般不為0,因此在某種意義上說它也滿足“特徵值”這個要求。
第二件事:如果在測試資料集中發現了一條資料的類別標籤已經缺失,那麼我們的簡單做法是將該條資料丟棄。這是因為類別標籤與特徵不同,很難確定採用某個合適的值來替換。採用Logistic迴歸進行分類時這種做法是合理的,而如果採用類似kNN的方法就可能不太可行。
使用Logistic迴歸方法進行分類並不需要做很多工作,所需做的只是把測試集上每個特徵向量乘以最優化方法得來的迴歸係數,再將該乘積結果求和,最後輸入到sigmoid函式中即可。如果對應sigmoid值大於0.5就預測類別標籤為1,否則為0。
對於病馬死亡率的判斷,可以使用前面實現的Logistic迴歸演算法。
首先從訓練樣本檔案中讀取訓練樣本,解析出特徵值和分類情況。
呼叫改進的隨機梯度上升演算法函式stocGradAscent1()產生迴歸係數。
然後從測試樣本檔案中讀取測試樣本,解析出特徵值和分類情況。
呼叫分類函式classifyVector()對測試樣本進行分類,判斷該分類正確情況。
最後返回分類錯誤率。程式中對測試樣本進行了10次測試,求得了平均錯誤率。
程式的具體實現如下:
#使用改進的隨機梯度上升演算法,通過訓練樣本產生迴歸係數;
#利用迴歸係數對測試樣本進行分類,統計分類錯誤率
def colicTest():
frTrain = open('horseColicTraining.txt') #開啟訓練樣本集
frTest = open('horseColicTest.txt') #開啟測試樣本集
trainingSet = [] #儲存所有樣本的特徵值的列表變數
trainingLabels = [] #儲存所有樣本分類資訊的列表變數
#每個樣本中有21個特徵值,儲存在變數trainingSet中;每個樣本的分類資訊儲存在變數trainingLabels中
for line in frTrain.readlines(): #從訓練樣本檔案中逐個取出樣本
currLine = line.strip().split('\t') #去掉行首行末的空格,並用空格分隔,即將22個數值取出存到變數中
lineArr = []
for i in range(21): #將21個特徵值從列表變數currLine中讀取出來,儲存到列表變數lineArr中
lineArr.append(float(currLine[i]))
trainingSet.append(lineArr) #將當前樣本20個特徵值新增到列表trainingSet中
trainingLabels.append(float(currLine[21])) #將當前樣本分類資訊新增到列表trainingLabels中
#呼叫改進的隨機梯度上升演算法對訓練樣本進行500次迭代訓練,產生迴歸係數
trainWeights = Logistic.stocGradAscent1(array(trainingSet), trainingLabels, 500)
errorCount = 0
numTestVec = 0
#使用獲取的迴歸係數,對測試樣本進行分類,判斷分類正確情況
for line in frTest.readlines():
numTestVec += 1.0 #統計測試樣本個數
currLine = line.strip().split('\t') #去掉行首行末的空格,並用空格分隔,即將22個數值取出存到變數中
lineArr = []
for i in range(21): #將21個特徵值從列表變數currLine中讀取出來,儲存到列表變數lineArr中
lineArr.append(float(currLine[i]))
#判斷當前測試樣本的分類情況是否與真實分類一致;如果不一致,錯誤次數加1
if int(Logistic.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():
numTests = 10
errorSum = 1.0
for k in range(numTests): #統計10次測試的錯誤率,並累加
errorSum += colicTest()
print "after %d iterations the average error rate is: %f" % \
(numTests, errorSum / float(numTests)) #打印出10次平均錯誤率
3、小結
Logistic迴歸的目的是尋找一個非線性函式sigmoid的最佳擬合引數,求解過程可以由最優化演算法來完成。在最優化演算法中,最常用的就是梯度上升演算法,而梯度上升演算法又可以簡化為隨機梯度上升演算法。
隨機梯度上升演算法與梯度上升演算法的效果相當,但佔用更少的計算資源。此外,隨機梯度上升是一個線上演算法,它可以在新資料到來時就完成引數更新,而不需要更新讀取整個資料集來進行批處理運算。
機器學習的一個重要問題是如何處理缺失資料。這個問題沒有標準答案,取決於實際應用中的需求。現有一些解決方案,每種方案都各有優缺點。
本文中涉及的所有code可以訪問如下目錄獲取:
(全文完)