支持向量機(jī)

機(jī)器學(xué)習(xí)實戰(zhàn)

支持向量機(jī)(SVM)被認(rèn)為是最好的現(xiàn)成分類器,意味著在數(shù)據(jù)上應(yīng)用基本形式的SVM分類器就可以得到低錯誤率的結(jié)果寇僧。SVM能夠?qū)τ?xùn)練集之外的數(shù)據(jù)點做出很好的分類決策艾蓝。這里介紹最流行的一種是實現(xiàn)即序列最小化算法摹量。

基于最大間隔分隔數(shù)據(jù)

A

B

上面兩組圖中能夠都畫出一條直線將圓形點和方形點進(jìn)行區(qū)分?
B組圖中能夠很容易的畫出這樣的一條直線將數(shù)據(jù)集分開瞄勾,這樣的數(shù)據(jù)叫做線性可分?jǐn)?shù)據(jù)糊昙。這樣的一條直線稱為分隔超平面辛掠。在上線給出的例子當(dāng)中谢谦,由于數(shù)據(jù)點是二維的释牺,所以分隔超平面是一條直線。如果是數(shù)據(jù)是三維的則這分隔超平面是一個面回挽。我們希望找到距離分隔超平面最近的點没咙,確保它們離分隔面的距離盡可能給你的遠(yuǎn)。這里點到分隔面的距離被稱為間隔千劈。

支持向量機(jī) 就是離分隔超平面最近的那些點祭刚。

我們就是要找到最大化支持向量到分隔面的距離,需要找到此問題的優(yōu)化求解方法墙牌。

尋找最大間隔

圖3

圖3中H_2H_3都是可以分隔數(shù)據(jù)的超平面涡驮,但是怎么求解最佳分隔直線呢?
分隔超平面的形式可以定義為W^TX+b = 0的形式喜滨,要計算圖中點到超平面的距離需要給出點到分隔面的法線或垂線的長度捉捅,即|W^T+b|/||W||
其中

W:
X: 訓(xùn)練實例
b: bias

圖4

假設(shè)現(xiàn)在是二維的情況下
X = (x1, X2)虽风。
則超平面方程是 :
b+w_1x_1+w_2x_2 = 0
則所有超平面右上方的點滿足:b+w_1x_1+w_2x_2 > 0
所有超平面左下方的點滿足:b+w_1x_1+w_2x_2 < 0

這樣我們可以定義個函數(shù)是的f(W^Tx+b),當(dāng)u<0時f(u)輸出-1棒口,反之輸出+1寄月。當(dāng)計算數(shù)據(jù)點到分隔面的距離并確定分隔面的位置時,間隔通過label*(W^Tx+b)來計算无牵。如果數(shù)據(jù)點處于正方向并且里分隔超平面很遠(yuǎn)的位置時漾肮,(W^Tx+b)會是一個很大的正數(shù),同時label*(W^Tx+b)也會是一個很大的正數(shù)茎毁。而如果數(shù)據(jù)點處于負(fù)方向并且離分隔超平面很遠(yuǎn)的位置時克懊,此時由于類別標(biāo)簽為-1,怎label*(W^Tx+b)任然是個很大的正數(shù)七蜘。
現(xiàn)在需要找出其中w和b保檐。為此我們需要找到具有最小間隔的數(shù)據(jù)點,而這些數(shù)據(jù)點也是前面提到的支持向量崔梗。一旦找到具有最小間隔的數(shù)據(jù)點夜只,我們就需要對該間隔最大化。

  • 代碼示例
//引入numpy
from numpy import *
//讀取文件數(shù)據(jù)
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
#隨機(jī)數(shù)獲取
def selectJrand(i,m):
    j = i;
    while(j==i):
        j = int(random.uniform(0,m))
    return j
//參數(shù)矯正
def clipAlpha(aj,H,L):
    if aj > H:
        aj = H
    if L > aj:
        aj = L
    return aj



