統計學習方法c++實現之六 支持向量機(SVM)及SMO算法


前言

支持向量機(SVM)是一種很重要的機器學習分類算法,本身是一種線性分類算法,但是由於加入了核技巧,使得SVM也可以進行非線性數據的分類;SVM本來是一種二分類分類器,但是可以擴展到多分類,本篇不會進行對其推導一步一步羅列公式,因為當你真正照着書籍進行推導后你就會發現他其實沒那么難,主要是動手。本篇主要集中與實現,即使用著名的序列最小最優化(SMO)算法進行求解,本篇實現的代碼主要參考了Platt J. Sequential minimal optimization: A fast algorithm for training support vector machines[J]. 1998.這是SMO的論文,論文中詳細解釋了如何使用SMO算法,還有偽代碼,我的C++程序就是根據偽代碼實現的(沒錯,SMO算法我推不出來)。代碼地址.

對於SVM的一些理解

首先,《統計學習方法》中對於SVM的講解已經很好了,請務必跟着一步一步推導,這樣你就會發現整個SVM的推導過程無非就是以下幾步:

  1. 將分類器建模成n維空間中的超平面,但是這個超平面有個很重要的選取原則,那就是讓所有樣本點到超平面的距離都盡量大,也就是讓距離超平面最近的點到超平面的距離達到最大,於是得出了約束最大化問題。
  2. 將問題簡化到只和決定超平面的參數w有關,使用熟悉的拉格朗日數乘法將約束最優化問題變成一個式子,然后轉化為對偶問題。
  3. 求解對偶問題的最優解a,然后根據原問題和對偶問題的關系由a求出w,此時就得到SVM的參數了。

1,2步都是需要推導的,唯獨涉及實現的地方是第3步,我們實現的重點變成了如何快速的得到最優解a(a可是有N個分量的,N為訓練樣本數)。於是SMO算法就出現了。

再者關於核函數,之前看博客,有人理解成核函數是一種映射,即將非線性問題映射為線性問題,有人評論說這是不嚴謹的,核函數不是一種映射,當時很迷茫,但是現在看來,核函數是一種技巧,他讓我們可以使用目前空間的內積來代表某個目標空間的內積

具體的關於SVM的推導和核技巧的理解查看書籍就可以,自己推導一遍就都明白了

序列最小最優化(SMO)算法

《統計學習方法》上對於SMO算法的講解很清楚(跟原論文思路一樣),就是將待優化的n個參數選兩個作為優化對象,其他的固定,然后轉化為二元最優化問題。道理我都懂,但是實現的時候遇到了很多麻煩,關鍵在於啟發式的變量選取,於是便找到原論文,沒想到Platt大神已經把偽代碼寫好了,於是,我就把他的偽代碼用c++實現了一遍。這部分我對一些推導還是不明白,在這里就不獻丑了,看代碼吧,首先給出論文中的偽代碼截圖。
在這里插入圖片描述
在這里插入圖片描述

代碼結構

在這里插入圖片描述

c++實現

這部分主要列出偽代碼的takestep部分各變量的更新代碼。

int SVM::SMOTakeStep(int& i1, int& i2) {
    //變量名跟偽代碼中基本一樣,這里用i1, i2代表數據點對應的的拉格朗日乘子,E每個樣本點的預測輸出與真值的誤差
    //存儲在vector中,避免重復計算
    ...
    ...
    ...
    double a1 = alpha[i1] + s * (alpha[i2] - a2);
    double b1;
    //please notice that the update equation is from <<統計學習方法>>p130, not the equation in paper
    b1= -E[i1] - y1 * (a1 - alpha[i1]) * kernel(trainDataF[i1], trainDataF[i1]) -
                y2 * (a2 - alpha[i2]) * kernel(trainDataF[i1], trainDataF[i2]) + b;
    double b2;
    b2 = -E[i2] - y1 * (a1 - alpha[i1]) * kernel(trainDataF[i1], trainDataF[i2]) -
                y2 * (a2 - alpha[i2]) * kernel(trainDataF[i2], trainDataF[i2]) + b;
    double bNew = (b1 + b2) / 2;
    b = bNew;
    w = w + y1 * (a1 - alpha[i1]) * trainDataF[i1] + y2 * (a2 - alpha[i2]) *
                                                     trainDataF[i2];
    //this is the linear SVM case, this equation are from the paper equation 22
    alpha[i1] = a1;
    alpha[i2] = a2;
//    vector<double> wtmp (indim);
//    for (int i=0; i<trainDataF.size();++i)
//    {
//        auto tmp = alpha[i]*trainDataF[i]*trainDataGT[i];
//        wtmp = wtmp+tmp;
//    }
//    w = wtmp;
    E[i1] = computeE(i1);
    E[i2] = computeE(i2);
    return 1;

特別注意b的計算公式,這里被坑了好久,原論文的計算b1 ,b2的公式全是正號,因為論文中svm的超平面公式是\(wx-b\),這與書上的公式不同,所以導致我的算法一直不收斂,最后從頭看論文才發現...

其他的實現都在這里,如果有問題歡迎交流。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM