機器學習之支持向量機

機器學習中另一種重要的算法是支持向量機,簡稱SVM。下面我們就這個算法進行介紹。

首先支持向量機的整體思路是:

 簡單情況,線性可分,把問題轉化爲一個凸優化問題,可以用拉格朗日乘子法簡化,
然後用既有的算法解決
 複雜情況,線性不可分,用映射函數將樣本投射到高維空間,使其變成線性可分的情
形。利用核函數來減少高維度計算量

這個思路的具體實現是什麼呢,首先考慮樣本集線性可分情況

簡單情況,線性可分

如上圖,如何選取最優的分離平面(決策邊界)?

就好比所有數據點都是地雷,現在要找出一條最大邊界,使得部隊能夠快速的通過。像下圖中

這兩個邊界都可以讓部隊通過,但是右邊的路線能是部隊快速的通過,這個就是支持向量機(SVM)要做的事情。而通道上緊挨着的3個數據點,在這裏叫做“支持向量”。。

好了,下面我們就要對SVM進行一步一步的算法分析,怎麼能夠得到這條最大的邊界。

上圖中我們假設最優的分離平面是w.x+b = 0

上邊界定義爲w.x+b = 1,數據點x1位於上邊界上

下邊界定義爲w.x+b = -1,數據點x2位於下邊界上。(注意,這裏w.x是點乘,內積)

而我們要求解的是兩個邊界的最大距離d

用將上下邊界上的點帶入到邊界上,並用上邊界減去下邊界可得

w.(x1-x2) = 2

而根據內積的幾個意義可知

w.(x1-x2) = 2---------------->||w||*||x1-x2||*cosθ = 2 (θ爲x1與x2向量和距離d的夾角)

所以。||w||*||x1-x2||*cosθ = 2  ---------------->||w||*d = 2由此可得   d = 2/||w||。。。。

到這裏我們的目標是優化d = 2/||w||,使得d最大。而條件是

(因爲是分類。)

而這樣的“如果”是不方便我們進行數學計算的,並且按照通常的思路,求極大值問題的時候我們往往轉換成求極小值問題。所以我們進行整理則有

到這裏,似乎有些進行不下去了。這時候需要用到-------------拉格朗日乘子法。。。。

將上圖中的式子進行轉換

其中α是拉格朗日乘子。

根據拉格朗日乘子法我們要求解L(w,b,a)的最大值

對w和b分別求導可得

將上述帶回到

 

最終呢,我們得到的一個帶約束條件的式子:

到這裏,還有一種情況,需要我們加入鬆弛變量和懲罰函數

如上圖這種情況,有部分黑色的點在白色區域內,同樣部分白色點在黑色區域內。由於這些異常點存在。我們不能完全分離。加入鬆弛變量和懲罰函數,就是使得邊界到這些異常點越近越好。所以將原來的需求變成如下形式。

其中C越大懲罰力度越大,我們找到的一個分隔區域越窄。C越小懲罰力度越小,我們找到這個分隔區域越寬。

最後經過拉格朗日乘子法可得

由此看出,跟原來沒有加懲罰函數和鬆弛變量的式子區別在於限制條件的變化。

到這裏,最後要怎麼計算這個式子,得出結果呢

就是採用SMO算法,這個算法有興趣的朋友可以自行百度瞭解一下。

主要的思路是:空出兩個乘子後,其餘乘子隨機賦值。通過上面的式子計算出這兩個沒有賦值的乘子。每次只更新兩個乘子,最後反覆迭代,知道空出的兩個乘子不再變化。

複雜情況,線性不可分

而對於線性不可分的情況。思路就是將低維不可分的映射到高維可分。

公式中紅字的地斱要使用映射後的樣本向量代替做內積

但是高維映射會導致維度災難

最初的特徵是n維的,我們將其映射到n^2維,然後再計算,這樣需要的時間從原先的O(n)變成O(n^2)
因此,我們引入核函數。

舉個例子:

在一組房價的數據集中,特徵是房子的面積x,這裏的x是實數,結果y是房子的價格。假設我們從樣本點的分佈中看到x和y符合3次曲線,那麼我們希望使用x的三次多項式來逼近這些樣本點。那麼首先需要將特徵x擴展到三維clip_image002[6],然後尋找特徵和結果之間的模型。我們將這種特徵變換稱作特徵映射(feature mapping)。映射函數稱作clip_image004[10],在這個例子中

我們將得到的特徵映射後的特徵應用於SVM分類,而不是最初的特徵。這樣,我們需要將前面clip_image008[4]公式中的內積從clip_image010[16],映射到clip_image012[42]。如果要實現該開頭的式子,只需先計算clip_image020[10],然後計算clip_image022[10]即可,然而這種計算方式是非常低效的。比如最初的特徵是n維的,我們將其映射到clip_image024[6]維,然後再計算,這樣需要clip_image026[6]的時間。

