1. 程式人生 > >Python 深入淺出支援向量機(SVM)演算法

Python 深入淺出支援向量機(SVM)演算法

相比於邏輯迴歸,在很多情況下,SVM演算法能夠對資料計算從而產生更好的精度。而傳統的SVM只能適用於二分類操作,不過卻可以通過核技巧(核函式),使得SVM可以應用於多分類的任務中。

本篇文章只是介紹SVM的原理以及核技巧究竟是怎麼一回事,最後會介紹sklearn svm各個引數作用和一個demo實戰的內容,儘量通俗易懂。至於公式推導方面,網上關於這方面的文章太多了,這裡就不多進行展開了~

1.SVM簡介

支援向量機,能在N維平面中,找到最明顯得對資料進行分類的一個超平面!看下面這幅圖:

如上圖中,在二維平面中,有紅和藍兩類點。要對這兩類點進行分類,可以有很多種分類方法,就如同圖中多條綠線,都可以把資料分成兩部分。

但SVM做的,是找到最好的那條線(二維空間),或者說那個超平面(更高維度的空間),來對資料進行分類。這個最好的標準,就是最大間距。

至於要怎麼找到這個最大間距,要找到這個最大間距,這裡大概簡單說一下,兩個類別的資料,到超平面的距離之和,稱之為間隔。而要做的就是找到最大的間隔。

這最終就變成了一個最大化間隔的優化問題。

2.SVM的核技巧

核技巧,主要是為了解決線性SVM無法進行多分類以及SVM在某些線性不可分的情況下無法分類的情況。

比如下面這樣的資料:

這種時候就可以使用核函式,將資料轉換一下,比如這裡,我們手動定義了一個新的點,然後對所有的資料,計算和這個新的點的歐式距離,這樣我們就得到一個新的資料。而其中,離這個新點距離近的資料,就被歸為一類,否則就是另一類。這就是核函式。

這是最粗淺,也是比較直觀的介紹了。通過上面的介紹,是不是和Sigmoid有點像呢?都是通過將資料用一個函式進行轉換,最終得到結果,其實啊,Sigmoid就是一鍾核函式來著,而上面說的那種方式,是高斯核函式。

這裡補充幾點:

  • 1.上面的圖中只有一個點,實際可以有無限多個點,這就是為什麼說SVM可以將資料對映到多維空間中。計算一個點的距離就是1維,2個點就是二維,3個點就是三維等等。。。
  • 2.上面例子中的紅點是直接手動指定,實際情況中可沒辦法這樣,通常是用隨機產生,再慢慢試出最好的點。
  • 3.上面舉例這種情況屬於高斯核函式,而實際常見的核函式還有多項式核函式,Sigmoid核函式等等。

OK,以上就是關於核技巧(核函式)的初步介紹,更高階的這裡也不展開了,網上的教程已經非常多了。

接下來我們繼續介紹sklearn中SVM的應用方面內容。

3.sklearn中SVM的引數

def SVC(C=1.0, 
             kernel='rbf', 
             degree=3, 
             gamma='auto_deprecated',
             coef0=0.0, 
             shrinking=True, 
             probability=False,
             tol=1e-3, 
             cache_size=200, 
             class_weight=None,
             verbose=False, 
             max_iter=-1, 
             decision_function_shape='ovr',
             random_state=None)
 
- C:類似於Logistic regression中的正則化係數,必須為正的浮點數,預設為 1.0,這個值越小,說明正則化效果越強。換句話說,這個值越小,越訓練的模型更泛化,但也更容易欠擬合。
- kernel:核函式選擇,比較複雜,稍後介紹
- degree:多項式階數,僅在核函式選擇多項式(即“poly”)的時候才生效,int型別,預設為3。
- gamma:核函式係數,僅在核函式為高斯核,多項式核,Sigmoid核(即“rbf“,“poly“ ,“sigmoid“)時生效。float型別,預設為“auto”(即值為 1 / n_features)。
- coef0:核函式的獨立項,僅在核函式為多項式核核Sigmoid核(即“poly“ ,“sigmoid“)時生效。float型別,預設為0.0。獨立項就是常數項。
- shrinking:不斷縮小的啟發式方法可以加快優化速度。 就像在FAQ中說的那樣,它們有時會有所幫助,有時卻沒有幫助。 我認為這是執行時問題,而不是收斂問題。
- probability:是否使用概率評估,布林型別,預設為False。開啟的話會評估資料到每個分類的概率,不過這個會使用到較多的計算資源,慎用!!
- tol:停止迭代求解的閾值,單精度型別,預設為1e-3。邏輯迴歸也有這樣的一個引數,功能都是一樣的。
- cache_size:指定使用多少記憶體來執行,浮點型,預設200,單位是MB。
- class_weight:分類權重,也是和邏輯迴歸的一樣,我直接就搬當時的內容了:分類權重,可以是一個dict(字典型別),也可以是一個字串"balanced"字串。預設是None,也就是不做任何處理,而"balanced"則會去自動計算權重,分類越多的類,權重越低,反之權重越高。也可以自己輸出一個字典,比如一個 0/1 的二元分類,可以傳入{0:0.1,1:0.9},這樣 0 這個分類的權重是0.1,1這個分類的權重是0.9。這樣的目的是因為有些分類問題,樣本極端不平衡,比如網路攻擊,大部分正常流量,小部分攻擊流量,但攻擊流量非常重要,需要有效識別,這時候就可以設定權重這個引數。
- verbose:輸出詳細過程,int型別,預設為0(不輸出)。當大於等於1時,輸出訓練的詳細過程。僅當"solvers"引數設定為"liblinear"和"lbfgs"時有效。
- max_iter:最大迭代次數,int型別,預設-1(即無限制)。注意前面也有一個tol迭代限制,但這個max_iter的優先順序是比它高的,也就如果限制了這個引數,那是不會去管tol這個引數的。
- decision_function_shape:多分類的方案選擇,有“ovo”,“ovr”兩種方案,也可以選則“None”,預設是“ovr”,詳細區別見下面。
- random_state:隨時數種子。

