前言
本文将介紹機器學習分類算法中的Logistic回歸分類算法并給出僞代碼,Python代碼實作。
(說明:從本文開始,将接觸到最優化算法相關的學習。旨在将這些最優化的算法用于訓練出一個非線性的函數,以用于分類。)
算法原理
首先要提到的概念是回歸。
對于回歸這個概念,在以後的文章會有系統而深入的學習。簡單的說,回歸就是用一條線對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. 另外邏輯回歸容易欠拟合,導緻分類的精度不高。
轉載于:https://www.cnblogs.com/scut-fm/p/4192053.html