注:最近開始學習《人工智能》選修課,老師提綱挈領的介紹了一番,聽完課只瞭解了個大概,剩下的細節只能本身繼續摸索。html
從本質上講:機器學習就是一個模型對外界的刺激(訓練樣本)作出反應,趨利避害(評價標準)。python
許多人對線性迴歸都比較熟悉,但知道邏輯迴歸的人可能就要少的多。從大的類別上來講,邏輯迴歸是一種有監督的統計學習方法,主要用於對樣本進行分類。git
在線性迴歸模型中,輸出通常是連續的,例如$$y = f(x) = ax + b$$,對於每個輸入的x,都有一個對應的y輸出。模型的定義域和值域均可以是[-∞, +∞]。可是對於邏輯迴歸,輸入能夠是連續的[-∞, +∞],但輸出通常是離散的,即只有有限多個輸出值。例如,其值域能夠只有兩個值{0, 1},這兩個值能夠表示對樣本的某種分類,高/低、患病/健康、陰性/陽性等,這就是最多見的二分類邏輯迴歸。所以,從總體上來講,經過邏輯迴歸模型,咱們將在整個實數範圍上的x映射到了有限個點上,這樣就實現了對x的分類。由於每次拿過來一個x,通過邏輯迴歸分析,就能夠將它納入某一類y中。github
邏輯迴歸也被稱爲廣義線性迴歸模型,它與線性迴歸模型的形式基本上相同,都具備 ax+b,其中a和b是待求參數,其區別在於他們的因變量不一樣,多重線性迴歸直接將ax+b做爲因變量,即y = ax+b,而logistic迴歸則經過函數S將ax+b對應到一個隱狀態p,p = S(ax+b),而後根據p與1-p的大小決定因變量的值。這裏的函數S就是Sigmoid函數算法
$$S(t) = \frac{1}{1 + e^{-t}}$$數組
將t換成ax+b,能夠獲得邏輯迴歸模型的參數形式:$$p(x; a,b) = \frac{1}{1 + e^{-(ax+b)}} ……(1)$$app
圖1:sigmoid函數的圖像機器學習
經過函數S的做用,咱們能夠將輸出的值限制在區間[0, 1]上,p(x)則能夠用來表示機率p(y=1|x),即當一個x發生時,y被分到1那一組的機率。但是,等等,咱們上面說y只有兩種取值,可是這裏卻出現了一個區間[0, 1],這是什麼鬼??其實在真實狀況下,咱們最終獲得的y的值是在[0, 1]這個區間上的一個數,而後咱們能夠選擇一個閾值,一般是0.5,當y>0.5時,就將這個x歸到1這一類,若是y<0.5就將x歸到0這一類。可是閾值是能夠調整的,好比說一個比較保守的人,可能將閾值設爲0.9,也就是說有超過90%的把握,才相信這個x屬於1這一類。瞭解一個算法,最好的辦法就是本身從頭實現一次。下面是邏輯迴歸的具體實現。函數
邏輯迴歸模型的代價函數學習
邏輯迴歸通常使用交叉熵做爲代價函數。關於代價函數的具體細節,請參考代價函數,這裏只給出交叉熵公式:
$$J(\theta) = -\frac{ 1 }{ m }[\sum_{ i=1 }^{ m } ({y^{(i)} \log h_\theta(x^{(i)}) + (1-y^{(i)}) \log (1-h_\theta(x^{(i)})})]$$
m:訓練樣本的個數;
hθ(x):用參數θ和x預測出來的y值;
y:原訓練樣本中的y值,也就是標準答案
上角標(i):第i個樣本
下面的數據來自《機器學習實戰》中的示例:
-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 0.406704 7.067335 1 0.667394 12.741452 0 -2.460150 6.866805 1 0.569411 9.548755 0 -0.026632 10.427743 0
上面的數據一共是3列10行,其中前兩列爲x1和x2的值,第3列表示y的值;10行表示取了10個樣本點。咱們能夠將這些數據當作訓練模型參數的訓練樣本。
見到訓練樣本就能夠比較直觀的理解算法的輸入,以及咱們如何利用這些數據來訓練邏輯迴歸分類器,進而用訓練好的模型來預測新的樣本(檢測樣本)。
從邏輯迴歸的參數形式,式子(1)咱們能夠看到邏輯迴歸模型中有兩個待定參數a(x的係數)和b(常數項),咱們如今給出來的數據有兩個特徵x1, x2,所以整個模型就增長了一項:ax1 + cx2 + b。爲了形式上的統一,咱們使用帶下標的a表示不一樣的參數(a0表示常數項b並做x0的參數<x0=1>,a1、a2分別表示x1和x2的參數),就能夠獲得:
$$ a_0x_0 + a_1x_1 + a_2x_2 $$
這樣統一塊兒來後,就可使用矩陣表示了(比起前面展開的線性表示方式,用矩陣表示模型和參數更加簡便,並且矩陣運算的速度也更快):
$$ \begin{bmatrix} a_0 & a_1 & a_2 \end{bmatrix} \begin{bmatrix} x_0 \\ x_1 \\ x_2 \end{bmatrix} = a^{ \mathrm{ T } }X$$
將上面的式子帶入到(1)式,咱們就能夠獲得邏輯迴歸的另外一種表示形式了:
$$p(x; a) = \frac{1}{1 + e^{-a^{ \mathrm{ T } }X}} ……(2)$$
此時,能夠很清楚的看到,咱們後面的行動都是爲了肯定一個合適的a(一個參數向量),使得對於一個新來的X(也是一個向量),咱們能夠儘量準確的給出一個y值,0或者1.
注:數據是二維的,也就是說這組觀察樣本中有兩個自變量,即兩個特徵(feature)。
就像上面說的,訓練分類器的過程,就是根據已經知道的數據(訓練樣本)肯定一個使得代價函數的值最小的a(參數向量/迴歸係數)的過程。邏輯迴歸模型屬於有監督的學習方法,上面示例數據中的第3列實際上是訓練樣本提供的"標準答案"。也就是說,這些數據是已經分好類的(兩類,0或者1)。在訓練階段,咱們要作的就是利用訓練樣本和(2)式中的模型,估計一個比較合適的參數a,使得僅經過前面兩列數據(觀察值/測量值)就能夠估計一個值h(a),這個值越接近標準答案y,說明咱們的模型預測的越準確。
下面是估計迴歸係數a的值的過程,仍是借鑑了《機器學習實戰》中的代碼,作了少許修改:
其中計算參數梯度,即代價函數對每一個參數的偏導數(下面代碼中的第36-38行),的詳細推導過程能夠參考這裏
1 ''' 2 Created on Oct 27, 2010 3 Logistic Regression Working Module 4 @author: Peter 5 ''' 6 from numpy import * 7 import os 8 9 path = 'D:\MechineLearning\MLiA_SourceCode\machinelearninginaction\Ch05' 10 training_sample = 'trainingSample.txt' 11 testing_sample = 'testingSample.txt' 12 13 # 從文件中讀入訓練樣本的數據,同上面給出的示例數據 14 # 下面第20行代碼中的1.0表示x0 = 1 15 def loadDataSet(p, file_n): 16 dataMat = []; labelMat = [] 17 fr = open(os.path.join(p, file_n)) 18 for line in fr.readlines(): 19 lineArr = line.strip().split() 20 dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])]) # 三個特徵x0, x1, x2 21 labelMat.append(int(lineArr[2])) # 標準答案y 22 return dataMat,labelMat 23 24 def sigmoid(inX): 25 return 1.0/(1+exp(-inX)) 26 27 # 梯度降低法求迴歸係數a,因爲樣本量少,我將迭代次數改爲了1000次 28 def gradAscent(dataMatIn, classLabels): 29 dataMatrix = mat(dataMatIn) #convert to NumPy matrix 30 labelMat = mat(classLabels).transpose() #convert to NumPy matrix 31 m,n = shape(dataMatrix) 32 alpha = 0.001 # 學習率 33 maxCycles = 1000 34 weights = ones((n,1)) 35 for k in range(maxCycles): # heavy on matrix operations 36 h = sigmoid(dataMatrix*weights) # 模型預測值, 90 x 1 37 error = h - labelMat # 真實值與預測值之間的偏差, 90 x 1 38 temp = dataMatrix.transpose()* error # 交叉熵代價函數對全部參數的偏導數, 3 x 1 39 weights = weights - alpha * temp # 更新權重 40 return weights 41 42 # 下面是我本身寫的測試函數 43 def test_logistic_regression(): 44 dataArr, labelMat = loadDataSet(path, training_sample) # 讀入訓練樣本中的原始數據 45 A = gradAscent(dataArr, labelMat) # 迴歸係數a的值 46 h = sigmoid(mat(dataArr)*A) #預測結果h(a)的值 47 print(dataArr, labelMat) 48 print(A) 49 print(h) 50 # plotBestFit(A) 51 52 test_logistic_regression()
上面代碼的輸出以下:
([[1.0, -0.017612, 14.053064], [1.0, -1.395634, 4.662541], [1.0, -0.752157, 6.53862], [1.0, -1.322371, 7.152853], [1.0, 0.423363, 11.054677], [1.0, 0.406704, 7.067335], [1.0, 0.667394, 12.741452], [1.0, -2.46015, 6.866805], [1.0, 0.569411, 9.548755], [1.0, -0.026632, 10.427743]], [0, 1, 0, 0, 0, 1, 0, 1, 0, 0])
[[ 1.39174871] [-0.5227482 ] [-0.33100373]]
[[ 0.03730313] [ 0.64060602] [ 0.40627881] [ 0.4293251 ] [ 0.07665396] [ 0.23863652] [ 0.0401329 ] [ 0.59985228] [ 0.11238742] [ 0.11446212]]
標準答案是{0, 1},如何將預測到的結果與標準答案y進行比較呢?取0.5做爲閾值,大於該值的樣本就劃分到1這一組,小於等於該值的樣本就劃分到0這一組,這樣就能夠將數據分爲兩類。檢查一下結果能夠看到,咱們如今分出來的1這一類中包括原來y=1的兩個樣本,另外一類包括原來y=0的全部樣本和一個y=1的樣本(分錯了)。鑑於咱們選擇取的樣本比較少(只有10個),這樣的效果其實還算很是不錯的!
上面已經求出了一組迴歸係數,它肯定了不一樣類別數據之間的分割線。能夠利用X內部(x1與x2之間的關係)的關係畫出該分割線,從而更直觀的感覺到分類的效果。
添加下面一段代碼:
1 # 分類效果展現,參數weights就是迴歸係數 2 def plotBestFit(weights): 3 import matplotlib.pyplot as plt 4 dataMat,labelMat=loadDataSet(path, training_sample) 5 dataArr = array(dataMat) 6 n = shape(dataArr)[0] 7 xcord1 = []; ycord1 = [] 8 xcord2 = []; ycord2 = [] 9 for i in range(n): 10 if int(labelMat[i])== 1: 11 xcord1.append(dataArr[i,1]); ycord1.append(dataArr[i,2]) 12 else: 13 xcord2.append(dataArr[i,1]); ycord2.append(dataArr[i,2]) 14 fig = plt.figure() 15 ax = fig.add_subplot(111) 16 ax.scatter(xcord1, ycord1, s=30, c='red', marker='s') 17 ax.scatter(xcord2, ycord2, s=30, c='green') 18 x = arange(-3.0, 3.0, 0.1) 19 y = (-weights[0]-weights[1]*x)/weights[2] # x2 = f(x1) 20 ax.plot(x.reshape(1, -1), y.reshape(1, -1)) 21 plt.xlabel('X1'); plt.ylabel('X2'); 22 plt.show()
將上面的test_logistic_regression()函數中的最後一句註釋去掉,調用plotBestFit函數就能夠看到分類的效果了。
這裏說明一下上面代碼中的第19行,這裏設置了sigmoid函數的取值爲1/2,也就是說取閾值爲0.5來劃分最後預測的結果。這樣能夠獲得$$e^{-a^{ \mathrm{ T } }X} = 1$$,即$-a^TX=0$,能夠推出$x_2 = (-a_0x_0 - a_1x_1)/a_2$,同第19行,也就是說這裏的$y$其實是$x_1$,而$x$是$x_1$。所以下圖表示的是$x_1$與$x_2$之間的關係。
分類效果圖以下:
三個紅色的點是原來$y=1$的樣本,有一個分錯了。這裏至關於將全部的數據用二維座標(x1, x2)表示了出來,並且根據迴歸參數畫出的線將這些點一分爲二。若是有新的樣本,不知道在哪一類,只用將該點畫在圖上,看它在這條直線的哪一邊就能夠分類了。
下面是使用90個訓練樣本獲得的結果:
能夠看出一個很是明顯的規律是,$y=1$的這一類樣本(紅色的點)具備更小的$x_2$值,當$x_2$相近時則具備更大的$x_1$值。
此時計算出來的迴歸係數a爲:
[[ 5.262118 ] [ 0.60847797] [-0.75168429]]
添加一個預測函數,以下:
直接將上面計算出來的迴歸係數a拿來使用,測試數據其實也是《機器學習實戰》這本書中的訓練數據,我拆成了兩份,前面90行用來作訓練數據,後面10行用來當測試數據。
1 def predict_test_sample(): 2 A = [5.262118, 0.60847797, -0.75168429] # 上面計算出來的迴歸係數a 3 dataArr, labelMat = loadDataSet(path, testing_sample) 4 h_test = sigmoid(mat(dataArr) * mat(A).transpose()) # 將讀入的數據和A轉化成numpy中的矩陣 5 print(h_test) # 預測的結果
調用上面的函數,能夠獲得如下結果,即h(a):
[[ 0.99714035] [ 0.04035907] [ 0.12535895] [ 0.99048731] [ 0.98075409] [ 0.97708653] [ 0.09004989] [ 0.97884487] [ 0.28594188] [ 0.00359693]]
下面是咱們的測試數據(原來的訓練樣本後十行的數據,包括標準答案y):
0.089392 -0.715300 1 1.825662 12.693808 0 0.197445 9.744638 0 0.126117 0.922311 1 -0.679797 1.220530 1 0.677983 2.556666 1 0.761349 10.693862 0 -2.168791 0.143632 1 1.388610 9.341997 0 0.317029 14.739025 0
比較咱們預測獲得的h(a)和標準答案y,若是按照0.5爲分界線的話,咱們利用前90個樣本訓練出來的分類器對後面10個樣本的類型預測所有正確。
附件:
github上的代碼更新到python3.6, 2019-1-6
完整代碼:https://github.com/OnlyBelter/MachineLearning_examples/tree/master/de_novo/regression
訓練數據:https://github.com/OnlyBelter/MachineLearning_examples/blob/master/de_novo/data/Logistic_Regression-trainingSample.txt
測試數據:https://github.com/OnlyBelter/MachineLearning_examples/blob/master/de_novo/data/Logistic_Regression-testingSample.txt
參考:
http://baike.baidu.com/item/logistic%E5%9B%9E%E5%BD%92
https://en.wikipedia.org/wiki/Sigmoid_function
《機器學習實戰》,哈林頓著,李銳等譯,人民郵電出版社,2013年6月初版