1. 程式人生 > >【機器學習演算法實現】logistic迴歸 基於Python和Numpy函式庫

【機器學習演算法實現】logistic迴歸 基於Python和Numpy函式庫

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow

也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!

               

【機器學習演算法實現】系列文章將記錄個人閱讀機器學習論文、書籍過程中所碰到的演算法,每篇文章描述一個具體的演算法、演算法的程式設計實現、演算法的具體應用例項。爭取每個演算法都用多種語言程式設計實現。所有程式碼共享至github:

https://github.com/wepe/MachineLearning-Demo     歡迎交流指正!


(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+

Θ1*x1+Θ2*x2=0  (x0=1)。

因為上圖是二維的,所以引數Θ=(Θ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)函式
分類函式,根據引數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)

這兩個引數可以根據實際情況調整


           

給我老師的人工智慧教程打call!http://blog.csdn.net/jiangjunshow

這裡寫圖片描述