Logistic迴歸或者叫邏輯迴歸,雖然名字裏有迴歸二字,但它是用來作分類的。其主要思想爲:根據現有數據對分類界線創建迴歸公式,以此進行分類。python
logistic迴歸是一種分類方法,經常使用於兩分類問題。爲機率型非線性迴歸模型,是研究二分類觀察結果與一些影響因素
之間關係的一種多變量分析方法。一般的問題是,研究某些因素條件下某個結果是否發生,好比醫學中根據病人的一些症狀來判斷它是否患有某種病。算法
相關概念windows
迴歸概念數組
假設如今有一些數據點,咱們用一條直線對這些點進行擬合(這條直線稱爲最佳擬合直線),這個擬合的過程就叫作迴歸。進而能夠獲得對這些點的擬合直線方程,那麼咱們根據這個迴歸方程,怎麼進行分類呢?請看下面。app
二值輸出分類函數dom
咱們想要的函數應該是: 能接受全部的輸入而後預測出類別。例如,在兩個類的狀況下,上述函數輸出 0 或 1.或許你以前接觸過具備這種性質的函數,該函數稱爲 海維塞得階躍函數(Heaviside step function)
,或者直接稱爲 單位階躍函數
。然而,海維塞得階躍函數的問題在於: 該函數在跳躍點上從 0 瞬間跳躍到 1,這個瞬間跳躍過程有時很難處理。幸虧,另外一個函數也有相似的性質(能夠輸出 0 或者 1 的性質),且數學上更易處理,這就是 Sigmoid 函數。 Sigmoid 函數具體的計算公式以下:ide
所以,爲了實現 Logistic 迴歸分類器,咱們能夠在每一個特徵上都乘以一個迴歸係數(以下公式所示),而後把全部結果值相加,將這個總和代入 Sigmoid 函數中,進而獲得一個範圍在 0~1 之間的數值。任何大於 0.5 的數據被分入 1 類,小於 0.5 即被納入 0 類。因此,Logistic 迴歸也是一種機率估計,好比這裏Sigmoid 函數得出的值爲0.5,能夠理解爲給定數據和參數,數據被分入 1 類的機率爲0.5。函數
基於最優化方法的迴歸係數肯定
設Sigmoid函數的輸入爲z,則z=w1x1+w2x2+w3x3+...+wnxn=WTX,其中向量X是分類器的輸入數據,向量W就是咱們要找到的最佳迴歸係數。爲了尋找該最佳參數,須要用到最優化理論的一些知識。咱們這裏使用的是——梯度上升法(Gradient Ascent)。優化
梯度上升法與梯度降低法spa
要找到某函數的最大值,最好的方法是沿着該函數的梯度方向探尋。若是梯度記爲 ▽ ,則函數 f(x, y) 的梯度由下式表示:
這個梯度意味着要沿 x 的方向移動 ,沿 y 的方向移動
。其中,函數f(x, y) 必需要在待計算的點上有定義而且可微。
梯度算子老是指向函數值增加最快的方向。這裏所說的是移動方向,而未提到移動量的大小。該量值稱爲步長,記做 α 。用向量來表示的話,梯度上升算法的迭代公式以下:
該公式將一直被迭代執行,直至達到某個中止條件爲止,好比迭代次數達到某個指定值或者算法達到某個能夠容許的偏差範圍。
問:有人會好奇爲何有些書籍上說的是梯度降低法(Gradient Decent)?
答: 其實這個兩個方法在此狀況下本質上是相同的。關鍵在於代價函數(cost function)或者叫目標函數(objective function)。若是目標函數是損失函數,那就是最小化損失函數來求函數的最小值,就用梯度降低。 若是目標函數是似然函數(Likelihood function),就是要最大化似然函數來求函數的最大值,那就用梯度上升。在邏輯迴歸中, 損失函數和似然函數無非就是互爲正負關係。
只須要在迭代公式中的加法變成減法。所以,對應的公式能夠寫成
Logistic迴歸 代價函數
定義邏輯迴歸的代價函數爲:
,其中
hΘ(x)與Cost(hΘ(x),y)之間的關係以下圖:
這樣構建的Cost(hΘ(x),y)的函數特色是:當實際的y=1且hΘ(x)也爲1時偏差爲0,當y=1但hΘ(x)不爲1時偏差隨着hΘ(x)變小而變大;當實際的y=0且hΘ(x)也爲0時偏差爲0,當y=0但hΘ(x)不爲0時偏差隨着hΘ(x)的變大而變大。
將構建的Cost(hΘ(x),y)簡化以下:
帶入代價函數得:
Logistic迴歸 工做原理
每一個迴歸係數初始化爲1
重複R次:
計算整個數據集的梯度
使用 步長*梯度 更新迴歸係數向量
返回迴歸係數
Logistic迴歸 算法特色
優勢:計算代價不高,易於理解和實現。
缺點:容易欠擬合,分類精度不高。
使用數據類型:數值型和標稱型數據。
有關回歸係數W更新的公式推導
http://blog.csdn.net/achuo/article/details/51160101
項目案例
咱們採用存儲在 TestSet.txt 文本文件中的數據,存儲格式以下:
-0.017612 14.053064 0 -1.395634 4.662541 1 -0.752157 6.538620 0 -1.322371 7.152853 0 0.423363 11.054677 0
繪製在圖中,如圖所示:
代碼實現
from math import exp from numpy import * from matplotlib import pyplot as plt from matplotlib.font_manager import FontProperties def loadDataSet(filename): dataMat = [] labelMat = [] f = open(filename) for line in f.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): return 1.0/(1+exp(-inX)) def gradAscent(dataMatIn, classLabels): ''' 梯度上升算法 :param dataMatIn: 數據集,一個二維Numpy數組,每列分別表明每一個不一樣的特徵,每行則表明每一個訓練樣本。 :param classLabels: 類別標籤,是一個(1,100)的行向量。 :return: 返回迴歸係數 ''' dataMatrix = mat(dataMatIn) labelMat = mat(classLabels).transpose() # m->樣本數,n->特徵數 在本例中m=100,n=3 m, n = shape(dataMatrix) # alpha表明向目標移動的步長 alpha = 0.001 # 迭代次數 maxCycles = 500 # 權重值初始全爲1 weights = ones((n, 1)) for k in range(maxCycles): h = sigmoid(dataMatrix * weights) # shape = (100,1) error = labelMat - h # shape = (100,1) weights += alpha * dataMatrix.transpose() * error return weights def stocGradAscent(dataMatIn, classLabels, numIter=150): ''' 隨機梯度上升法 :param dataMatIn: 數據集,一個二維Numpy數組,每列分別表明每一個不一樣的特徵,每行則表明每一個訓練樣本。 :param classLabels: 類別標籤,是一個(1,100)的行向量。 :param numIter: 外循環次數 :return: 返回迴歸係數 ''' dataMatrix = mat(dataMatIn) m, n = shape(dataMatrix) weights = ones((n, 1)) # 隨機梯度, 循環150,觀察是否收斂 for j in range(numIter): # [0, 1, 2 .. m-1] dataIndex = list(range(m)) for i in range(m): # i和j的不斷增大,致使alpha的值不斷減小,可是不爲0 alpha = 4/(1.0+j+i)+0.0001 # alpha 會隨着迭代不斷減少,但永遠不會減少到0,由於後邊還有一個常數項0.0001 # 隨機產生一個 0~len(dataIndex)之間的一個值 # random.uniform(x, y) 方法將隨機生成下一個實數,它在[x,y]範圍內,x是這個範圍內的最小值,y是這個範圍內的最大值。 randIndex = int(random.uniform(0, len(dataIndex))) choose = dataIndex[randIndex] # sum(dataMatrix[i]*weights)爲了求 f(x)的值, f(x)=w1*x1+w2*x2+..+wn*xn h = sigmoid(sum(dataMatrix[choose]*weights)) error = classLabels[choose] - h weights += alpha * error * dataMatrix[choose].transpose() del(dataIndex[randIndex]) return weights def showData(dataArr, labelMat): ''' 展現數據集分佈狀況 :param dataArr: :param labelMat: :return: None ''' n = shape(dataArr)[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(figsize=(8, 6)) ax = fig.add_subplot(111) p1 = ax.scatter(xcord1, ycord1, s=30, c='red', marker='s') p2 = ax.scatter(xcord2, ycord2, s=30, c='green') plt.legend([p1, p2], ['Class 1', 'Class 0'], loc='lower right', scatterpoints=1) plt.show() def plotBestFit(dataArr, labelMat, weights1, weights2): ''' 將咱們獲得的數據可視化展現出來 :param dataArr:樣本數據的特徵 :param labelMat:樣本數據的類別標籤,即目標變量 :param weights:迴歸係數 :return:None ''' font = FontProperties(fname=r"c:\windows\fonts\simsun.ttc", size=14) n = shape(dataArr)[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(figsize=(8, 6)) ax1 = fig.add_subplot(121) ax2 = fig.add_subplot(122) ax1.scatter(xcord1, ycord1, s=30, c='red', marker='s') ax1.scatter(xcord2, ycord2, s=30, c='green') ax2.scatter(xcord1, ycord1, s=30, c='red', marker='s') ax2.scatter(xcord2, ycord2, s=30, c='green') x1 = arange(-3.0, 3.0, 0.1) y1 = (-weights1[0] - weights1[1] * x1) / weights1[2] x2 = arange(-3.0, 3.0, 0.1) y2 = (-weights2[0] - weights2[1] * x2) / weights2[2] ax1.plot(x1, y1) ax2.plot(x2, y2) ax1_title_text = ax1.set_title(u'梯度上升算法', FontProperties=font) ax2_title_text = ax2.set_title(u'隨機梯度上升算法', FontProperties=font) plt.xlabel('X') plt.ylabel('Y') #plt.setp(ax1_title_text, size=20, weight='bold', color='black') #plt.setp(ax2_title_text, size=20, weight='bold', color='black') plt.show() def testLR(): dataMat, classLabels = loadDataSet('data/TestSet.txt') dataArr = array(dataMat) weights1= gradAscent(dataArr, classLabels) weights2 = stocGradAscent(dataArr, classLabels) test(dataArr, classLabels) plotBestFit(dataArr, classLabels, weights1, weights2) if __name__ == '__main__': testLR()
結果圖: