理解SVM ——入門SVM和代碼實現

這篇博客我們來理解一下SVM。其實,之前好多大牛的博客已經對SVM做了很好的理論描述。例如CSDN上july的那篇三層境界介紹SVM的博文,連接如下:

http://blog.csdn.net/v_july_v/article/details/7624837

那麼我這裏拋去一些複雜的公式推導,給出一些SVM核心思想,以及用Python實現代碼,再加上我自己的理解註釋。

 

1. SVM的核心思想

SVM的分類思想本質上和線性迴歸LR分類方法類似,就是求出一組權重係數,在線性表示之後可以分類。我們先使用一組trainging set來訓練SVM中的權重係數,然後可以對testingset進行分類。

說的更加更大上一些:SVM就是先訓練出一個分割超平面separation hyperplane, 然後該平面就是分類的決策邊界,分在平面兩邊的就是兩類。顯然,經典的SVM算法只適用於兩類分類問題,當然,經過改進之後,SVM也可以適用於多類分類問題。

我們希望找到離分隔超平面最近的點,確保它們離分隔面的距離儘可能遠。這裏點到分隔面的距離被稱爲間隔margin. 我們希望這個margin儘可能的大。支持向量support vector就是離分隔超平面最近的那些點,我們要最大化支持向量到分隔面的距離。

那麼爲了達到上面的目的,我們就要解決這樣的一個問題:如何計算一個點到分隔面的距離?這裏我們可以借鑑幾何學中點到直線的距離,需要變動的是我們這裏是點到超平面的距離。具體轉換過程如下:

 

除了定義距離之外,我們還需要設定兩類數據的類別標記分別爲1和-1,這樣做是因爲我們將標記y和距離相乘,不管屬於哪一類,乘積都是一個正數,這樣有利於我們設計目標函數。

這樣我們便可以定義我們SVM的目標函數:

 

求解這個問題需要經過一系列的轉換。具體如下:

 

SMO算法的目標是求出一系列alpha和b,一旦求出了這些alpha,就很容易計算出權重向量w,並得到分隔超平面。SMO的工作遠離是:每次循環中選擇兩個alpha進行優化處理,一旦找到一對合適的alpha,那麼就增大其中一個,同時減小另一個。選擇的alpha要滿足在間隔邊界之外的條件,而且還沒有進行過區間化處理或者不再邊界上。

 

《機器學習實戰》中給出了利用Python實現SVM的代碼,我加了一些註釋便於理解,如下:

 

<span style="font-size:24px;">from numpy import * 
import random 

def loadDataSet(fileName):  #構建數據庫和標記庫
    dataMat = []; labelMat = [] 
    fr = open(fileName) 
    for line in fr.readlines(): 
        lineArr = line.strip().split('\t') 
        dataMat.append([float(lineArr[0]), float(lineArr[1])]) 
        labelMat.append(float(lineArr[2]))  #只有一列
    return dataMat, labelMat 


def selectJrand(i, m):  #生成一個隨機數
    j=i 
    while(j==i): 
        j=int(random.uniform(0, m))  #生成一個[0, m]的隨機數,int轉換爲整數。注意,需要import random
    return j 

def clipAlpha(aj, H, L):  #閾值函數
    if aj>H: 
        aj=H 
    if aj<L: 
        aj=L 
    return aj 

def smoSimple(dataMatIn, classLabels, C, toler, maxIter): 
    dataMatrix = mat(dataMatIn); labelMat = mat(classLabels).transpose()
    b = 0; m,n = shape(dataMatrix)
    alphas = mat(zeros((m,1)))
    iter = 0
    while(iter<maxIter):  #迭代次數
        alphaPairsChanged=0 
        for i in range(m):  #在數據集上遍歷每一個alpha
            #print alphas 
            #print labelMat
            fXi = float(multiply(alphas,labelMat).T*(dataMatrix*dataMatrix[i,:].T)) + b
            #fXi=float(np.multiply(alphas, labelMat).T*dataMatrix*dataMatrix[i, :].T)+b  #.T也是轉置
            Ei=fXi-float(labelMat[i]) 
            if((labelMat[i]*Ei<-toler) and (alphas[i]<C)) or ((labelMat[i]*Ei>toler) and (alphas[i]>0)): 
                j=selectJrand(i, m)  #從m中選擇一個隨機數,第2個alpha j
                fXj=float(multiply(alphas, labelMat).T*dataMatrix*dataMatrix[j, :].T)+b 
                Ej=fXj-float(labelMat[j]) 
                
                alphaIold=alphas[i].copy()  #複製下來,便於比較
                alphaJold=alphas[j].copy() 
                
                if(labelMat[i]!=labelMat[j]):  #開始計算L和H
                    L=max(0, alphas[j]-alphas[i]) 
                    H=min(C, C+alphas[j]-alphas[i]) 
                else: 
                    L=max(0, alphas[j]+alphas[i]-C) 
                    H=min(C, alphas[j]+alphas[i]) 
                if L==H: 
                    print 'L==H' 
                    continue 
                
                #eta是alphas[j]的最優修改量,如果eta爲零,退出for當前循環
                eta=2.0*dataMatrix[i, :]*dataMatrix[j, :].T-\
                    dataMatrix[i, :]*dataMatrix[i, :].T-\
                    dataMatrix[j, :]*dataMatrix[j, :].T 
                if eta>=0: 
                    print 'eta>=0' 
                    continue 
                alphas[j]-=labelMat[j]*(Ei-Ej)/eta  #調整alphas[j] 
                alphas[j]=clipAlpha(alphas[j], H, L)  
                if(abs(alphas[j]-alphaJold)<0.00001):  #如果alphas[j]沒有調整
                    print 'j not moving enough' 
                    continue 
                alphas[i]+=labelMat[j]*labelMat[i]*(alphaJold-alphas[j])  #調整alphas[i]
                b1=b-Ei-labelMat[i]*(alphas[i]-alphaIold)*\
                    dataMatrix[i, :]*dataMatrix[i, :].T-\
                    labelMat[j]*(alphas[j]-alphaJold)*\
                    dataMatrix[i, :]*dataMatrix[j, :].T 
                b2=b-Ej-labelMat[i]*(alphas[i]-alphaIold)*\
                    dataMatrix[i, :]*dataMatrix[j, :].T-\
                    labelMat[j]*(alphas[j]-alphaJold)*\
                    dataMatrix[j, :]*dataMatrix[j, :].T 
                
                if(0<alphas[i]) and (C>alphas[i]): 
                    b=b1 
                elif(0<alphas[j]) and (C>alphas[j]): 
                    b=b2 
                else: 
                    b=(b1+b2)/2.0 
                alphaPairsChanged+=1 
                
                print 'iter: %d i: %d, pairs changed %d' %(iter, i, alphaPairsChanged) 
        if(alphaPairsChanged==0): 
            iter+=1 
        else: 
            iter=0 
        print 'iteration number: %d' %iter
    return b, alphas 

                
if __name__=="__main__": 
    dataArr, labelArr=loadDataSet('testSet.txt') 
    b, alphas=smoSimple(dataArr, labelArr, 0.6, 0.001, 40)
    
    print b, alphas </span>


還有其他語言實現的SVM,可以在下面網站中找到,以及一些註釋:

 

http://www.csie.ntu.edu.tw/~cjlin/libsvm/

http://www.pami.sjtu.edu.cn/people/gpliu/document/libsvm_src.pdf

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章