1 支持向量機(SVM)的基本概念
SVM是一種分類算法,其側重於模式識別方面。使用SVM可以大大提高分類的准確性。
分類相當於模式識別的子集合,模式識別重點在於對已知數據進行特征發現與提取。
SVM重點在於解決線性可分的問題。但很多時候,實際的問題是線性不可分的。SVM的思想就是將線性不可分的問題轉化線性可分的問題。那么如何來是實現呢?就是將空間映射到多維空間。如把二維空間映射到三維空間。以增加維數來減少方程的次數。
比如,在二維空間中,不得不用 f(x)=ax2+b+c 這個曲線將兩類樣本分開,而不是用一條直線將它們分開,這樣就是一個線性不可分的問題。為了解決這個問題,我們可以使用換元法將即 x^2=y1 , x=y2 , 這樣一來替換為 z=a'y1+b'y2+c' 這個線性方程,從而轉換到了高維空間,代價是維度的增加引入了更多的變量。這樣我們就完成了從線性不可分問題到線性可分問題的轉換。
所謂支持向量,即在每一類樣本集中,過離separating hyper plane 最近的樣本做一個與separating hyper plane 平行的線/超平面。有幾個分類,就有幾個這種直線/超平面,這樣的直線/超平面就叫做支持向量(Support Vector)。

2 核函數
那么找到一個線/超平面來完成二分類成為問題的關鍵。這個線/超平面的函數被我們成為“核函數”。常見的核函數有:
- 線性核函數————linear
- 多項式核函數————poly
- 徑向基核函數————rbf(用得較多)
- Sigmoid核函數————sigmoid
那么如何來選擇合適的核函數呢?答案就是比較不同核函數的分類的准確率。在實際處理分類問題時,分別計算幾種核函數的分類性能,將准確率最高的核函數作為最終用於預測分類的核函數即可。在本文的例子中,我們使用畫圖的方式來判斷分類性能的好壞。
使用模型不同,轉換的方式不同,在多維空間中的圖形就不同,分類的效果就不同。
網上關於SVM、線性可不可分、核函數的博文很多,如http://www.cnblogs.com/LeftNotEasy/archive/2011/05/02/basic-of-svm.html 。這里就不再贅述。
3 比較分類效果與子圖的划分
為了比較不同分類器的准確性,我們采用繪制散點圖,然后人工觀察來判斷。我們將兩個類的點在坐標系中用不同顏色表示出來,同時標出訓練數據,看預測點與訓練點的在圖中的重疊情況。重疊越緊密,說明分類的效果越准確。
在繪圖的時候,我們希望在一個平面中同時展現多幅圖片,這時使用matplotlib模塊的子圖(subplot)來展現。
subplot的參數
subplot(橫向划分個數,縱向划分個數,當前定位)
第一個參數代表的是橫向要划分的子圖個數,第二個參數代表的是縱向要划分的子圖的個數,第三個參數表示當前的定位。
使用示例一:

使用示例二:

使用示例三:

4 源碼
應用場景
假設在第一象限有10個點,它們分別是[0, 0], [1, 1], ... , [9, 9]。它們[0,0],[1,1],[2,2],[3,3]屬於“0”這個類(類標簽為0),另外6個點屬於“1”這個類(類標簽為1)。
現在第一象限構造900*900個點,縱橫坐標方向上相鄰的點距離為0.01。
[ 0.00, 0.00],[ 0.01, 0.00], [ 0.02, 0.00],..., [ 8.97, 8.99], [ 8.98, 8.99],[ 8.99, 8.99]
這樣,布滿區間的點作為預測數據,用各種分類模型進行二分類。最后通過繪圖模塊給不同類的點上色以判斷分類性能。
import numpy as npy
import matplotlib.pyplot as plt
from sklearn import svm
x=[]#存儲樣本數據
y=[]#存儲類標號
for i in range(0,10):#構造10個點作為訓練數據
if(i<=3):#if(i<=3 or i>=8):
x.append([i,i])
y.append(0)
else:
x.append([i,i])
y.append(1)
train_x=npy.array(x)#轉換為數組
train_y=npy.array(y)
'''
創建svm分類器的格式:
svm.SVC(kernel=某個核函數).fit(訓練樣本,類標簽)
'''
#linear
linear_svc=svm.SVC(kernel="linear").fit(train_x,train_y)
#poly 要定義維度,degree決定了多項式的最高次冪.關於SVC參數的意義請參見文章后頭的內容。
poly_svc=svm.SVC(kernel="poly",degree=4).fit(train_x,train_y)
#徑向基核函數(這時SVC默認的核函數)
rbf_svc=svm.SVC().fit(train_x,y)
#Sigmoid
sigmoid_svc=svm.SVC(kernel="sigmoid").fit(train_x,train_y)
#下面就可以進行預測了
x1,x2=npy.meshgrid(npy.arange(train_x[:,0].min(),train_x[:,0].max(),0.01),npy.arange(train_x[:,1].min(),train_x[:,1].max(),0.01))
#先生成各個點。定義最小值和最大值后,定義隔多少值建立一個點。
#npy.arange(train_x[:,1].min(),train_x[:,1].max(),0.01))返回的是900個元素的數組
#meshgrid函數用來產生矩陣。上面的語句也是就是numpy.meshgrid(numpy.arange(0,9,0.01),numpy.arange(0,9,0.01))
'''
x1是矩陣
[[ 0. , 0.01, 0.02, ..., 8.97, 8.98, 8.99],
[ 0. , 0.01, 0.02, ..., 8.97, 8.98, 8.99],
[ 0. , 0.01, 0.02, ..., 8.97, 8.98, 8.99],
...,
[ 0. , 0.01, 0.02, ..., 8.97, 8.98, 8.99],
[ 0. , 0.01, 0.02, ..., 8.97, 8.98, 8.99],
[ 0. , 0.01, 0.02, ..., 8.97, 8.98, 8.99]]
x2是矩陣
[[ 0. , 0. , 0. , ..., 0. , 0. , 0. ],
[ 0.01, 0.01, 0.01, ..., 0.01, 0.01, 0.01],
[ 0.02, 0.02, 0.02, ..., 0.02, 0.02, 0.02],
...,
[ 8.97, 8.97, 8.97, ..., 8.97, 8.97, 8.97],
[ 8.98, 8.98, 8.98, ..., 8.98, 8.98, 8.98],
[ 8.99, 8.99, 8.99, ..., 8.99, 8.99, 8.99]]
'''
splocation=1
for i in [linear_svc,poly_svc,rbf_svc,sigmoid_svc]:#遍歷各個模型以便繪圖,以看哪個核函數的准確率更高
rst = i.predict(npy.c_[x1.ravel(),x2.ravel()])#橫坐標和縱坐標的組合。x1.ravel()和x2.ravel()都是長度為810000的數組。c_[]用來將前后兩個數組串聯成一個810000行、2列的矩陣。
#因為上面用到了四種分類模型,那么一個2×2的圖就能夠顯示完全了。
plt.subplot(2,2,splocation)#第一個參數代表的是橫向要划分的子圖個數,第二個參數代表的是縱向要划分的子圖的個數,第三個參數表示當前的定位
plt.contourf(x1,x2,rst.reshape(x1.shape))#contourf用來填充顏色。(當前橫坐標,當前縱坐標,預測的分類結果(轉為x1的規模維數))
#訓練數據的點也繪制出來
for j in range(0,len(y)):
if(int(y[j])==0):
plt.plot(train_x[j:j+1,0],train_x[j:j+1],"yo")#y代表黃色,o代表散點圖
else:
plt.plot(train_x[j:j+1,0],train_x[j:j+1],"ko")#類別為1填充為黑色。k代表黑色
splocation+=1
plt.show()
運行結果:

可見,在比較這種分布下,徑向基核函數表現出更好的分類性能。
5 SVC的參數
具體參數的意義與使用請參見鏈接:http://scikit-learn.org/stable/modules/generated/sklearn.svm.SVC.html
- C:目標函數的懲罰系數C,用來平衡分類間隔margin和錯分樣本的,default C = 1.0;
C越大,相當於懲罰松弛變量,希望松弛變量接近0,即對誤分類的懲罰增大,趨向於對訓練集全分對的情況,這樣對訓練集測試時准確率很高,但泛化能力弱。C值小,對誤分類的懲罰減小,允許容錯,將他們當成噪聲點,泛化能力較強。
-
kernel :核函數,默認是rbf,可以是‘linear’, ‘poly’, ‘rbf’, ‘sigmoid’, ‘precomputed’
-
degree :多項式poly函數的維度,默認是3,選擇其他核函數時會被忽略。
-
gamma : ‘rbf’,‘poly’ 和‘sigmoid’的核函數參數。默認是’auto’,則gamma=1/n_features
-
coef0 :核函數的常數項。對於‘poly’和 ‘sigmoid’有用。
-
probability :是否采用概率估計。默認為False。要采用的話必須先於調用fit,這個過程會增加用時。
-
shrinking :是否采用shrinking heuristic方法,默認為true
-
tol :停止訓練的誤差值大小,默認為1e-3
-
cache_size :核函數cache緩存大小,默認為200
-
class_weight :類別的權重,字典形式傳遞。設置第幾類的參數C為weight*C(C-SVC中的C)
-
verbose :允許冗余輸出。跟多線程有關系。默認為False。
-
max_iter :最大迭代次數。-1為無限制。
-
decision_function_shape :是否返回模型中每一個類別的樣本的ovr決策函數,或者ovo決策函數。 默認為None
-
random_state :數據洗牌時的種子值,int值
主要調節的參數有:C、kernel、degree、gamma、coef0。
6 推廣
官方文檔是學習的絕佳利器。傳送門:http://scikit-learn.org/dev/modules/svm.html
6.1 多分類
SVM算法最初是為二值分類問題設計的,當處理多類問題時,就需要構造合適的多類分類器。目前,構造SVM多類分類器的方法主要有兩類:一類是直接法,直接在目標函數上進行修改,將多個分類面的參數求解合並到一個最優化問題中,通過求解該最優化問題“一次性”實現多類分類。這種方法看似簡單,但其計算復雜度比較高,實現起來比較困難,只適合用於小型問題中;另一類是間接法,主要是通過組合多個二分類器來實現多分類器的構造,常見的方法有one-against-one和one-against-all兩種。
- 一對多法(one-versus-rest,簡稱1-v-r SVMs)。訓練時依次把某個類別的樣本歸為一類,其他剩余的樣本歸為另一類,這樣k個類別的樣本就構造出了k個SVM。分類時將未知樣本分類為具有最大分類函數值的那類。
- 一對一法(one-versus-one,簡稱1-v-1 SVMs)。其做法是在任意兩類樣本之間設計一個SVM,因此k個類別的樣本就需要設計k(k-1)/2個SVM。當對一個未知樣本進行分類時,最后得票最多的類別即為該未知樣本的類別。Libsvm中的多類分類就是根據這個方法實現的。
- 層次支持向量機(H-SVMs)。層次分類法首先將所有類別分成兩個子類,再將子類進一步划分成兩個次級子類,如此循環,直到得到一個單獨的類別為止。 對c和d兩種方法的詳細說明可以參考論文《支持向量機在多類分類問題中的推廣》(計算機工程與應用。2004)
- 其他多類分類方法。除了以上幾種方法外,還有有向無環圖SVM(Directed Acyclic Graph SVMs,簡稱DAG-SVMs)和對類別進行二進制編碼的糾錯編碼SVMs。
6.2 回歸
支持分類的支持向量機可以推廣到解決回歸問題,這種方法稱為支持向量回歸。
支持向量分類所產生的模型僅僅依賴於訓練數據的一個子集,因為構建模型的成本函數不關心在超出邊界范圍的點,類似的,通過支持向量回歸產生的模型依賴於訓練數據的一個子集,因為構建模型的函數忽略了靠近預測模型的數據集。
有三種不同的實現方式:支持向量回歸SVR,nusvr和linearsvr。linearsvr提供了比SVR更快實施但只考慮線性核函數,而nusvr實現比SVR和linearsvr略有不同。
作為分類類別,訓練函數將X,y作為向量,在這種情況下y是浮點數