先從一本書提及吧----《機器學習實戰》算法
做者在書中講到邏輯迴歸的時候,用簡短的語言介紹了一下理論以後,就給出了一段代碼。然而就是這段代碼把我帶進了誤區,也許不能叫誤區,而是由於我本身的水平不夠。後來在查閱資料的時候,發現有人也由於這個問題糾結了很久。也許這本書是寫給一些有經驗的人員看的,不是特別適合做爲入門的書。編程
在查找關於邏輯迴歸相關資料的時候,發現大多數都是介紹了好多數學公式,因此我一直都在理解數學公式的基礎上同時試圖在腦海中演練該如何編程實現它,而後再對照上面提到的書中的代碼,而後悲哀的發現瞭解不了。而且,查到的大多數資料上並無詳細的代碼實現,若是有,也是跟書上的代碼是同樣的。app
最後,從網上找到書中使用的測試數據,跟蹤打印代碼中的每一個變量,才理解了書中第一段代碼的求解原理,進而理解了後面一些代碼的原理。如今回過頭想一想確實比較簡單,可是這個簡單是有一個前提的:書上的代碼或者資料中推導出的的公式或者我本身的理解,這三者之間必須是有一個錯誤的。dom
而且在這個過程當中,我一直試圖繞過那麼多數學公式和一堆概念,可是發現很難,因此我按照本身的理解剔除掉一些無用的數學公式和概念,力求用最少的理論解釋清楚什麼是邏輯迴歸。
機器學習
1.邏輯迴歸的定義ide
1)有一種定義是這樣的:邏輯迴歸實際上是一個線性分類器,只是在外面嵌套了一個邏輯函數,主要用於二分類問題。函數
這個定義明確的指明瞭邏輯迴歸的特色:學習
a) 一個線性分類器測試
b)外層有一個邏輯函數優化
2)假設有一個線性函數z,其通常公式爲:
(公式一)
轉換成求和公式:
(公式二)
轉化成向量的形式:
(公式三)
3)邏輯函數(也叫Sigmoid函數)
基本上採用的都是下面這個函數:
(公式四)
這個函數的做用就是把無限大或者無限小的數據壓縮到[0,1]之間,用來估計機率。圖像大體爲:
基本上是以0.5分界,0.5以上爲1,0.5如下爲0。可是這個分界值能夠本身設定。
4)邏輯迴歸函數
綜合公式四和公式一或者公式四和公式三,便可獲得邏輯迴歸函數:
(公式五)或者
(公式六)
其實,若是編程求解的話,到這裏基本就能夠了。可是既然都提到了最大似然估計,那咱們也說下。
2.最大似然估計
最大似然估計是創建在這樣的思想上:已知某個參數能使這個樣本出現的機率最大,咱們固然不會再去選擇其餘小几率的樣本,因此乾脆就把這個參數做爲估計的真實值。
換句話說就是:既然咱們沒法知道真實值,那麼就把這個看成真實值吧!
其惟一的做用就是給這個算法找一個說的過去的理論基礎,而後在這個基礎上推導出最大似然函數,接着構建損失函數。這對於非數學專業的人來講,用途並不大,有時候甚至會形成理解上的困難,進而變成學習的阻礙。其實,咱們徹底能夠繞過這個阻礙,只去關注最後的結果。
補充說明:若是想要理解推導過程,能夠先看看最大似然估計思想,而後也要理解聯合機率。由於有些講邏輯迴歸的文章會直接跳出最大似然估計的函數,若是不瞭解這兩點內容容易抓瞎。
3.求解方式
1)使用梯度降低法求解
通過一系列推導以後,得出梯度降低法求解的核心公式,即權重的更新方式:
(公式七)
須要說明的是:α表示降低的步長,能夠本身指定。hθ 表示損失函數或者懲罰係數。若是hθ 表示懲罰係數,那麼如何求的這組係數纔是整個邏輯迴歸算法的重點。
在開始寫代碼前,再介紹另一個求解方式:向量化
2)向量化
向量化是使用矩陣計算來代替for循環,以簡化計算過程,提升效率。(下面引用下其餘文章的講解:出現的地方太多不知道哪一個是原做者,見諒)
向量化過程:
約定訓練數據的矩陣形式以下,x的每一行爲一條訓練樣本,而每一列爲不一樣的特稱取值:
g(A)的參數A爲一列向量,因此實現g函數時要支持列向量做爲參數,並返回列向量。
θ更新過程能夠改成:
綜上所述,向量化後θ更新的步驟以下:
a)求 A=x*θ
b)求 E=g(A)-y
c)求
4.實現過程(下面的代碼實現的是梯度上升法,其跟梯度降低法的惟一區別就是和
之間的減號變成了加號,前者求最大值,後者求最小值)
代碼基本脫胎於《機器學習實戰》這本書,可是有改動。
1)普通的梯度上升法
下面這段代碼,也就是開頭提到的那個形成誤解的代碼,其實現依據是向量化求解,並非根據公式七來的,因此若是對照公式七理解這段代碼會徹底摸不着頭腦。若是對照向量化後θ(也就是權重)的更新步驟會很容易理解。
''' 普通的梯度上升法 ''' import numpy as np import os import pandas as pd def loadDataSet(): ##運行腳本所在目錄 base_dir=os.getcwd() ##記得添加header=None,不然會把第一行看成頭 data=pd.read_table(base_dir+r"\lr.txt",header=None) ##dataLen行dataWid列 :返回值是dataLen=100 dataWid=3 dataLen,dataWid = data.shape ##訓練數據集 xList = [] ##標籤數據集 lables = [] ##讀取數據 for i in range(dataLen): row = data.values[i] xList.append(row[0:dataWid-1]) lables.append(row[-1]) return xList,lables ##邏輯函數 def sigmoid(inX): return 1.0/(1+np.exp(-inX)) ##梯度上升函數 def gradAscent(datamatIn,classLables): ##把datamatIn從列表轉換成矩陣 dataMatrix = np.mat(datamatIn) ##把列表轉換成100行1列的矩陣,而np.mat(classLables)是轉換成1行100列的矩陣 labelMat = np.mat(classLables).transpose() ##求矩陣的長寬 m,n = np.shape(dataMatrix) ##步長,能夠本身設置 alpha = 0.001 ##最大循環次數 maxTry = 500 ##初始化向量:2行1列的矩陣 weights =np.ones((n,1)) ##循環必定次數,求權重 for k in range(maxTry): ##dataMatrix 100行2列 weights是2行1列 ##h是100行1列 h = sigmoid(dataMatrix*weights) ##向量的誤差 error = (labelMat - h) ##dataMatrix.transpose() 轉換成2行100列的矩陣 ##error 是100行1列 ##weights是2行1列的值 weights = weights + alpha*dataMatrix.transpose()*error return weights ''' 結果大於0.3的設置爲1,正確率基本100% ''' def GetResult(): dataMat,labelMat=loadDataSet() weights=gradAscent(dataMat,labelMat) dataMatrix = np.mat(dataMat) ##求的最後的結果 h = sigmoid(dataMatrix*weights) ##打印結果,觀察數據 for i in range(len(h)): print(str(h[i])+":"+str(labelMat[i])) #print(h) #print(weights) ##0.08108752 -0.1233496 if __name__=='__main__': GetResult()
2)隨機梯度上升發
這個算法,纔是符合公式七的算法,可是代碼中並無求和這步,只有括號中的那部分,這也是我開頭說的三者之間必有一個錯誤的地方。
只包括核心部分,其餘部分見上段代碼
''' 結果大於0.29或者0.26均可以,也只有1-2個分類錯誤 weights:[ 0.0868611 -0.13086297] ''' ##隨機梯度上升算法 def gradAscent(datamatIn,classLables): m,n = np.shape(datamatIn) ##步長,能夠本身指定,決定收斂速度 alpha = 0.001 ##最大循環次數 maxTry = 200 ##初始化權重:列表而不是矩陣 weights =np.ones(n) ##循環求解:在整個數據集上循環 for k in range(maxTry): ##對每行進行處理 for i in range(m): ##每行向量化 h = sigmoid(sum(datamatIn[i]*weights)) ##每行向量誤差 error = (classLables[i] - h) ##更新權重 weights = weights +alpha*error*datamatIn[i] return weights ##打印結果 def GetResult(): dataMat,labelMat=loadDataSet() weights=gradAscent(dataMat,labelMat) m,n = np.shape(dataMat) for i in range(m): h = sigmoid(sum(dataMat[i]*weights)) print(str(h)+" : "+str(labelMat[i])) #print(weights)
3)改進的隨機梯度上升算法
書中還講到了一個改進的隨機梯度上升算法。
##隨機梯度上升函數 def gradAscent(datamatIn,classLables): m,n = np.shape(datamatIn) ##循環次數 maxTry = 150 ##初始化權重:列表 weights =np.ones(n) ##循環求解 for j in range(maxTry): ##在整個數據集上循環 for i in range(m): ##跟新alpha,即跟新步長值 alpha = 4/(1.0+j+i)+0.01 ##隨機抽取一個下標 randIndex = int(np.random.uniform(0,m)) ##對抽到下標的數據行進行求值 h = sigmoid(sum(datamatIn[randIndex]*weights)) ##求得偏差值 error = classLables[randIndex] - h ##更新權重 weights = weights +alpha*error*datamatIn[randIndex] return weights
該算法每次都會調整步長值,即緩解了隨着循環次數的增長形成的特徵值的波動,也保證了當j<<max(i)時,步長值的降低不是嚴格降低的。而避免參數的嚴格降低在優化退火算法中經常用到。
5.使用sklearn包中的邏輯迴歸算法(非完整代碼,缺乏部分在第一個代碼段)
sklearn包中的LogisticRegression函數,默認使用L2正則化防止過分擬合。
from sklearn.linear_model import LogisticRegression def sk_lr(X_train,y_train): model = LogisticRegression() model.fit(X_train, y_train) model.score(X_train,y_train) #print('權重',model.coef_) return model.predict(X_train) ##分類錯了2個 def GetResult(): dataMat,labelMat=loadDataSet() pred = sk_lr(dataMat,labelMat) for i in range(len(pred)): print(str(pred[i])+" : "+str(labelMat[i])) if __name__=='__main__': GetResult()
最後得出的預測結果就是0,1值,跟標籤對比,有兩個分類錯了。
6.邏輯迴歸優缺點
優勢:計算代價不高,易於理解和實現
缺點:容易欠擬合,分類精度可能不高
適用數據類型:數值型和標稱型數據
附錄:測試數據
-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 0.850433 6.920334 1 1.347183 13.175500 0 1.176813 3.167020 1 -1.781871 9.097953 0 -0.566606 5.749003 1 0.931635 1.589505 1 -0.024205 6.151823 1 -0.036453 2.690988 1 -0.196949 0.444165 1 1.014459 5.754399 1 1.985298 3.230619 1 -1.693453 -0.557540 1 -0.576525 11.778922 0 -0.346811 -1.678730 1 -2.124484 2.672471 1 1.217916 9.597015 0 -0.733928 9.098687 0 -3.642001 -1.618087 1 0.315985 3.523953 1 1.416614 9.619232 0 -0.386323 3.989286 1 0.556921 8.294984 1 1.224863 11.587360 0 -1.347803 -2.406051 1 1.196604 4.951851 1 0.275221 9.543647 0 0.470575 9.332488 0 -1.889567 9.542662 0 -1.527893 12.150579 0 -1.185247 11.309318 0 -0.445678 3.297303 1 1.042222 6.105155 1 -0.618787 10.320986 0 1.152083 0.548467 1 0.828534 2.676045 1 -1.237728 10.549033 0 -0.683565 -2.166125 1 0.229456 5.921938 1 -0.959885 11.555336 0 0.492911 10.993324 0 0.184992 8.721488 0 -0.355715 10.325976 0 -0.397822 8.058397 0 0.824839 13.730343 0 1.507278 5.027866 1 0.099671 6.835839 1 -0.344008 10.717485 0 1.785928 7.718645 1 -0.918801 11.560217 0 -0.364009 4.747300 1 -0.841722 4.119083 1 0.490426 1.960539 1 -0.007194 9.075792 0 0.356107 12.447863 0 0.342578 12.281162 0 -0.810823 -1.466018 1 2.530777 6.476801 1 1.296683 11.607559 0 0.475487 12.040035 0 -0.783277 11.009725 0 0.074798 11.023650 0 -1.337472 0.468339 1 -0.102781 13.763651 0 -0.147324 2.874846 1 0.518389 9.887035 0 1.015399 7.571882 0 -1.658086 -0.027255 1 1.319944 2.171228 1 2.056216 5.019981 1 -0.851633 4.375691 1 -1.510047 6.061992 0 -1.076637 -3.181888 1 1.821096 10.283990 0 3.010150 8.401766 1 -1.099458 1.688274 1 -0.834872 -1.733869 1 -0.846637 3.849075 1 1.400102 12.628781 0 1.752842 5.468166 1 0.078557 0.059736 1 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