'''
創(chuàng)建一個Alpha向量并將其初始化為0向量
當(dāng)?shù)螖?shù)小于最大迭代次數(shù)時(外循環(huán))
    對數(shù)據(jù)集中的每個數(shù)據(jù)向量(內(nèi)循環(huán)):
        如果該向量可以被優(yōu)化:
            隨機(jī)選擇另一個數(shù)據(jù)向量
            同時優(yōu)化這兩個向量
            如果兩個向量都不能被優(yōu)化蒜魄,退出內(nèi)循環(huán)
如果所有向量都沒有被優(yōu)化扔亥,增加迭代數(shù)目,繼續(xù)下一次循環(huán)

'''
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):
        #每次循環(huán)都將alphaPairsChanged設(shè)為0.在對整個集合順序遍歷谈为。用于記錄alpha是否優(yōu)化
        alphaPairsChanged = 0
        for i in range(m):
            #計算fXi這就是我們預(yù)測的類別 然后基于這個實例的預(yù)測結(jié)果和真是結(jié)果的對比 就可以計算Ei誤差
            fXi = float(multiply(alphas,labelMat).T*(dataMatrix*dataMatrix[i,:].T)) + b
            Ei = fXi - float(labelMat[i])#if checks if an example violates KKT conditions
            #如果誤差很大就需要對該實例對應(yīng)的alpha值進(jìn)行優(yōu)化
            #if語句中不管是正間隔還是負(fù)間隔都會被測試旅挤,并且在該if語句中,也同時檢查alpha的值伞鲫,以保證其不能等于0或C粘茄。
            #由于后面alpha小于0或者大于C時將會被調(diào)整為0或C,所有一旦在該if語句中等于這兩個值的話秕脓,就不需要再優(yōu)化了
            if ((labelMat[i]*Ei < -toler) and (alphas[i] < C)) or ((labelMat[i]*Ei > toler) and (alphas[i] > 0)):
                #隨機(jī)選擇一個值
                j = selectJrand(i,m)
                #計算誤差
                fXj = float(multiply(alphas,labelMat).T*(dataMatrix*dataMatrix[j,:].T)) + b
                Ej = fXj - float(labelMat[j])
                #計算兩個alpha的誤差
                alphaIold = alphas[i].copy(); alphaJold = alphas[j].copy();
                #計算L和H 用于將alpha[j]調(diào)整到0到C之間
                if (labelMat[i] != labelMat[j]):
                    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
                #alpha[j]的最優(yōu)修改量
                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] 如果輕微改變則結(jié)束循環(huán)
                alphas[j] -= labelMat[j]*(Ei - Ej)/eta
                alphas[j] = clipAlpha(alphas[j],H,L)
                if (abs(alphas[j] - alphaJold) < 0.00001): print( "j not moving enough"); continue
                alphas[i] += labelMat[j]*labelMat[i]*(alphaJold - alphas[j])#update i by the same amount as j
                                                                        #the update is in the oppostie direction
                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__':
    dataMat,labelMat = loadDataSet('svn.txt')
    b,alphas = smoSimple(dataMat,labelMat,0.6,0.001,40)
    for i in range(100):
        if alphas[i]>0:
            print(dataMat[i],labelMat[i])

[4.658191, 3.507396] -1.0
[3.457096, -0.082216] -1.0
[6.080573, 0.418886] 1.0
  • sklearn示例
from sklearn import svm
if __name__ == '__main__':
    dataMat,labelMat = loadDataSet('svn.txt')
    #SVC有很多參數(shù)提供調(diào)試選擇柒瓣,達(dá)到最優(yōu)的解
    clf = svm.SVC(kernel='linear')
    clf.fit(dataMat,labelMat)
    print(clf)
    print(clf.support_vectors_)

SVC(C=1.0, cache_size=200, class_weight=None, coef0=0.0,
  decision_function_shape=None, degree=3, gamma='auto', kernel='linear',
  max_iter=-1, probability=False, random_state=None, shrinking=True,
  tol=0.001, verbose=False)
[[ 4.658191  3.507396]
 [ 3.457096 -0.082216]
 [ 6.080573  0.418886]]

利用完整Platte SMO算法加速優(yōu)化

'''
用一個對象來保存一些重要的值
'''
class optStruct:
    def __init__(self,dataMatIn,classLabels,C,toler):
        self.X = dataMatIn
        self.labelMat = classLabels
        self.C = C
        self.tol = toler
        self.m = shape(dataMatIn)[0]
        self.alphas = mat(zeros((self.m,1)))
        self.b = 0
        self.eCache = mat(zeros((self.m,2)))
'''
對于給定的alpha值 calcEk能夠計算E值并返回 真實值與預(yù)測值之間的誤差
'''
def calcEk(oS,k):
    fXk = float(multiply(oS.alphas,oS.labelMat).T*(oS.X*oS.X[k,:].T))+oS.b
    Ek = fXk - float(oS.labelMat[k])
    return Ek
'''
用于選擇第二個alpha值以保證在每次優(yōu)化中采用最大的步長
'''
def selectJ(i,oS,Ei):
    maxK = -1;maxDeltaE = 0;Ej = 0
    oS.eCache[i] = [1,Ei]
    #返回非零E值對應(yīng)的alpha值
    validEcacheList = nonzero(oS.eCache[:,0].A)[0] #.A是什么操作
    if(len(validEcacheList)) > 1:
        for k in validEcacheList:
            if k == i:continue
            Ek = calcEk(oS,k)
            deltaE = abs(Ei-Ek)
            if(deltaE > maxDeltaE):
                #選擇使得該變量最大的那個值
                maxK = k;maxDeltaE = deltaE;Ej = Ek
        return maxK,Ej
    else:
        j = selectJrand(i,oS.m)
        Ej = calcEk(oS,j)
    return j,Ej
#計算誤差值并存入緩存中
def updateEk(oS,k):
    Ek = calcEk(oS,k)
    oS.eCache[k] = [1,Ek]
def innerL(i,oS):
    Ei = calcEk(oS,i)
    if((oS.labelMat[i]*Ei < -oS.tol) and (oS.alphas[i] < oS.C)) or ((oS.labelMat[i]*Ei > oS.tol) and (oS.alphas[i]>0)):
        j,Ej = selectJ(i,oS,Ei)
        alphaIold = oS.alphas[i].copy();alphaJold = oS.alphas[j].copy()
        if(oS.labelMat[i] != oS.labelMat[j]):
            L = max(0, oS.alphas[j] - oS.alphas[i])
            H = min(oS.C, oS.C + oS.alphas[j] - oS.alphas[i])
        else:
            L = max(0,oS.alphas[j]+oS.alphas[i] - oS.C)
            H = min(oS.C,oS.alphas[j] + oS.alphas[i])
        if L == H: print("L==H");return 0
        eta = 2.0*oS.X[i,:]*oS.X[j,:].T - oS.X[i,:]*oS.X[i,:].T - oS.X[j,:]*oS.X[j,:].T
        if eta >= 0:print("eta>=0");return 0
        oS.alphas[j] -= oS.labelMat[j]*(Ei - Ej)/eta
        oS.alphas[j] = clipAlpha(oS.alphas[j],H,L)
        updateEk(oS,j)
        if(abs(oS.alphas[j]-alphaJold) < 0.00001):
            print("j not moving enough");return 0
        oS.alphas[i] += oS.labelMat[j]*oS.labelMat[i]*(alphaJold - oS.alphas[j])
        updateEk(oS,i)
        b1 = oS.b - Ei - oS.labelMat[i]*(oS.alphas[i]-alphaIold)*oS.X[i,:]*oS.X[i,:].T \
             - oS.labelMat[j]*(oS.alphas[j]-alphaJold)*oS.X[i,:]*oS.X[j,:].T
        b2 = oS.b - Ej - oS.labelMat[i]*(oS.alphas[i]-alphaIold)*oS.X[i,:]*oS.X[j,:].T \
             - oS.labelMat[j]*(oS.alphas[j]-alphaJold)*oS.X[j,:]*oS.X[j,:].T
        if(0<oS.alphas[i]) and (oS.C>oS.alphas[i]):oS.b = b1
        elif(0<oS.alphas[j]) and (oS.C>oS.alphas[j]):oS.b = b2
        else:oS.b = (b1+b2)/2.0
        return 1
    else:
        return 0
def smoP(dataMatIn, classLabels, C, toler, maxIter, kTup=('lin', 0)):  # full Platt SMO
    oS = optStruct(mat(dataMatIn), mat(classLabels).transpose(), C, toler)
    iter = 0
    entireSet = True;alphaPairsChanged = 0
    while (iter < maxIter) and ((alphaPairsChanged > 0) or (entireSet)):
        alphaPairsChanged = 0
        if entireSet:  # go over all
            for i in range(oS.m):
                alphaPairsChanged += innerL(i, oS)
                print("fullSet, iter: %d i:%d, pairs changed %d" % (iter, i, alphaPairsChanged))
            iter += 1
        else:  # go over non-bound (railed) alphas
            nonBoundIs = nonzero((oS.alphas.A > 0) * (oS.alphas.A < C))[0]
            for i in nonBoundIs:
                alphaPairsChanged += innerL(i, oS)
                print("non-bound, iter: %d i:%d, pairs changed %d" % (iter, i, alphaPairsChanged))
            iter += 1
        if entireSet:
            entireSet = False  # toggle entire set loop
        elif (alphaPairsChanged == 0):
            entireSet = True
        print("iteration number: %d" % iter)
    return oS.b, oS.alphas
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市吠架,隨后出現(xiàn)的幾起案子芙贫,更是在濱河造成了極大的恐慌,老刑警劉巖傍药,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件磺平,死亡現(xiàn)場離奇詭異,居然都是意外死亡拐辽,警方通過查閱死者的電腦和手機(jī)拣挪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來俱诸,“玉大人菠劝,你說我怎么就攤上這事∫野#” “怎么了闸英?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵锯岖,是天一觀的道長。 經(jīng)常有香客問我甫何,道長出吹,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任辙喂,我火速辦了婚禮捶牢,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘巍耗。我一直安慰自己秋麸,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布炬太。 她就那樣靜靜地躺著灸蟆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪亲族。 梳的紋絲不亂的頭發(fā)上炒考,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天,我揣著相機(jī)與錄音霎迫,去河邊找鬼。 笑死知给,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的涩赢。 我是一名探鬼主播,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼谒主,長吁一口氣:“原來是場噩夢啊……” “哼朝扼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起霎肯,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤榛斯,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后驮俗,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體懂缕,經(jīng)...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年王凑,在試婚紗的時候發(fā)現(xiàn)自己被綠了聋丝。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片工碾。...
    茶點故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖渊额,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情旬迹,我是刑警寧澤,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布奔垦,位于F島的核電站,受9級特大地震影響椿猎,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜鸵贬,卻給世界環(huán)境...
    茶點故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一阔逼、第九天 我趴在偏房一處隱蔽的房頂上張望兆衅。 院中可真熱鬧嗜浮,春花似錦、人聲如沸危融。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至瓦灶,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間贼陶,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工碉怔, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人撮胧。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓,卻偏偏與公主長得像馒闷,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子纳账,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,612評論 2 350

推薦閱讀更多精彩內(nèi)容