機器學習之旅---SVM分類器

本次內容主要講解什麼是支持向量,SVM分類是如何推導的,最小序列SMO算法部分推導。

最後給出線性和非線性2分類問題的smo算法matlab實現代碼。


一、什麼是支持向量機(Support Vector Machine)

本節內容部分翻譯Opencv教程:

http://docs.opencv.org/doc/tutorials/ml/introduction_to_svm/introduction_to_svm.html#introductiontosvms

 

SVM是一個由分類超平面定義的判別分類器。也就是說給定一組帶標籤的訓練樣本,算法將會輸出一個最優超平面對新樣本(測試樣本)進行分類。

什麼樣的超平面纔是最優的?我們考慮如下場景:對於一個由二維座標點構成的線性可分集合,如何找到一條直線將兩類座標分隔開?


      上圖中,你可以看到存在多條直線將兩類座標分開。它們中有沒有最好的?我們可以直觀地定義如下規則:如果一條分割的直線離座標點太近,那它就不是最佳的。因爲它會對噪聲敏感,不能正確的推廣。因此,我們的目標是找到一條分割線,它要離所有的樣本點都儘可能的遠。

       SVM算法就是找一個超平面,並且它到離他最近的訓練樣本的距離要最大。即最優分割超平面最大化訓練樣本邊界。


原諒哥的翻譯水平,看着自己也彆扭,重新整理下:

對線性可分集,總能找到使樣本正確劃分的分界面,而且有無窮多個,哪個是最優? 必須尋找一種最優的分界準則,是兩類模式分開的間隔最大。


在介紹如何推到之前,瞭解一個定義:

分隔超平面:上述將數據集分割開來的直線叫做分隔超平面。

超平面:如果數據集是N維的,那麼就需要N-1維的某對象來對數據進行分割。該對象叫做超平面,也就是分類的決策邊界。

間隔

一個點到分割面的距離,稱爲點相對於分割面的距離。

數據集中所有的點到分割面的最小間隔的2倍,稱爲分類器或數據集的間隔。

最大間隔:SVM分類器是要找最大的數據集間隔。

支持向量:離分割超平面最近的那些點。


SVM算法其實就是最大化支持向量到分割面的距離。另外,目前上面講的都是針對線性可分的數據集。非線性的數據集,需要核函數轉換空間,才具有非線性數據處理能力。

最優分類超平面只由少數支持向量決定,問題具有稀疏性。

 

二、推導

下面講解下SVM的數學原理,其實就是複述我之前寫過的東西。

http://blog.csdn.net/jinshengtao/article/details/17636953





(這裏yi(wTx+b)稱爲點到分割面的函數間隔。yi(wTx+b)/|w|稱爲點到分割面的幾何間隔)


            現在的目標就是找出分類器定義中的w和b。爲此,我們必須找到具有最小間隔的數據點,而這些數據點也就是前面提到的支持向量。一旦找到具有最小間隔的數據點,我們就需要對該間隔最大化。這就可以寫作:


          直接求解上面的公式很難。並且到目前爲止,我們知道所有數都不那麼幹淨,不能100%線性可分。我們可以通過引入鬆弛變量,來允許有些數據點可以處於分割面的錯誤的一側。如下面幾幅圖所示:



 εi是鬆弛變量,常數C代表懲罰係數,即如果某個x是屬於某一類,但是它偏離了該類,跑到邊界上後者其他類的地方去了,C越大表明越不想放棄這個點,邊界就會縮小,錯分點更少,但是過擬合情況會比較嚴重。


爲此,我們引入拉格朗日乘子,用條件極值求解最優分界面,構造拉格朗日函數,也是之前公式(3)的對偶形式:



也就是說,只要求的拉格朗日乘子ai,我們就能得到分類邊界。


三、序列最小化方法SMO

    C-SVM是一個凸二次規劃問題,SMO算法能快速解決SVM的二次規劃問題,把它分解成一些子問題來解決。在每一步中,SMO算法僅選擇兩個拉格朗日乘子進行優化,然後再更新SVM以反應新的優化值。

   對於C-SVM目標函數和優化過程中必須遵循的約束條件如下:



     我們需要一種算法來求解最優化問題。(在以前的實現,我用了matlab提供的求條件極值的函數,http://blog.csdn.net/jinshengtao/article/details/17675653)。

現在打算自己用smo算法實現,下面就重點講下smo算法。

       C-SVM的KTT條件爲:

(KTT條件是指在滿足一些有規則的條件下, 一個非線性規劃(Nonlinear Programming)問題能有最優化解法的一個必要和充分條件.即最優解都需要滿足KTT條件)。現在這裏對於C-SVM的KTT條件爲:


其中,b*由上面的公式計算得到。

SMO算法包括兩個步驟:一是用分析的方法解一個簡單的優化問題;二是選擇待優化的拉格朗日乘子的策略。

(1)   簡單的優化問題----兩個拉格朗日乘子優化問題的解

    爲了求解只有兩個乘子的優化問題【因爲(xi,yi)都已知】,SMO方法首先計算它的約束,然後再解帶有約束的最小化問題。對於兩個乘子,其二維約束很容易表達:邊界約束使得乘子在方框內,而線性等式約束使得乘子在對角線上,這裏yi∈{1,-1},



    現在,在一條線段上求目標函數的極值,相當於一個一維的極值問題。我們可以把a1用a2表示,對a2求無條件極值,如果目標函數是嚴格凹的,最小值就一定在這一極值點(極值點在區間內)或在區間端點(極值點在區間外)。a2確定下來後,a1也確定了。因此,我們要先找到a2的優化區間,再在這個區間中對a2求最小值。

根據上面兩幅圖可以知道,a2優化區間:

當y1和y2異號時:LH的計算是由a2-a1正負未知所導致的】


當y1和y2同號時:





(b)當L的二階導數爲0,即訓練樣本中出現相同的特徵時,我們計算目標函數是單調的。最小值在線段兩個端點上的取值,我們將a2=L和a2=H分別代入目標函數,這樣拉格朗日乘子就修正到目標函數較小的端點上,比較ΨL和ΨH就可以求得Ψ的最小值。最終得到a2的值,接着一串值都解出來了。


至於偏置值b,每一步都要更新,因爲前面的KKT條件指出了ai和yiui關係,而ui和b有關,在每一步計算出ai後,根據KKT條件來調整b,分如下幾種情況


(2)   選擇策略----選取兩個拉格朗日乘子的啓發規則

事實上即使我們不採用任何找點法,只是按順序抽取ai,aj的所有組合進行優化,目標函數也會不斷下降。直到任一對ai,aj都不能繼續優化,目標函數就會收斂到極小值。我們採取某種找點方法只是爲了使算法收斂得更快。

這種試探法先選擇最有可能需要優化的a2,再針對這樣的a2選擇最有可能取得較大修正步長的a1。這樣,我們在程序中使用兩個層次的循環:

外層循環首先遍歷所有樣本查找違反KTT規則的乘子進行優化,當完成一次遍歷後外層循環再遍歷那些非邊界乘子的樣本(0<a<C),挑選那些違反KTT條件的樣本進行優化。外層循環重複執行,直到所有的非邊界樣本在一定範圍內均滿足KKT條件。

    在選定第一個拉格朗日乘子ai後,內層循環會通過最大化步長的方式來挑選第二個拉格朗日乘子,即最大化|Ei-Ej|,當Ei爲正時最小化Ej,當爲負Ei時最大化Ej.

下面給出matlab代碼實現 :

1.線性可分簡單smo

function [b,alphas] = smoSimple(data, class, C, toler, maxIter)
b = 0;
[m,n] = size(data);
alphas = zeros(m,1);
iter=0;
while (iter < maxIter)
    alphasChanges = 0;
    for k=1:1:m
        fxk = (alphas .* class)' * data * data(k,:)' + b;   % f = wx+b
        ek = fxk - class(k);
        if (((ek*class(k) < -toler) && (alphas(k) < C)) || ((ek*class(k) > toler) && (alphas(k) > 0)))
            j = selectJrand(k,m);
            fxj = (alphas .* class)' * data * data(j,:)' + b;   % f = wx+b
            ej = fxj - class(j);
            
            temp_k = alphas(k);
            temp_j = alphas(j);
            if(class(k) ~= class(j))
                L = max(0, alphas(j) - alphas(k));
                H = min(C, C + alphas(j) - alphas(k));
            else
                L = max(0, alphas(k) + alphas(j) - C);
                H = min(C, alphas(k) + alphas(j));
            end
            if L == H
                continue;
            end
            eta = 2.0 * data(k,:) * data(j,:)' - data(k,:) * data(k,:)' - data(j,:) * data(j,:)';
            if eta >= 0
                continue;
            end
            alphas(j) = alphas(j) - class(j) * (ek - ej) / eta;
            alphas(j) = clipalpha(alphas(j), H, L);
            
            if(abs(alphas(j) - temp_j) < 0.00001)
                continue;
            end
            
            alphas(k) = alphas(k) + class(k) * class(j) * (temp_j - alphas(j));
            b1 = b - ek - class(k) * (alphas(k) - temp_k) * data(k,:) * data(k,:)' - class(j) * (alphas(j) - temp_j) * data(k,:) * data(j,:)';
            b2 = b - ej - class(k) * (alphas(k) - temp_k) * data(k,:) * data(j,:)' - class(j) * (alphas(j) - temp_j) * data(j,:) * data(j,:)'; 
            
            if (alphas(k) > 0 && alphas(k) < C)
                b = b1;
            elseif(alphas(j) > 0 && alphas(j) < C)
                b = b2;
            else
                b = (b1 + b2)/2;
            end
            alphasChanges = alphasChanges + 1;
        end 
    end
    if alphasChanges == 0
        iter = iter + 1;
    else
        iter = 0;
    end
end
end

function index = selectJrand(k,m)
    index = k;
    while(index == k)
        index = randi([1,m],1,1);  
    end
end

function res = clipalpha(a, H, L)
    if a > H
        a = H;
    end
    
    if a < L
        a = L;
    end
    res = a;
end

clc;
clear;

load Data

[r,c] = size(Data);
Test = Data(:,1:2);
Label = Data(:,3);

[b, alphas] = smoSimple(Test, Label, 0.6, 0.001, 40);
 
%%畫圖
figure(1)
axis([-2 12 -8 6])
for k = 1:1:r
    hold on
    if Data(k,3) == 1
        plot(Data(k,1),Data(k,2),'r+');
    else
        plot(Data(k,1),Data(k,2),'b*');
    end
end

%畫支持向量及分割面
%result=[];
for k=1:1:r
    if alphas(k)~= 0
        hold on
        %result =[result;alphas(k)];
        QX = plot(Data(k,1:1),Data(k,2:2),'Ok','MarkerSize',12);
        set(QX,'LineWidth',2.0);
    end
end
W=(alphas.*Label)'*Data(:,1:2);
y=(-W(1).* Data(:,1:1)-b) ./W(2);
plot(Data(:,1:1),y);

結果:


上述代碼運行事件較長,此處僅是100個點的小規模數據集,對於更大的數據集收斂時間更長。


2.完整的smo算法

function [b, res_alphas] = smoP(data, class, C, toler, maxIter)
    [m,n] = size(data);
    iter = 0;
    entireSet = 1;
    alphaPairsChanged = 0;
    oS = init(data,class,C,toler,m);
        
    while(((iter<maxIter)&&(alphaPairsChanged > 0)) || (entireSet == 1))
        alphaPairsChanged = 0;
        if entireSet == 1
            for k = 1:1:m
                [ret, oS] = innerL(k, oS);
                alphaPairsChanged = alphaPairsChanged + ret;
            end
            iter = iter + 1;
        else
            nonBoundIs = [];
            for k = 1:1:m
               if ((oS.alphas(k) < C) && (oS.alphas(k) > 0))
                   nonBoundIs = [nonBoundIs k];
               end
            end
            [r,c] = size(nonBoundIs);
            for k = 1:1:c
                index = nonBoundIs(k);
                [ret, oS] = innerL(index, oS);
                alphaPairsChanged = alphaPairsChanged + ret;
            end
            iter = iter + 1;
        end
        if entireSet == 1
            entireSet = 0;
        elseif alphaPairsChanged == 0
            entireSet = 1;
        end
    end
    b = oS.b;
    res_alphas = oS.alphas;
end

function oS = init(data,class,C,toler,m)
    alphas = zeros(m,1);
    eCache = zeros(m,2);
    b = 0;
    
    oS.data = data;
    oS.class = class;
    oS.C = C;
    oS.toler = toler;
    oS.m = m;
    oS.alphas = alphas;
    oS.b = b;
    oS.eCache = eCache;
    
end

function [ret,oS] = innerL(k, oS)
    Ei = calcEk(oS, k);
    if(((oS.class(k)*Ei < -oS.toler) && (oS.alphas(k) < oS.C)) || ((oS.class(k)*Ei > oS.toler) && (oS.alphas(k) > 0)))
        [j, Ej] = selectJ(k, oS, Ei);
        temp_k = oS.alphas(k);
        temp_j = oS.alphas(j);
        
        if oS.class(k) ~= oS.class(j)
            L = max(0, oS.alphas(j) - oS.alphas(k));
            H = min(oS.C, oS.C +oS.alphas(j) - oS.alphas(k));
        else
            L = max(0, oS.alphas(j) + oS.alphas(k) - oS.C);
            H = min(oS.C, oS.alphas(j) + oS.alphas(k));
        end
        if L == H
            ret = 0;
            return;
        end
        eta = 2.0 * oS.data(k,:) * oS.data(j,:)' - oS.data(k,:) * oS.data(k,:)' - oS.data(j,:) * oS.data(j,:)';
        if eta >=0 
            ret = 0;
            return;
        end
        oS.alphas(j) = oS.alphas(j) - oS.class(j) * (Ei - Ej) / eta;
        oS.alphas(j) = clipalpha(oS.alphas(j), H, L);
        
        %update Ek
        Et = calcEk(oS, j);
        oS.eCache(j,:) = [1 Et];
        
        if(abs(oS.alphas(j) - temp_j) < 0.00001)
            ret = 0;
            return;
        end
        
        oS.alphas(k) =   oS.alphas(k) + oS.class(j)*oS.class(k)*(temp_j - oS.alphas(j));
        Et = calcEk(oS, k);
        oS.eCache(k,:) = [1 Et];
        
        b1 = oS.b - Ei - oS.class(k) * (oS.alphas(k) - temp_k) * oS.data(k,:) * oS.data(k,:)' - oS.class(j) * (oS.alphas(j) - temp_j) * oS.data(k,:) * oS.data(j,:)';
        b2 = oS.b - Ej - oS.class(k) * (oS.alphas(k) - temp_k) * oS.data(k,:) * oS.data(j,:)' - oS.class(j) * (oS.alphas(j) - temp_j) * oS.data(j,:) * oS.data(j,:)'; 
        
        if (oS.alphas(k)>0) && (oS.alphas(k)<oS.C)
            oS.b = b1;
        elseif(oS.alphas(j)>0) && (oS.alphas(j)<oS.C)
            oS.b = b2;
        else
            oS.b = (b1+b2)/2.0;
        end
        ret = 1;
        return;
    else
        ret = 0;
        return;
    end
end

function Ek = calcEk(oS, k)
    fXk = (oS.alphas .* oS.class)' * oS.data * oS.data(k,:)' + oS.b;
    Ek = fXk - oS.class(k);
end

function [j, Ej] = selectJ(k, oS, Ei)
    maxK = -1;
    maxDeltaE = 0; 
    Ej = 0;
    oS.eCache(k,:) =[1 Ei];
    validEcacheList = [];
    
    for l = 1:1:oS.m
        if oS.eCache(l,1:1) ~= 0
            validEcacheList = [validEcacheList l];
        end
    end
    [r, c] = size(validEcacheList);
    if c > 1
        for l=1:1:c
            index = validEcacheList(l)
            if index == k
                continue;
            end
            Ek = calcEk(oS,index);
            deltaE = abs(Ei - Ek);
            if(deltaE > maxDeltaE)
                maxK = index;
                maxDeltaE = deltaE;
                Ej = Ek;
            end
        end
        j = maxK;
    else
        j = selectJrand(k, oS.m);
        Ej = calcEk(oS, j);
    end
end

function index = selectJrand(k,m)
    index = k;
    while(index == k)
        index = randi([1,m],1,1);  
    end
end

function res = clipalpha(a, H, L)
    if a > H
        a = H;
    end
    
    if a < L
        a = L;
    end
    res = a;
end

clc;
clear;

load Data

[r,c] = size(Data);
Test = Data(:,1:2);
Label = Data(:,3);

[b, alphas] = smoP(Test, Label, 0.6, 0.001, 40);

%%畫圖
figure(1)
axis([-2 12 -8 6])
for k = 1:1:r
    hold on
    if Data(k,3) == 1
        plot(Data(k,1),Data(k,2),'r+');
    else
        plot(Data(k,1),Data(k,2),'b*');
    end
end

%畫支持向量及分割面
%result=[];
for k=1:1:r
    if alphas(k)~= 0
        hold on
        %result =[result;alphas(k)];
        QX = plot(Data(k,1:1),Data(k,2:2),'Ok','MarkerSize',12);
        set(QX,'LineWidth',2.0);
    end
end
W=(alphas.*Label)'*Data(:,1:2);
y=(-W(1).* Data(:,1:1)-b) ./W(2);
plot(Data(:,1:1),y);

運行結果:


與第一個代碼唯一的不同就是選擇alphas的方式。第一個代碼覆蓋了所有數據集。常數C=0.6,一方面要保證所有樣例的間隔不小於1.0,另一方面又要使得分類間隔儘可能大,並且要在 這兩方面平衡。如果C很大,那麼分類器將力圖通過分割超平面對所有的樣例都正確分類。小圓點標註的是支持向量。如果數據集非線性可分,支持向量 會在超平面附近聚集成團


四、非線性可分問題


對於上圖,在二維平面中很難用直線分割,但是這裏明顯存在着兩類數據。接下來,我們就使用一種稱爲核函數的工具將數據轉化成易於分類器理解的形式。

1.      利用核函數將數據映射到高維空間

對於上圖而言,如果只在x和y構成的座標系中插入直線進行分類的話,我們不會得到理想的結果。我們可以對數據進行轉換從而得到某些新的變量來表示數據。在這種情況下,我們就更容易得到大於零或小於零的測試結果。數學家們將數據從一個特徵空間轉換到另一特徵空間的過程稱爲特徵空間映射,通常我們將低維特徵空間映射到高維特徵空間。下面舉個例子來形象地理解核函數:


我們把橫軸上端點a和b之間紅色部分裏的所有點定爲正類,兩邊的黑色部分裏的點定爲負類。試問能找到一個線性函數把兩類正確分開麼?不能,因爲二維空間裏的線性函數就是指直線,顯然找不到符合條件的直線。但我們可以找到一條曲線,例如下面這一條


    顯然通過點在這條曲線的上方還是下方就可以判斷點所屬的類別(你在橫軸上隨便找一點,算算這一點的函數值,會發現負類的點函數值一定比0大,而正類的一定比0小)。這條曲線就是我們熟知的二次曲線。

上述過程即完成了一維空間向二維空間的映射。

對於SVM分類問題,所有的運算都可以寫成內積形式(點積),我們把內積運算替換成核函數,即可完成特徵映射。核函數主要有:

l  多項式核

l  傅立葉核

l  B樣條核

l  Sigmod核

l  高斯徑向基核

核函數並不僅僅應用於支持向量機,很多其他機器學習算法也要用到。下面就介紹高斯徑向基核函數。

徑向基函數是一種採用向量作爲自變量的函數,能夠基於向量距離輸出一個標量,具體數學公式:



     其中,σ是用戶定義的用於確定到達率或者說是函數值跌落到0的速度參數。這個高斯核函數將數據從其特徵空間映射到更高維的空間,具體說來這裏是映射到一個無窮維的空間。我們不用確切地理解數據是如何表現的。

【這裏扯一下我的同學,他的論文《基於矩陣運算的單隱層Madaline網絡批量學習》,人家提出數據往低維空間映射,比較神奇哈】

最終的分類平面:(推導參考:http://blog.csdn.net/wangran51/article/details/7354915http://blog.csdn.net/wangran51/article/details/7354915


代碼:

function [b, res_alphas] = rbf_smoP(data, class, C, toler, maxIter, k1)
    [m,n] = size(data);
    iter = 0;
    entireSet = 1;
    alphaPairsChanged = 0;
    oS = init(data, class, C, toler, m, k1);
        
    while(((iter<maxIter)&&(alphaPairsChanged > 0)) || (entireSet == 1))
        alphaPairsChanged = 0;
        if entireSet == 1
            for k = 1:1:m
                [ret, oS] = innerL(k, oS);
                alphaPairsChanged = alphaPairsChanged + ret;
            end
            iter = iter + 1;
        else
            nonBoundIs = [];
            for k = 1:1:m
               if ((oS.alphas(k) < C) && (oS.alphas(k) > 0))
                   nonBoundIs = [nonBoundIs k];
               end
            end
            [r,c] = size(nonBoundIs);
            for k = 1:1:c
                index = nonBoundIs(k);
                [ret, oS] = innerL(index, oS);
                alphaPairsChanged = alphaPairsChanged + ret;
            end
            iter = iter + 1;
        end
        if entireSet == 1
            entireSet = 0;
        elseif alphaPairsChanged == 0
            entireSet = 1;
        end
    end
    b = oS.b;
    res_alphas = oS.alphas;
end

function K = kernelTrans(X, A, k1)
    [m, n] = size(X);
    K = zeros(m,1);
    for j = 1:1:m
        deltaRow = X(j,:) - A;
        K(j) = deltaRow * deltaRow';
    end
    K = exp(K./(-2*k1));
end

function oS = init(data,class,C,toler,m,k1)
    alphas = zeros(m,1);
    eCache = zeros(m,2);
    b = 0;
    
    oS.data = data;
    oS.class = class;
    oS.C = C;
    oS.toler = toler;
    oS.m = m;
    oS.alphas = alphas;
    oS.b = b;
    oS.eCache = eCache;
    oS.K = zeros(m,m);
    for j = 1:1:m
        oS.K(:,j) = kernelTrans(oS.data,oS.data(j,:),k1);
    end
end

function [ret,oS] = innerL(k, oS)
    Ei = calcEk(oS, k);
    if(((oS.class(k)*Ei < -oS.toler) && (oS.alphas(k) < oS.C)) || ((oS.class(k)*Ei > oS.toler) && (oS.alphas(k) > 0)))
        [j, Ej] = selectJ(k, oS, Ei);
        temp_k = oS.alphas(k);
        temp_j = oS.alphas(j);
        
        if oS.class(k) ~= oS.class(j)
            L = max(0, oS.alphas(j) - oS.alphas(k));
            H = min(oS.C, oS.C +oS.alphas(j) - oS.alphas(k));
        else
            L = max(0, oS.alphas(j) + oS.alphas(k) - oS.C);
            H = min(oS.C, oS.alphas(j) + oS.alphas(k));
        end
        if L == H
            ret = 0;
            return;
        end
        eta = 2.0 * oS.K(k,j) - oS.K(k,k) - oS.K(j,j);
        if eta >=0 
            ret = 0;
            return;
        end
        oS.alphas(j) = oS.alphas(j) - oS.class(j) * (Ei - Ej) / eta;
        oS.alphas(j) = clipalpha(oS.alphas(j), H, L);
        
        %update Ek
        Et = calcEk(oS, j);
        oS.eCache(j,:) = [1 Et];
        
        if(abs(oS.alphas(j) - temp_j) < 0.00001)
            ret = 0;
            return;
        end
        
        oS.alphas(k) =   oS.alphas(k) + oS.class(j)*oS.class(k)*(temp_j - oS.alphas(j));
        Et = calcEk(oS, k);
        oS.eCache(k,:) = [1 Et];
        
        b1 = oS.b - Ei - oS.class(k) * (oS.alphas(k) - temp_k) * oS.K(k,k) - oS.class(j) * (oS.alphas(j) - temp_j) * oS.K(k,j);
        b2 = oS.b - Ej - oS.class(k) * (oS.alphas(k) - temp_k) * oS.K(k,j) - oS.class(j) * (oS.alphas(j) - temp_j) * oS.K(j,j); 
        
        if (oS.alphas(k)>0) && (oS.alphas(k)<oS.C)
            oS.b = b1;
        elseif(oS.alphas(j)>0) && (oS.alphas(j)<oS.C)
            oS.b = b2;
        else
            oS.b = (b1+b2)/2.0;
        end
        ret = 1;
        return;
    else
        ret = 0;
        return;
    end
end

function Ek = calcEk(oS, k)
    fXk = (oS.alphas .* oS.class)' * oS.K(:,k) + oS.b;
    Ek = fXk - oS.class(k);
end

function [j, Ej] = selectJ(k, oS, Ei)
    maxK = -1;
    maxDeltaE = 0; 
    Ej = 0;
    oS.eCache(k,:) =[1 Ei];
    validEcacheList = [];
    
    for l = 1:1:oS.m
        if oS.eCache(l,1:1) ~= 0
            validEcacheList = [validEcacheList l];
        end
    end
    [r, c] = size(validEcacheList);
    if c > 1
        for l=1:1:c
            index = validEcacheList(l);
            if index == k
                continue;
            end
            Ek = calcEk(oS,index);
            deltaE = abs(Ei - Ek);
            if(deltaE > maxDeltaE)
                maxK = index;
                maxDeltaE = deltaE;
                Ej = Ek;
            end
        end
        j = maxK;
    else
        j = selectJrand(k, oS.m);
        Ej = calcEk(oS, j);
    end
end

function index = selectJrand(k,m)
    index = k;
    while(index == k)
        index = randi([1,m],1,1);  
    end
end

function res = clipalpha(a, H, L)
    if a > H
        a = H;
    end
    
    if a < L
        a = L;
    end
    res = a;
end

clc;
clear;

load NData
load NTest

Data = ndata;
Data_Test = ntest;
[r,c] = size(Data);
Test = Data(:,1:2);
Label = Data(:,3);

[b, alphas] = rbf_smoP(Test, Label, 200, 0.0001, 1000,1.3);

%%畫圖
figure(1)
axis([-1.5 1.5 -1.5 1.5])
for k = 1:1:r
    hold on
    if Data(k,3) == 1
        plot(Data(k,1),Data(k,2),'r+');
    else
        plot(Data(k,1),Data(k,2),'b*');
    end
end
%%畫支持向量
support_vector = [];
lable_sv = [];
alphas_sv = [];
for k=1:1:r
    if alphas(k)~= 0
        hold on
        support_vector = [support_vector; Test(k,1:2)];
        lable_sv = [lable_sv Label(k)];
        alphas_sv = [alphas_sv alphas(k)];
        %result =[result;alphas(k)];
        QX = plot(Data(k,1:1),Data(k,2:2),'Ok','MarkerSize',12);
        set(QX,'LineWidth',2.0);
    end
end
%%預測
temp = lable_sv .* alphas_sv;
[m, n] = size(Data_Test);
errorCount = 0;
for k = 1:1:m
    value = kernelTrans(support_vector, Data_Test(k,1:2),1.3);
    predict = temp * value + b;
    if predict > 0
        predict = 1;
    else
        predict = -1;
    end
    if predict ~= Data_Test(k,3:3)
        errorCount = errorCount + 1;
    end
end
errorCount

運行結果:


支持向量圍繞超平面成團了。。。


預測結果,錯分類2,效果不錯。

代碼地址:

http://download.csdn.net/detail/jinshengtao/8134089


唉,這篇博文寫了將近一個月,斷斷續續的,自己寫到最後都不知道寫的什麼了,尤其smo推導那塊,亂七八糟,大家可以參考網上其他的優秀文章。

華爲比較辛苦,搞適配單板,bcm sdk啥的一點意思都沒有,明年打算辭職咯









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