【機器學習演算法實現】logistic迴歸 基於Python和Numpy函式庫
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow
也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!
【機器學習演算法實現】系列文章將記錄個人閱讀機器學習論文、書籍過程中所碰到的演算法,每篇文章描述一個具體的演算法、演算法的程式設計實現、演算法的具體應用例項。爭取每個演算法都用多種語言程式設計實現。所有程式碼共享至github:
(2)logistic迴歸__基於Python和Numpy函式庫
1、演算法簡介
本文的重點放在演算法的工程實現上,關於演算法的原理不具體展開,logistic迴歸演算法很簡單,可以看看Andrew Ng的視訊: https://class.coursera.org/ml-007 ,也可以看看一些寫得比較好的博文: 洞庭之子的博文 。下面我只列出一些個人認為重要的點。迴歸的概念
logistic迴歸演算法之所以稱作“logistic”,是因為它運用了logistic函式,即sigmoid函式。
logistic迴歸演算法一般用於二分類問題(當然也可以多類別,後面會講)。
logistic迴歸的演算法思想:
用上面的圖來分析,每個O或X代表一個特徵向量,這裡是二維的,可以寫成x=(x1,x2)。
用logistic迴歸進行分類的主要思想就是根據現有資料集,對分類邊界建立迴歸公式,拿上面這個圖來說,就是根據
這些OOXX,找出那條直線的公式:Θ0*x0+Θ1*x1+Θ2*x2=Θ0+
因為上圖是二維的,所以引數Θ=(Θ0,Θ1, Θ2),分類邊界就由這個(Θ0,Θ1, Θ2)確定,對於更高維的情況也
是一樣的,所以無論二維三維更高維,分類邊界可以統一表示成f(x)=ΘT*x (ΘT表示Θ的轉置)。
對於分類邊界上的點,代入分類邊界函式就得到f(x)=0,同樣地,對於分類邊界之上的點,代入得到f(x)>0,對於分
類邊界之下的點,代入得到f(x)<0。這樣就可以依據f(x)大於0或者小於0來分類了。
logistic迴歸的最後一步就是將f(x)作為輸入,代入Sigmoid函式,當f(x)>0時,sigmoid函式的輸出就大0.5,且隨
著f(x)趨於正無窮,sigmoid函式的輸出趨於1。當f(x)<0時,sigmoid函式的輸出就小於0.5,且隨著f(x)趨於負無
窮,sigmoid函式的輸出趨於0。
所以我們要尋找出最佳引數Θ,使得對於1類別的點x,f(x)趨於正無窮,對於0類別的點x,f(x)趨於負無窮(實際編
程中不可能正/負無窮,只要足夠大/小即可)。
總結一下思緒,logistic迴歸的任務就是要找到最佳的擬合引數。下圖的g(z)即sigmoid函式,跟我上面講的一樣,
將f(x)=ΘT*x作為g(z)的輸入。
以上就是logistic迴歸的思想,重點在於怎麼根據訓練資料求得最佳擬合引數Θ?這可以用最優化演算法來求解,比如
常用的梯度上升演算法,關於梯度上升演算法這裡也不展開,同樣可以參考上面推薦的博文。
所謂的梯度,就是函式變化最快的方向,我們一開始先將引數Θ設為全1,然後在演算法迭代的每一步裡計算梯度,沿
著梯度的方向移動,以此來改變引數Θ,直到Θ的擬合效果達到要求值或者迭代步數達到設定值。Θ的更新公式:
alpha是步長,一系列推導後:
這個公式也是下面寫程式碼所用到的。
後話:理解logistic迴歸之後可以發現,其實它的本質是線性迴歸,得到ΘT*x的過程跟線性迴歸是一樣的,只不過
後面又將ΘT*x作為logistic函式的輸入,然後再判斷類別。
2、工程例項
logistic迴歸一般用於二分類問題,比如判斷一封郵件是否為垃圾郵件,判斷照片中的人是男是女,預測一場比賽輸還
是贏......當然也可以用於多分類問題,比如k類別,就進行k次logistic迴歸。
我的前一篇文章:kNN演算法__手寫識別 講到用kNN演算法識別數字0~9,這是個十類別問題,如果要用logistic迴歸,
得做10次logistic迴歸,第一次將0作為一個類別,1~9作為另外一個類別,這樣就可以識別出0或非0。同樣地可以將
1作為一個類別,0、2~9作為一個類別,這樣就可以識別出1或非1........
本文的例項同樣是識別數字,但為了簡化,我只選出0和1的樣本,這是個二分類問題。下面開始介紹實現過程:
(1)工程檔案說明
在我的工程檔案目錄下,有訓練樣本集train和測試樣本集test,原始碼檔案命名為logistic regression.py
訓練樣本集train和測試樣本集test裡面只有0和1樣本:
logistic regression.py實現的功能:從train裡面讀取訓練資料,然後用梯度上升演算法訓練出引數Θ,接著用引數Θ來預
測test裡面的測試樣本,同時計算錯誤率。
(2)原始碼解釋
- loadData(direction)函式
實現的功能就是從資料夾裡面讀取所有訓練樣本,每個樣本(比如0_175.txt)裡有32*32的資料,程式將32*32的資料
整理成1*1024的向量,這樣從每個txt檔案可以得到一個1*1024的特徵向量X,而其類別可以從檔名“0_175.txt”裡截
取0出來。因此,從train資料夾我們可以獲得一個訓練矩陣m*1024和一個類別向量m*1,m是樣本個數。
def loadData(direction): trainfileList=listdir(direction) m=len(trainfileList) dataArray= zeros((m,1024)) labelArray= zeros((m,1)) for i in range(m): returnArray=zeros((1,1024)) #每個txt檔案形成的特徵向量 filename=trainfileList[i] fr=open('%s/%s' %(direction,filename)) for j in range(32): lineStr=fr.readline() for k in range(32): returnArray[0,32*j+k]=int(lineStr[k]) dataArray[i,:]=returnArray #儲存特徵向量 filename0=filename.split('.')[0] label=filename0.split('_')[0] labelArray[i]=int(label) #儲存類別 return dataArray,labelArray
程式碼裡面用到python os模組裡的listdir(),用於從資料夾裡讀取所有檔案,返回的是列表。
python裡的open()函式用於開啟檔案,之後用readline()一行行讀取
- sigmoid(inX)函式
def sigmoid(inX): return 1.0/(1+exp(-inX))
- gradAscent(dataArray,labelArray,alpha,maxCycles)函式
用梯度下降法計算得到迴歸係數,alpha是步長,maxCycles是迭代步數。
def gradAscent(dataArray,labelArray,alpha,maxCycles): dataMat=mat(dataArray) #size:m*n labelMat=mat(labelArray) #size:m*1 m,n=shape(dataMat) weigh=ones((n,1)) for i in range(maxCycles): h=sigmoid(dataMat*weigh) error=labelMat-h #size:m*1 weigh=weigh+alpha*dataMat.transpose()*error return weigh
用到numpy裡面的mat,矩陣型別。shape()用於獲取矩陣的大小。
這個函式返回引數向量Θ,即權重weigh
- classfy(testdir,weigh)函式
def classfy(testdir,weigh): dataArray,labelArray=loadData(testdir) dataMat=mat(dataArray) labelMat=mat(labelArray) h=sigmoid(dataMat*weigh) #size:m*1 m=len(h) error=0.0 for i in range(m): if int(h[i])>0.5: print int(labelMat[i]),'is classfied as: 1' if int(labelMat[i])!=1: error+=1 print 'error' else: print int(labelMat[i]),'is classfied as: 0' if int(labelMat[i])!=0: error+=1 print 'error' print 'error rate is:','%.4f' %(error/m)
- digitRecognition(trainDir,testDir,alpha=0.07,maxCycles=10)函式
整合上面的所有函式,呼叫這個函式進行數字識別,alpha和maxCycles有預設形參,這個可以根據實際情況更改。
def digitRecognition(trainDir,testDir,alpha=0.07,maxCycles=10): data,label=loadData(trainDir) weigh=gradAscent(data,label,alpha,maxCycles) classfy(testDir,weigh)
用loadData函式從train裡面讀取訓練資料,接著根據這些資料,用gradAscent函式得出引數weigh,最後就可以用擬 合引數weigh來分類了。
3、試驗結果
工程檔案可以到這裡下載:github地址
執行logistic regression.py,採用預設形參:alpha=0.07,maxCycles=10,看下效果,錯誤率0.0118
>>> digitRecognition('train','test')
改變形參,alpah=0.01,maxCycles=50,看下效果,錯誤率0.0471
>>> digitRecognition('train','test',0.01,50)
這兩個引數可以根據實際情況調整