邏輯迴歸(Logistic)雖帶有迴歸二字,但它倒是一個經典的二分類算法,它適合處理一些二分類任務,例如疾病檢測、垃圾郵件檢測、用戶點擊率以及上文所涉及的正負情感分析等等。python
首先了解一下何爲迴歸?假設如今有一些數據點,咱們利用一條直線對這些點進行擬合(該線稱爲最佳擬合直線),這個擬合的過程就稱做迴歸。利用邏輯迴歸進行分類的主要思想是:根據現有數據對分類邊界線創建迴歸公式,以此進行分類。算法
線性迴歸算法後面的筆記會介紹,這裏簡單對比一下二者,邏輯迴歸和線性迴歸的本質相同,都意在擬合一條直線,但線性迴歸的目的是擬合輸入變量的分佈,儘量讓全部樣本到該條直線的距離最短;而邏輯迴歸的目的是擬合決策邊界,使數據集中不一樣的樣本儘量分開,因此兩個算法的目的是不一樣的,處理的問題也不一樣。數組
咱們想要的函數應該是,能接受全部的輸入而且預測出類別,好比二分類中的0或者一、正或者負,這種性質的函數被稱爲海維賽德階躍函數,圖像以下:app
但這種函數的問題在於從0跳躍到1的過程很是難處理,好比咱們常接觸的屢次函數,可能在某種條件下須要求導解決問題;而Sigmoid函數也具備相似的性質,而且在數學上更容易處理,其公式以下:函數
下圖是Sigmoid函數在不一樣座標尺度下的兩條曲線圖。當x爲0時,Sigmoid函數值爲0.5,隨着x的增大,對應的Sigmoid值將逼近於1;而隨着x的減少,Sigmoid值將逼近於0。若是橫座標刻度足夠大,Sigmoid函數看起來就很像一個階躍函數。學習
若咱們將Sigmoid函數的輸入記做z,可推出下面公式:spa
它表示將這兩個數值向量對應元素相乘而後所有相加起來獲得z值,其中向量x是分類器的輸入數據,向量w就是咱們要找到的能使分類器儘量準確的最佳參數。3d
由上述公式就可得:code
其中$h_w(x)$的做用就是給定輸入時,輸出分類爲正向類(1)的可能性。例如,對於一個給定的x,$h_w(x)$的值爲0.8,則說明有80%的可能輸出爲正向類(1),有20%的可能輸出爲負向類(0),兩者成補集關係。視頻
對於一個二分類問題而言,咱們給定輸入,函數最終的輸出只能有兩類,0或者1,因此咱們能夠對其分類。
爲了運算便捷,咱們將其整合爲一個公式,以下:
因爲乘法計算量過大,因此能夠將乘法變加法,對上式求對數,以下:
能夠看出當y=1時,加號後面式子的值爲0;當y=0時,加號前面式子的值爲0,這與上文分類式子達到的效果是同樣的。L(w)稱爲似然函數,l(w)稱爲對數似然函數,是依據最大似然函數推導而成。此時的應用是梯度上升求最大值,若是梯度降低求最小值,可在公式以前乘以$-\frac{1}{n}$。
爲了學習嘛,這裏再介紹一下另外一種方式,利用損失函數推導應用於梯度降低的公式;損失函數是衡量真實值與預測值之間差距的函數,因此損失函數值越小,對應模型的效果也越好,損失函數公式以下:
可能只看公式理解相對抽象,經過對應的函數圖像足以理解上式,以下:
注意!!!公式後面的y*不表明縱座標 !!!
當類標籤y=1時,對應的-log(x)圖像越接近於1,其距離x軸越近,表明其損失越小;反之當類標籤y=0時,對應的-log(1-x)圖像越接近於0,其距離x軸越近,表明其損失越小,也就是預測值越接近於真實值。
將兩個損失函數綜合起來得:
對於m個樣本,總損失函數爲:
其中m爲樣本個數、yi爲標籤,可取0或一、i爲第i個樣本、$p(x_i)$爲預測輸出。
上面已經列出了一大堆的公式,難道這又要開始一連串的大公式?
心態放平,上文雖然說公式有點多,但目的都是爲了推出最後的對數似然函數或者總損失函數,掌握這兩個是關鍵,梯度上升和梯度降低也會利用上面的公式作推導,因此兩者之間存在關聯。首先梯度降低你須要瞭解一下何爲梯度?
若是將梯度記爲▽,則函數f(x,y)的梯度可由下式表示:
通俗的說,即對多元函數的參數求偏導,並把求得的各個參數的偏導以向量的形式寫出來。或者你只要理解這個梯度要沿着x的方向移動$\frac{\delta f(x,y)}{\delta x}$,沿着y方向移動$\frac{\delta f(x,y)}{\delta y}$足以,但f(x,y)必需要在待計算的點上有定義且可微。
下圖爲一個梯度降低的例子,梯度降低法在到達每個點以後都會從新評估下一步將要移動的方向。從x0開始,在計算完該點的梯度,函數就會移動到下一個點x1。在x1點,梯度會從新計算,繼而移動到x2點。如此循環迭代此過程,直到知足中止條件,每次迭代過程都是爲了找出當前能選取到的最佳移動方向。
以前一直在討論移動方向,而未提到過移動量的大小。該量值稱爲步長,記做$\alpha$。那麼就能夠得出梯度上升法的迭代公式
:
因此對於上文所說起的對數似然函數$J(w)$,咱們也能夠利用上述迭代的方式,一步一步移動到目標值或者無限接近於目標值,$J(w)$求偏導的公式以下:
可能有的人看到這個偏導公式有點蒙,其實這裏面用到的三個函數公式都是上文所說起的,來回顧一下。
求偏導過程涉及到高數知識,即最外層函數對外層函數求偏導、外層函數對內層函數求偏導、內層函數對其元素求偏導,三者相乘可得出所需偏導。推導過程有些麻煩,這裏只給出推導結果,在後面運用時咱們也只會運用到最終結果,以下:
若是利用將對數似然函數換成損失函數$J(\Theta)$,則獲得的是有關計算梯度降低的公式,以下:
兩個公式中的w和$\Theta$的含義是同樣的,都表明咱們所求的最佳迴歸係數,兩個公式對比能夠看出梯度上升和梯度降低只有加減號區別之分。下面這個動圖就能夠很好的展現梯度降低法的過程:
公式推導部分至此結束了,基礎偏好的夥伴可能一遍就懂了,但基礎偏弱理解起來比較困難,偶當時也是對着書、跟着視頻啃了很久,多啃幾遍終歸會理解的。
有這樣一份數據集,共100個樣本、兩個特徵(X1與X2)以及一個分類標籤,部分數據和所繪製圖像以下:
X1 | X2 | 類別 |
---|---|---|
0.197445 | 9.744638 | 0 |
0.078557 | 0.059736 | 1 |
-1.099458 | 1.688274 | 1 |
1.319944 | 2.171228 | 1 |
-0.783277 | 11.009725 | 0 |
在此數據集上,咱們將經過梯度降低法找到最佳迴歸係數,也就是擬合出Logistic迴歸模型的最佳參數。
該算法的僞代碼以下:
每一個迴歸係數初始化爲1 重複R次: 計算整個數據集的梯度 使用alpha*gradient更新迴歸係數的向量 返回迴歸係數
def loadDataSet(): dataMat = [] # 建立數據列表 labelMat = [] # 建立標籤列表 fr = open('LRData.txt','r',encoding='utf-8') #逐行讀取所有數據 for line in fr.readlines(): #將數據分割、存入列表 lineArr = line.strip().split() #數據存入數據列表 dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])]) #標籤存入標籤列表 labelMat.append(int(lineArr[2])) fr.close() return dataMat, labelMat
loadDataSet函數的做用是打開存儲數據的文本文件並逐行讀取。每行的前兩個值分別對應X1和X2,第三個值是數據對應的類別標籤。爲了方便計算,函數還在X1和X2以前添加了一個值爲1.0的X1,X1能夠理解爲偏置,即下圖中的x0。
#sigmoid函數 def sigmoid(inX): return 1.0 / (1 + np.exp(-inX))
sigmoid函數就是傳入一個參數(這裏是一個100*1的向量),經過公式計算並返回值。
def gradAscent(dataMatIn, classLabels): # 將列表轉換成numpy的matrix(矩陣) dataMatrix = np.mat(dataMatIn) # 將列表轉換成numpy的mat,並進行轉置 labelMat = np.mat(classLabels).T # 獲取dataMatrix的行數和列數。 m, n = np.shape(dataMatrix) # 設置每次移動的步長 alpha = 0.001 # 設置最大迭代次數 maxCycles = 500 # 建立一個n行1列都爲1的矩陣 weights = np.ones((n,1)) for k in range(maxCycles): # 公式中hΘ(x) h = sigmoid(dataMatrix * weights) # 偏差,即公式中y-hΘ(x) error = labelMat - h # 套入總體公式 weights = weights + alpha * dataMatrix.T * error return weights
最後weights返回的是一個3x1的矩陣,運行截圖以下:
gradAscent傳入參數爲loadDataSet的兩個返回值,而後經過numpy的mat方法將dataMatrix和labelMat 分爲轉化爲100x3和1x100的矩陣,但labelMat 通過T轉置後變成100x1的矩陣。而後初始化權重,利用的方法就是建立一個n行1列的矩陣。整個算法的關鍵處於for循環中,咱們先回顧一下上文的兩個公式。
其中h的計算結果即$h_w(x)$,權重weight爲W向量,輸入矩陣dataMatrix爲x向量。偏差error表明$y^{(i)}-h_w x^{(i)}$,有人可能會發現$\frac{1}{m}$沒有出如今代碼中,由於$\alpha$和$\frac{1}{m}$都爲常數,兩者相乘也爲常數,因此只須要用$\alpha$代替便可。
公式中的加和部分又怎麼體現呢?若是學過線性代數或者瞭解numpy運算的夥伴應該都理解矩陣的乘法,不理解也沒有關係,看下圖這個例子,當兩個矩陣相乘時,對應元素之間會求和做爲最終元素。
def plotDataSet(weight): #獲取權重數組 weight = weight.getA() # 加載數據和標籤 dataMat, labelMat = loadDataSet() # 將列表轉換成numpy的array數組 dataArr = np.array(dataMat) #獲取樣本個數 n = np.shape(dataMat)[0] #建立4個空列表,1表明正樣本、0表明負樣本 xcord1 = []; ycord1 = [] xcord0 = []; ycord0 = [] # 遍歷標籤列表,根據數據的標籤進行分類 for i in range(n): if int(labelMat[i]) == 1: # 若是標籤爲1,將數據填入xcord1和ycord1 xcord1.append(dataArr[i,1]); ycord1.append(dataArr[i,2]) else: # 若是標籤爲0,將數據填入xcord0和ycord0 xcord0.append(dataArr[i,1]); ycord0.append(dataArr[i,2]) #繪製圖像 fig = plt.figure() ax = fig.add_subplot(111) ax.scatter(xcord1, ycord1, s = 20, c = 'red', marker = '*',label = 'Class1') ax.scatter(xcord0, ycord0, s = 20, c = 'green',marker = 's',label = 'Class2') #繪製直線,sigmoid設置爲0 x = np.arange(-3.0, 3.0, 0.1) y = (-weight[0] - weight[1] * x) / weight[2] ax.plot(x, y) #標題、x標籤、y標籤 plt.title('LRData') plt.legend(loc='upper left') plt.xlabel('X1'); plt.ylabel('X2') plt.savefig("E:\machine_learning\LR03.jpg") plt.show()
這部分代碼惟一須要注意的是,將sigmoid的值設置爲0,能夠回憶一下文章剛開始時的Sigmoid函數圖像,0是兩個分類的分解處。所以,咱們設定$0=w_0x_0+w_1x_1+w_2x_2$,$x_0$的值爲1,因此已知迴歸係數,就可求得$x_1和x_2$的關係式,從而畫出決策邊界。
上圖能夠看出分類的效果仍是不錯的,根據函數繪製出的直線已經很大程度上將兩類樣本分隔開,100個樣本中,只有五個樣本分類錯誤,其中有三個仍是位於迴歸線上。
本文所講的梯度上升公式,是屬於批量梯度上升,此外還有隨機梯度上升、小批量梯度上升,而批量梯度上升每次計算都要計算全部的樣本,因此程序計算過程是十分複雜的,而且容易收斂到局部最優,而隨機梯度上升將會對算法進行調優,下一篇文章將會介紹隨機梯度上升,並分析二者之間的區別。
歡迎關注公衆號【奶糖貓】,後臺回覆"邏輯迴歸"可獲取數據和源碼供參考,感謝閱讀。