前言python
本文將介紹機器學習分類算法中的Logistic迴歸分類算法並給出僞代碼,Python代碼實現。算法
(說明:從本文開始,將接觸到最優化算法相關的學習。旨在將這些最優化的算法用於訓練出一個非線性的函數,以用於分類。)數組
算法原理 app
首先要提到的概念是迴歸。dom
對於迴歸這個概念,在之後的文章會有系統而深刻的學習。簡單的說,迴歸就是用一條線對N多數據點進行一個擬合,這個擬合的過程就叫作迴歸。機器學習
Logistic迴歸分類算法就是對數據集創建迴歸公式,以此進行分類。函數
而至於如何尋找最佳迴歸係數,或者說是分類器的訓練,就須要使用到最優化算法了。學習
迴歸分類器的形式測試
基本形式是用每一個特徵都乘以一個迴歸係數,而後把全部的結果值相加。優化
這樣算出的不少結果都是連續的,不利於分類,故能夠將結果再帶入到一個Sigmoid函數以獲得一些比較離散的分類結果。
Sigmoid函數的輪廓以下:
這樣,計算的結果會是一個0-1的值。進而0.5以上歸爲一類,如下歸爲一類便可。(通常的邏輯迴歸只能解決兩個分類的問題)
接下來的工做重點就轉移到了最佳迴歸係數的肯定了。
最佳迴歸係數的肯定
肯定最佳迴歸係數的過程,也就是對數據集進行訓練的過程。
求最佳迴歸係數的步驟以下:
1. 列出分類函數:
(θ 指回歸係數,在實踐中每每會再對結果進行一個Sigmoid轉換)
2. 給出分類函數對應的錯誤估計函數:
(m爲樣本個數)
只有當某個θ向量使上面的錯誤估計函數J(θ)取得最小值的時候,這個θ向量纔是最佳迴歸係數向量。
3. 採用梯度降低法或者最小二乘法求錯誤函數取得最小值的時候θ的取值:
爲表述方便,上式僅爲一個樣本的狀況,實際中要綜合多個樣本的狀況須要進行一個求和 (除非你使用後面會介紹的隨機梯度上升算法),具體請參考下面的代碼實現部分。
將步驟 2 中的錯誤函數加上負號,就能夠把問題轉換爲求極大值,梯度降低法轉換爲梯度上升法。
更詳盡的推導部分,在之後專門分析迴歸的文章中給出。
基於梯度上升法的最佳迴歸參數擬合
梯度上升法求最大值的核心思想是將自變量沿着目標函數的梯度方向移動,一直移動到指定的次數或者說某個容許的偏差範圍。
基於梯度上升法的分類僞代碼:
1 每一個迴歸係數初始化爲1 2 重複R次: 3 計算整個數據集的梯度 4 使用 alpha * gradient 更新迴歸係數向量 5 返回迴歸係數
下面給出完整的實現及測試代碼(用到的數據集文件共 4 列,前 3 列爲特徵,最後一列爲分類結果):
1 #!/usr/bin/env python 2 # -*- coding:UTF-8 -*- 3 4 ''' 5 Created on 2014-12-29 6 7 @author: fangmeng 8 ''' 9 10 import numpy 11 12 #===================================== 13 # 輸入: 14 # 空 15 # 輸出: 16 # dataMat: 測試數據集 17 # labelMat: 測試分類標籤集 18 #===================================== 19 def loadDataSet(): 20 '建立測試數據集,分類標籤集並返回。' 21 22 # 測試數據集 23 dataMat = []; 24 # 測試分類標籤集 25 labelMat = [] 26 # 文本數據源 27 fr = open('/home/fangmeng/testSet.txt') 28 29 # 載入數據 30 for line in fr.readlines(): 31 lineArr = line.strip().split() 32 dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])]) 33 labelMat.append(int(lineArr[2])) 34 35 return dataMat,labelMat 36 37 38 #===================================== 39 # 輸入: 40 # inX: 目標轉換向量 41 # 輸出: 42 # 1.0/(1+numpy.exp(-inX)): 轉換結果 43 #===================================== 44 def sigmoid(inX): 45 'sigmoid轉換函數' 46 47 return 1.0/(1+numpy.exp(-inX)) 48 49 #===================================== 50 # 輸入: 51 # dataMatIn: 數據集 52 # classLabels: 分類標籤集 53 # 輸出: 54 # weights: 最佳擬合參數向量 55 #===================================== 56 def gradAscent(dataMatIn, classLabels): 57 '基於梯度上升法的logistic迴歸分類器' 58 59 # 將數據集,分類標籤集存入矩陣類型。 60 dataMatrix = numpy.mat(dataMatIn) 61 labelMat = numpy.mat(classLabels).transpose() 62 63 # 上升步長度 64 alpha = 0.001 65 # 迭代次數 66 maxCycles = 500 67 # 初始化迴歸參數向量 68 m,n = numpy.shape(dataMatrix) 69 weights = numpy.ones((n,1)) 70 71 # 對迴歸係數進行maxCycles次梯度上升 72 for k in range(maxCycles): 73 h = sigmoid(dataMatrix*weights) 74 error = (labelMat - h) 75 weights = weights + alpha * dataMatrix.transpose()* error 76 77 return weights 78 79 def test(): 80 '測試' 81 82 dataArr, labelMat = loadDataSet() 83 print gradAscent(dataArr, labelMat) 84 85 if __name__ == '__main__': 86 test()
測試結果:
擬合結果展現
可以使用matplotlib畫決策邊界,用做分析擬合結果是否達到預期。
繪製及測試部分代碼以下所示:
1 #====================================== 2 # 輸入: 3 # weights: 迴歸係數向量 4 # 輸出: 5 # 圖形化的決策邊界演示 6 #====================================== 7 def plotBestFit(weights): 8 '決策邊界演示' 9 10 import matplotlib.pyplot as plt 11 # 獲取數據集 分類標籤集 12 dataMat,labelMat=loadDataSet() 13 dataArr = numpy.array(dataMat) 14 15 # 兩種分類下的兩種特徵列表 16 xcord1 = []; ycord1 = [] 17 xcord2 = []; ycord2 = [] 18 for i in range(numpy.shape(dataArr)[0]): 19 if int(labelMat[i])== 1: 20 xcord1.append(dataArr[i,1]); ycord1.append(dataArr[i,2]) 21 else: 22 xcord2.append(dataArr[i,1]); ycord2.append(dataArr[i,2]) 23 24 fig = plt.figure() 25 ax = fig.add_subplot(111) 26 ax.scatter(xcord1, ycord1, s=30, c='red', marker='s') 27 ax.scatter(xcord2, ycord2, s=30, c='green') 28 29 # 繪製決策邊界 30 x = numpy.arange(-3.0, 3.0, 0.1) 31 y = (-weights[0]-weights[1]*x)/weights[2] 32 ax.plot(x, y) 33 34 plt.xlabel('X1'); plt.ylabel('X2'); 35 plt.show() 36 37 def test(): 38 '測試' 39 40 dataArr, labelMat = loadDataSet() 41 weights = gradAscent(dataArr, labelMat) 42 plotBestFit(weights.getA())
測試結果:
更好的求最值方法 - 隨機梯度上升
Logistic迴歸的本質其實就是求擬合參數,而求擬合參數最核心的就是求錯誤估計函數的最小值。
仔細分析上面的代碼,會發現該分類的效率作多的耗費也是在求最值上面。因爲每次都要用全部數據來計算梯度,致使數據集很是大的時候,該算法很不給力。
實踐代表,每次僅僅用一個樣本點來更新迴歸係數,當全部樣本算完,也能達到類似的效果(僅僅是類似,或者說接近)。
因爲能夠在每一個樣本到達的時候對分類器進行增量式更新,所以隨機梯度上升算法實際上是一個在線學習算法。
基於隨機梯度上升的分類僞代碼:
1 全部迴歸係數初始化爲1 2 對數據集中的每一個樣本: 3 計算該樣本的梯度 4 使用 alpha * gradient 更新迴歸係數值 5 返回迴歸係數值
請對比上面的基本梯度上升算法進行理解學習。
優化以後的分類算法函數以下:
1 #===================================== 2 # 輸入: 3 # dataMatIn: 數據集(注意是向量類型) 4 # classLabels: 分類標籤集 5 # 輸出: 6 # weights: 最佳擬合參數向量 7 #===================================== 8 def stocGradAscent0(dataMatrix, classLabels): 9 '基於隨機梯度上升法的logistic迴歸分類器' 10 11 m,n = numpy.shape(dataMatrix) 12 # 上升步長度 13 alpha = 0.01 14 # 初始化迴歸參數向量 15 weights = numpy.ones(n) 16 17 # 對迴歸係數進行樣本數量次數的梯度上升,每次上升僅用一個樣本。 18 for i in range(m): 19 h = sigmoid(sum(dataMatrix[i]*weights)) 20 error = classLabels[i] - h 21 weights = weights + alpha * error * dataMatrix[i] 22 return weights
你也許會疑惑 "不是說隨機梯度上升算法嗎?隨機呢?",不用急,很快就會提到這個。
分析優化後的代碼能夠看到兩個區別:一個是當前分類結果變量h和偏差變量都是數值類型(以前爲向量類型),二是無矩陣轉換過程,數據集爲numpy向量的數組類型。
測試結果:
對比優化前的算法結果,能夠看出分類錯誤率更高了。
優化後的效果反而更差了?這樣說有點不公平,由於優化後的算法只是迭代了100次,而優化前的足足有500次。
那麼接下來能夠進一步優化,理論依據爲:增長迭代計算的次數,當達到一個接近收斂或者已經收斂的狀態時,中止迭代。
那麼如何作到這點呢?
第一是要動態的選定步長,第二,就是每次隨機的選定樣本,這就是爲何叫作隨機梯度上升算法了。
最終修改後的分類器以下:
1 #===================================== 2 # 輸入: 3 # dataMatIn: 數據集(注意是向量類型) 4 # classLabels: 分類標籤集 5 # 輸出: 6 # weights: 最佳擬合參數向量 7 #===================================== 8 def stocGradAscent1(dataMatrix, classLabels, numIter=150): 9 '基於隨機梯度上升法的logistic迴歸分類器' 10 11 m,n = numpy.shape(dataMatrix) 12 weights = numpy.ones(n) 13 14 # 迭代次數控制 15 for j in range(numIter): 16 # 樣本選擇集 17 dataIndex = range(m) 18 # 隨機選取樣本遍歷一輪 19 for i in range(m): 20 # 動態修正步長 21 alpha = 4/(1.0+j+i)+0.0001 22 # 隨機選取變量進行梯度上升 23 randIndex = int(numpy.random.uniform(0,len(dataIndex))) 24 h = sigmoid(sum(dataMatrix[randIndex]*weights)) 25 error = classLabels[randIndex] - h 26 weights = weights + alpha * error * dataMatrix[randIndex] 27 # 從選擇集中刪除已經使用過的樣本 28 del(dataIndex[randIndex]) 29 return weights
運行結果:
此次,和最原始的基於梯度上升法分類器的結果就差很少了。可是迭代次數大大的減小了。
網上也有一些很是嚴格的證實隨機梯度上升算法的收斂速度更快(相比基礎梯度上升算法)的證實,有興趣的讀者能夠查找相關論文。
小結
1. 邏輯迴歸的計算代價不高,是很經常使用的分類算法。集中基於隨機梯度上升的邏輯迴歸分類器可以支持在線學習。
2. 但邏輯迴歸算法缺點很明顯 - 通常只能解決兩個類的分類問題。
3. 另外邏輯迴歸容易欠擬合,致使分類的精度不高。