sklearn-SVM引數,kernel特徵選擇

kernel:核函式選擇,字串型別,可選的有“linear”,“poly”,“rbf”,“sigmoid”,“precomputed”以及自定義的核函式,預設選擇是“rbf”。各個核函式介紹如下:
“linear”:線性核函式,最基礎的核函式,計算速度較快,但無法將資料從低維度演化到高維度
“poly”:多項式核函式,依靠提升維度使得原本線性不可分的資料變得線性可分
“rbf”:高斯核函式,這個可以對映到無限維度,缺點是計算量比較大
“sigmoid”:Sigmoid核函式,對,就是邏輯迴歸裡面的那個Sigmoid函式,使用Sigmoid的話,其實就類似使用一個一層的神經網路
“precomputed”:提供已經計算好的核函式矩陣,sklearn不會再去計算,這個應該不常用
“自定義核函式”:sklearn會使用提供的核函式來進行計算
說這麼多,那麼給個不大嚴謹的推薦吧
樣本多,特徵多,二分類,選擇線性核函式
樣本多,特徵多,多分類,多項式核函式
樣本不多,特徵多,二分類/多分類,高斯核函式
樣本不多,特徵不多,二分類/多分類,高斯核函式

當然,正常情況下,一般都是用交叉驗證來選擇特徵,上面所說只是一個較為粗淺的推薦。

sklearn-SVM引數,多分類方案

其實這個在邏輯迴歸裡面已經有說過了,這裡還是多說一下。

原始的SVM是基於二分類的,但有些需求肯定是需要多分類。那麼有沒有辦法讓SVM實現多分類呢?那肯定是有的,還不止一種。

實際上二元分類問題很容易推廣到多元邏輯迴歸。比如總是認為某種型別為正值,其餘為0值。

舉個例子,要分類為A,B,C三類,那麼就可以把A當作正向資料,B和C當作負向資料來處理,這樣就可以用二分類的方法解決多分類的問題,這種方法就是最常用的one-vs-rest,簡稱OvR。而且這種方法也可以方便得推廣到其他二分類模型中(當然其他演算法可能有更好的多分類辦法)。

另一種多分類的方案是Many-vs-Many(MvM),它會選擇一部分類別的樣本和另一部分類別的樣本來做二分類。

聽起來很不可思議,但其實確實是能辦到的。比如資料有A,B,C三個分類。

我們將A,B作為正向資料,C作為負向資料,訓練出一個分模型。再將A,C作為正向資料,B作為負向資料,訓練出一個分類模型。最後B,C作為正向資料,C作為負向資料,訓練出一個模型。

通過這三個模型就能實現多分類,當然這裡只是舉個例子,實際使用中有其他更好的MVM方法。限於篇幅這裡不展開了。

MVM中最常用的是One-Vs-One(OvO)。OvO是MvM的特例。即每次選擇兩類樣本來做二元邏輯迴歸。

對比下兩種多分類方法,通常情況下,Ovr比較簡單,速度也比較快,但模型精度上沒MvM那麼高。MvM則正好相反,精度高,但速度上比不過Ovr。

4.sklearn SVM實戰

我們還是使用鳶尾花資料集,不過這次只使用其中的兩種花來進行分類。首先準備資料:

import matplotlib.pyplot as plt
import numpy as np
from sklearn import svm,datasets
import pandas as pd
tem_X = iris.data[:, :2]
tem_Y = iris.target
new_data = pd.DataFrame(np.column_stack([tem_X,tem_Y]))
#過濾掉其中一種型別的花
new_data = new_data[new_data[2] != 1.0]
#生成X和Y
X = new_data[[0,1]].values
Y = new_data[[2]].values

然後用資料訓練,並生成最終圖形


# 擬合一個SVM模型
clf = svm.SVC(kernel='linear')
clf.fit(X, Y)

# 獲取分割超平面
w = clf.coef_[0]
# 斜率
a = -w[0] / w[1]
# 從-5到5,順序間隔取樣50個樣本,預設是num=50
# xx = np.linspace(-5, 5)  # , num=50)
xx = np.linspace(-2, 10)  # , num=50)
# 二維的直線方程
yy = a * xx - (clf.intercept_[0]) / w[1]
print("yy=", yy)

# plot the parallels to the separating hyperplane that pass through the support vectors
# 通過支援向量繪製分割超平面
print("support_vectors_=", clf.support_vectors_)
b = clf.support_vectors_[0]
yy_down = a * xx + (b[1] - a * b[0])
b = clf.support_vectors_[-1]
yy_up = a * xx + (b[1] - a * b[0])

# plot the line, the points, and the nearest vectors to the plane
plt.plot(xx, yy, 'k-')
plt.plot(xx, yy_down, 'k--')
plt.plot(xx, yy_up, 'k--')

plt.scatter(clf.support_vectors_[:, 0], clf.support_vectors_[:, 1], s=80, facecolors='none')


plt.scatter(X[:, 0].flat, X[:, 1].flat, c='#86c6ec', cmap=plt.cm.Paired)
# import operator
# from functools import reduce
# plt.scatter(X[:, 0].flat, X[:, 1].flat, c=reduce(operator.add, Y), cmap=plt.cm.Paired)

plt.axis('tight')
plt.show()

最終的SVM的分類結果如下: