支持向量機原理


支持向量機概念

線性分類器

首先介紹一下線性分類器的概念,C1和C2是要區分的兩個類別,在二維平面中它們的樣本如上圖所示。中間的直線就是一個分類函數,它可以將兩類樣本完全分開。一般的,如果一個線性函數能夠將樣本完全正確的分開,就稱這些數據是線性可分的,否則稱為非線性可分的

線性函數是關於自變量的一次函數,在一維空間里就是一個點,在二維空間里就是一條直線,三維空間里就是一個平面,如果不關注空間的維數,線性函數還有一個統一的名稱——超平面(Hyper Plane),記作:

g(x) = wx + b

定義分類函數f(x),若點(x,y)在分類平面上方,即y > wx+b 則記f(x)=1 ; 若y < wx + b 記 f(x) = -1。(不分為0-1是因為數學處理方便的需要)

一般而言,一個點距離超平面的遠近可以表示分類的可信程度。以樣本點距分類平面間隔最大為目標的的線性分類器稱作最大間隔分類器(Maxmium Margin Classifier)

支持向量機(Support Vector Machine, SVM)

如圖,在最優分類平面上下作兩個平行等間距的超平面AB,使得超平面A或B過樣本點且AB之間沒有樣本點。

可以直觀的看出在A和B上的樣本"支撐"起了AB的間隔,也就是分類的可信程度。因為樣本點由向量表示,把這些AB上樣本點對應的向量稱作支持向量(Support Vector)

引出樣本(x,y) 到分類間隔距離的函數間隔(functional margin):

在此基礎上定義幾何間隔(geometrical margin):

其中,|| w|| 代表P范數,不關心具體的P值。幾何間隔實際上是歸一化之后的點到分類平面的距離。

這樣處理的優勢在於對於坐標系的等比例放縮會使函數間隔變化而不會使幾何間隔變化。

定義樣本組中所有樣本點到分類平面幾何間隔中的最小值為樣本組到平面的間隔:

為使分類的把握最大,定義目標函數:

通過求解這個優化問題,可以找到最優的分類平面。

** 凸集 ** 是指一個點的集合,其中任取兩個點連一條直線,這條線上的點仍然在這個集合內部。因為求解最優分類平面的優化問題可行域為一個凸集,所以這個問題為一個** 凸優化問題 **。

不等式約束下的拉格朗日乘數法

先提一下拉格朗日乘數法:拉格朗日乘數法是在在φ(x,y,...)=0的條件下求f(x,y,...)極值的數學方法。

構造函數:

其中,λ為常數稱為拉格朗日乘子。方程組:

的解是f(x,y,...)有極值的必要條件。

下面將拉格朗日乘數法由等式約束推廣到不等式約束:

為了便於求解,通過等比例放縮將函數間隔變為1,那么原目標函數變為:

(1)

為保證訓練數據分類正確且支持向量函數間隔為1,引入約束條件:

(2)

原優化問題變為(2)約束下求(1)的最優解問題。構造函數:

令:

此目標函數表示通過調整a使得F有極大值。

在條件(2)不滿足時,一定可以通過令a為正無窮使得C(w)達到正無窮。

這樣原優化問題轉化為:

放松約束

訓練數據集中少量的樣本點可能使得線性可分問題變為線性不可分問題, 很可能導致問題解決難度急劇增大。這些異常的樣本點可能很是噪聲,為此放松模型的約束從而使得模型允許少數異常樣本的存在。

原模型:

引入松弛變量和懲罰函數,改進模型:

直觀地說,松弛變量表示允許異常樣本點偏離的距離,懲罰因子(cost)表示所能容忍的異常樣本點造成的損失。

SMO優化算法

SMO(Sequential Minimal Optimization)優化算法是求解SVM的重要工具

因為存在約束:

使得當其它變量固定時也被固定,所以一次選取兩個參數進行優化。

SMO在每次循環中選擇兩個a進行優化,SMO算法的流程大致如下:

創建一個a向量並初始化為0向量
進行指定次數的迭代 {
	令a[i]遍歷數據集中的向量 {
		使用KKT條件判斷a[i]是否可以優化 {
		隨機選擇另外一個向量a[j]
			同時優化兩個向量(a[i]+a[j]不變)
			若a[i],a[j]均不能被優化則跳出內循環
		}
	}
若所有變量都沒有優化則增加迭代次數

這個算法...相...當...難...實...現,這里引用一下* Machine Learning in Action * 中的示例:

隨機選取a[j]以及保證a取值范圍的工具函數:

def RandJ(i,m):
    j=i 
    while (j==i):
        j = int(random.uniform(0,m))
    return j

def adjustAlpha(alpha, upper, lower):
    if alpha > upper: 
        alpha = upper
    if lower > alpha:
        alpha = lower
    return alpha

簡單SMO實現:

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):
            fXi = float(multiply(alphas,labelMat).T*(dataMatrix*dataMatrix[i,:].T)) + b
            Ei = fXi - float(labelMat[i])
			#if checks if an example violates KKT conditions
            if ((labelMat[i]*Ei < -toler) and (alphas[i] < C)) or ((labelMat[i]*Ei > toler) and (alphas[i] > 0)):
				# random select a[j]
                j = RandJ(i,m)
                fXj = float(multiply(alphas,labelMat).T*(dataMatrix*dataMatrix[j,:].T)) + b
                Ej = fXj - float(labelMat[j])
				# maintain a between 0 and C
                alphaIold = alphas[i].copy(); 
				alphaJold = alphas[j].copy();
                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
                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] = adjustAlpha(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	

函數的5個輸入分別為:數據集,標簽,邊界C,容錯和最大迭代次數。

上述實現采用隨機方法選擇優化對象,SMO算法可以使用啟發式算法進行選擇對象以提升效率。

因為實現過於復雜(其實是草民也不懂),故不再說明,詳見* 機器學習實戰 *。

核函數與多核學習算法

通過升高維度解決線性不可分問題

例如在一維空間上的一段線段,我們無法在一維向量空間中找到分類平面將它與其它部分分開,那么稱這類問題為線性不可分問題。

在二維空間中的二次曲線可以解決這個問題,但它不是線性分類函數。因此,定義向量:

並定義關於X 的線性函數y = w*x + b 作為分類函數。由此,一維空間的線性不可分問題轉化為二維空間上的線性可分問題。

核函數

為便於描述,將x映射到向量X的映射稱為H(x)。

通過各種變換得到的線性分類器:

由上式可知,對於新的樣本點只需計算它與訓練樣本點(只需計算支持向量)的內積 即可。

將非線性問題映射到高維的方法會極大增大計算量,即** 維度災難 **。

核函數是在低維下(通過x)計算高緯向量(由映射H得到的X)內積的方法。即核函數K(X,Y)滿足:

核函數不是唯一的,只要對於給定的H(x) 滿足上述定義式即可。Mercer定理指出核函數適應與某一映射H(x) 的條件。

示例:

令核函數:

則有:

常用的核函數有:

  • 多項式核函數

  • 高斯核函數(徑向基核函數,Radial Basis Function,RBF)

通過調控參數σ,高斯核函數具有相當的靈活性,是使用最廣泛的核函數之一。

核函數不是SVM的特有的,兩者是相互正交的概念。核函數作為一種數學工具有着廣泛的應用。

Mercer定理

對於m個訓練樣本,兩兩由核函數K求內積可得到m階方陣,稱為核矩陣(Kernel Matrix)。

因為:

所以方陣為對稱方陣。 對於任意向量Z 有:

可知核矩陣K 為半正定矩陣。由Mercer定理可知,只要核矩陣為對稱半正定矩陣那么對應的核函數是有效的。

多核學習算法(MKL)

在實際應用中特征向量可能是異構的,與其對應的最佳核函數未必相同。傳統的單核SVM需要根據經驗或實驗來確定核函數。

多核學習算法(Multiple Kernel Learning,MKL)本質上是定義M個基核函數(Base Kernel Function),並使用基函數的加權線性組合作為作為SVM的核函數。

通過優化算法自動學習權重,避免了人工選擇核函數的弊端。

多核線性組合最經典的是simpleMKL,也常被作為MKL的具體實現,應用在了計算機各領域。

為了使MKL應用地更廣后來又提出了GMKL(G即Generalized),最優化方法采用是PGD(Projected Gradient Descend) 。

為了改進收斂效果,Vishwanathan又提出SPG-GMKL(Spectral Projected Gradient),同時提出了多核的product組合。SPG-GMKL也被后來者視作state-of-art。MKL的經典實現有SimpleMKL,Shogun,SPG-GMKL,SMO-MKL。

SVM工具

經歷手寫SVM的慘烈教訓(還是太年輕)之后,我決定使用工具箱/第三方庫

Matlab

Matlab豐富的工具箱提供了各種方便。

Statistic Tools工具箱提供了svmtrain和svmclassify函數進行SVM分類。

traindata = [0 1; -1 0; 2 2; 3 3; -2 -1;-4.5 -4; 2 -1; -1 -3];
group = [1 1 -1 -1 1 1 -1 -1]';
testdata = [5 2;3 1;-4 -3];
svm_struct = svmtrain(traindata,group);    
Group = svmclassify(svm_struct,testdata);

svmtrain接受traindata和group兩個參數,traindata以一行表示一個樣本,group是與traindata中樣本對應的分類結果,用1和-1表示。

svmtrain返回一個存儲了訓練好的svm所需的參數的結構體svm_struct。

svmclassify接受svm_struct和以一行表示一個樣本的testdata,並以1和-1列向量的形式返回分類結果。


免責聲明!

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



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