假設展開後

這個時候發現我們可以只計算原始特徵x和z內積的平方(時間複雜度是O(n)),就等價與計算映射後特徵的內積。也就是說我們不需要花clip_image026[7]時間了。

最後給出常用的核函數

有了核函數後可以將上面的拉格朗日函數簡化

 

最後我們用代碼來使用SVM

import numpy as np
import pylab as pl
from sklearn import svm

而使用的數據集是一個非常有名的用於分類問題的數據集:鳶尾花數據集。它是基於鳶尾花的花萼的長度和寬度進行分類的。我們只用其中兩維特徵,這能夠方便可視化。

svc = svm.SVC(kernel='linear')
# 鳶尾花數據集是sklearn自帶的。
from sklearn import datasets
iris = datasets.load_iris()
# 只提取前面兩列數據作爲特徵
X = iris.data[:, :2]
y = iris.target
# 基於這些數據訓練出一個支持向量分離器SVC
svc.fit(X, y)

數據結果可視化

from matplotlib.colors import ListedColormap
# 因爲鳶尾花是3分類問題,我們要對樣本和預測結果均用三種顏色區分開。
cmap_light = ListedColormap(['#FFAAAA', '#AAFFAA', '#AAAAFF'])
cmap_bold = ListedColormap(['#FF0000', '#00FF00', '#0000FF'])

def plot_estimator(estimator, X, y):
    '''
    這個函數的作用是基於分類器,對預測結果與原始標籤進行可視化。
    '''
    estimator.fit(X, y)
    # 確定網格最大最小值作爲邊界
    x_min, x_max = X[:, 0].min() - .1, X[:, 0].max() + .1
    y_min, y_max = X[:, 1].min() - .1, X[:, 1].max() + .1
    # 產生網格節點
    xx, yy = np.meshgrid(np.linspace(x_min, x_max, 100),
                         np.linspace(y_min, y_max, 100))
    # 基於分離器,對網格節點做預測
    Z = estimator.predict(np.c_[xx.ravel(), yy.ravel()])

    # 對預測結果上色
    Z = Z.reshape(xx.shape)
    pl.figure()
    pl.pcolormesh(xx, yy, Z, cmap=cmap_light)

    # 同時對原始訓練樣本上色
    pl.scatter(X[:, 0], X[:, 1], c=y, cmap=cmap_bold)
    pl.axis('tight')
    pl.axis('off')
    pl.tight_layout()
plot_estimator(svc, X, y)

 

而加入懲罰函數和鬆弛變量

svc = svm.SVC(kernel='linear', C=1e3)
plot_estimator(svc, X, y)
pl.scatter(svc.support_vectors_[:, 0], svc.support_vectors_[:, 1], s=80, facecolors='none', zorder=10)
pl.title('High C values: small number of support vectors')

svc = svm.SVC(kernel='linear', C=1e-3)
plot_estimator(svc, X, y)
pl.scatter(svc.support_vectors_[:, 0], svc.support_vectors_[:, 1], s=80, facecolors='none', zorder=10)
pl.title('Low C values: high number of support vectors')

在試試利用核函數

採用核方法,能夠很方便地產生非線性分類邊界。

  • linear,線性核,會產生線性分類邊界。一般來說它的計算效率最高,而且需要數據最少。
  • poly ,多項式核,會產生多項式分類邊界。
  • rbf,徑向基函數,也就是高斯核,是根據與每一個支持向量的距離來決定分類邊界的。它的映射到無線維的。它是最靈活的方法,但是也需要最多的數據。
svc = svm.SVC(kernel='linear')
plot_estimator(svc, X, y)
pl.scatter(svc.support_vectors_[:, 0], svc.support_vectors_[:, 1], s=80, facecolors='none', zorder=10)
pl.title('Linear kernel')

svc = svm.SVC(kernel='poly', degree=4)
plot_estimator(svc, X, y)
pl.scatter(svc.support_vectors_[:, 0], svc.support_vectors_[:, 1], s=80, facecolors='none', zorder=10)
pl.title('Polynomial kernel')

svc = svm.SVC(kernel='rbf', gamma=1e2)
plot_estimator(svc, X, y)
pl.scatter(svc.support_vectors_[:, 0], svc.support_vectors_[:, 1], s=80, facecolors='none', zorder=10)
pl.title('RBF kernel')

我們可以看到,高斯核更靈活,而且對於訓練數據效果是最好的。但是要擔心過擬合。

最後,支持向量機SVM是一個在機器學習中常見的算法,雖然正逐漸被神經網絡慢慢替代,但是其算法的思想是我們要掌握的。

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