天天看點

支援向量機求解過程的可視化及其核函數kernel 【Tsai Tsai】

目錄

1.支援向量機分類器是如何工作的?

2. sklearn.svm.SVC

2.1 線性SVM決策過程的可視化

 2.2 參數說明

2.3 非線性資料集上的推廣與3D可視化

3. SVC的重要參數kernel

3.1 在sklearn中實作SVC的基本流程:

 3.2 kernel

3.3 月亮型、環型、簇型、分類型在4個核函數不同的表現

1.支援向量機分類器是如何工作的?

        支援向量機所作的事情其實非常容易了解。先來看看下面這一組資料的分布,這是一組兩種标簽的資料,兩種标簽分别由圓和方塊代表。支援向量機的分類方法,是在這組分布中找出一個超平面作為決策邊界,使模型在資料上的分類誤差盡量接近于小,尤其是在未知資料集上的分類誤差(泛化誤差)盡量小。

支援向量機求解過程的可視化及其核函數kernel 【Tsai Tsai】

超平面

         在幾何中,超平面是一個空間的子空間,它是次元比所在空間小一維的空間。 如果資料空間本身是三維的,則其超平面是二維平面,而如果資料空間本身是二維的,則其超平面是一維的直線。

        在二分類問題中,如果一個超平面能夠将資料劃分為兩個集合,其中每個集合中包含單獨的一個類别,我們就說這個超平面是資料的“決策邊界”。

         決策邊界一側的所有點在分類為屬于一個類,而另一側的所有點分類屬于另一個類。如果我們能夠找出決策邊界,分類問題就可以變成探讨每個樣本對于決策邊界而言的相對位置。比如上面的資料分布,我們很容易就可以在方塊和圓的中間畫出一條線,并讓所有落在直線左邊的樣本被分類為方塊,在直線右邊的樣本被分類為圓。如果把資料當作我們的訓練集,隻要直線的一邊隻有一種類型的資料,就沒有分類錯誤,我們的訓練誤差就會為0。但是,對于一個資料集來說,讓訓練誤差為0的決策邊界可以有無數條。

支援向量機求解過程的可視化及其核函數kernel 【Tsai Tsai】

但在此基礎上,我們無法保證這條決策邊界在未知資料集(測試集)上的表現也會優秀。對于現有的資料集來說,我們有

支援向量機求解過程的可視化及其核函數kernel 【Tsai Tsai】

支援向量機求解過程的可視化及其核函數kernel 【Tsai Tsai】

兩條可能的決策邊界。我們可以把決策邊界

支援向量機求解過程的可視化及其核函數kernel 【Tsai Tsai】

向兩邊平移,直到碰到離這條決策邊界最近的方塊和圓圈後停下,形成兩個新的超平面,分别是

支援向量機求解過程的可視化及其核函數kernel 【Tsai Tsai】

支援向量機求解過程的可視化及其核函數kernel 【Tsai Tsai】

,并且我們将原始的決策邊界移動到

支援向量機求解過程的可視化及其核函數kernel 【Tsai Tsai】

支援向量機求解過程的可視化及其核函數kernel 【Tsai Tsai】

的中間,確定

支援向量機求解過程的可視化及其核函數kernel 【Tsai Tsai】

支援向量機求解過程的可視化及其核函數kernel 【Tsai Tsai】

支援向量機求解過程的可視化及其核函數kernel 【Tsai Tsai】

的距離相等。在

支援向量機求解過程的可視化及其核函數kernel 【Tsai Tsai】

支援向量機求解過程的可視化及其核函數kernel 【Tsai Tsai】

中間的距離,叫做 這條決策邊界的邊際 (margin),通常記作

支援向量機求解過程的可視化及其核函數kernel 【Tsai Tsai】

。對

支援向量機求解過程的可視化及其核函數kernel 【Tsai Tsai】

也執行同樣的操作,然後我們來對比一下兩個決策邊界。現在兩條決策邊界右邊的資料都被判斷為圓,左邊的資料都被判斷為方塊,兩條決策邊界在現在的資料集上的訓練誤差都是0 ,沒有一個樣本被分錯。

支援向量機求解過程的可視化及其核函數kernel 【Tsai Tsai】

我們引入和原本的資料集相同分布的測試樣本(紅色所示),平面中的樣本變多了,此時我們可以發現,對于

支援向量機求解過程的可視化及其核函數kernel 【Tsai Tsai】

而言,依然沒有一個樣本被分錯,這條決策邊界上的泛化誤差也是0。但是對于

支援向量機求解過程的可視化及其核函數kernel 【Tsai Tsai】

 而言,卻有三個方塊被誤分類成了圓,而有兩個圓被誤分類成了方塊,這條決策邊界上的泛化誤差就遠遠大于

支援向量機求解過程的可視化及其核函數kernel 【Tsai Tsai】

了。這個例子表現出,擁有更大邊際的決策邊界在分類中的泛化誤差更小,這一點可以由結構風險最小化定律來證明(SRM)。如果邊際很小,則任何輕微擾動都會對決策邊界的分類産生很大的影響。邊際很小的情況,是一種模型在訓練集上表現很好,卻在測試集上表現糟糕的情況,是以會“過拟合”。是以我們在找尋決策邊界的時候,希望邊際越大越好。

支援向量機求解過程的可視化及其核函數kernel 【Tsai Tsai】

支援向量機,就是通過找出邊際最大的決策邊界,來對資料進行分類的分類器。 也是以,支援向量分類器又叫做最大邊際分類器。

2. sklearn.svm.SVC

class sklearn.svm.SVC ( C=1.0 , kernel=’rbf’ , degree=3 , gamma=’auto_deprecated’ , coef0=0.0 , shrinking=True , probability=False , tol=0.001 , cache_size=200 , class_weight=None , verbose=False , max_iter=-1 , decision_function_shape=’ovr’ , random_state=None )

2.1 線性SVM決策過程的可視化

from sklearn.datasets import make_blobs
from sklearn.svm import SVC
import matplotlib.pyplot as plt
import numpy as np
x,y=make_blobs(n_samples=50,centers=2,random_state=0,cluster_std=0.6) #簇的方差=0.6
x.shape
           
(50, 2)
      
plt.scatter(x[:,0],x[:,1],c=y,s=50,cmap="rainbow")
plt.xticks([])
plt.yticks([])
plt.show()
           
支援向量機求解過程的可視化及其核函數kernel 【Tsai Tsai】
#畫決策邊界:制作網格
plt.scatter(x[:,0],x[:,1],c=y,s=50,cmap="rainbow")
#擷取目前的子圖,如果不存在,則建立新的子圖
ax=plt.gca()
#擷取平面上兩條坐标軸的最大值和最小值
xlim=ax.get_xlim()
ylim=ax.get_ylim()
#在最大值和最小值之間形成30個規律的資料
axisx=np.linspace(xlim[0],xlim[1],30) 
axisy=np.linspace(ylim[0],ylim[1],30)
#使用meshgrid函數将兩個一維向量轉換為特征矩陣
axisy,axisx=np.meshgrid(axisy,axisx)
xy=np.vstack([axisx.ravel(),axisy.ravel()]).T
#其中ravel()是降維函數,vstack能夠将多個結構一緻的一維數組按行堆疊起來
# xy就是已經形成的網格,它是遍布在整個畫布上密集的點
plt.scatter(xy[:,0],xy[:,1],s=1,cmap="rainbow")
           
<matplotlib.collections.PathCollection at 0x26d6f2838b0>      
支援向量機求解過程的可視化及其核函數kernel 【Tsai Tsai】

重要接口decision_function,傳回每個輸入的樣本所對應的到決策邊界的距離,然後再将這個距離轉換為axisx的結構,這是由于畫圖的函數contour要求z的結構必須與x和y保持一緻 

# 模組化。通過fit計算出對應的決策邊界
clf=SVC(kernel="linear").fit(x,y)
z=clf.decision_function(xy).reshape(axisx.shape)
plt.scatter(x[:,0],x[:,1],c=y,s=50,cmap="rainbow")
ax=plt.gca()
# 畫出決策邊界和平行于決策邊界的超平面
ax.contour(axisx,axisy,z #橫坐标,縱坐标,高度
          ,colors="k"
          ,levels=[-1,0,1] #畫三條等高線,分别是z為-1,0,1的三條線
          ,alpha=0.5
          ,linestyles=["--","-","--"])
ax.set_xlim(xlim)
ax.set_ylim(ylim)
           
支援向量機求解過程的可視化及其核函數kernel 【Tsai Tsai】

為了便于更好地了解SVM作用原理,我們去第10個點作為例子:(z的本質是輸入的樣本到決策邊界的距離,而contour函數中的level其實是輸入了這個距離)

plt.scatter(x[:,0],x[:,1],c=y,s=50,cmap="rainbow")
plt.scatter(x[10,0],x[10,1],c="black",s=50,cmap="rainbow")
clf.decision_function(x[10].reshape(1,2))
           
支援向量機求解過程的可視化及其核函數kernel 【Tsai Tsai】
array([-3.33917354])      
plt.scatter(x[:,0],x[:,1],c=y,s=50,cmap="rainbow")
ax=plt.gca()
ax.contour(axisx,axisy,z
          ,colors="k"
          ,levels=[-3.33917354]
          ,alpha=0.5
          ,linestyles=["--"])
           
支援向量機求解過程的可視化及其核函數kernel 【Tsai Tsai】

對上述過程包裝成函數:

def plot_svc_decision_function(model,ax=None):
    if ax is None:
        ax=plt.gca()
    xlim=ax.get_xlim()
    ylim=ax.get_ylim()
    
    x=np.linspace(xlim[0],xlim[1],30)
    y=np.linspace(ylim[0],ylim[1],30)
    Y,X=np.meshgrid(y,x)
    xy=np.vstack([X.ravel(),Y.ravel()]).T
    p=model.decision_function(xy).reshape(X.shape)
    
    ax.contour(X,Y,p
              ,colors="k"
              ,levels=[-1,0,1]
              ,alpha=0.5
              ,linestyles=["--","-","--"])
    ax.set_xlim(xlim)
    ax.set_ylim(ylim)


clf=SVC(kernel="linear").fit(x,y)
plt.scatter(x[:,0],x[:,1],c=y,s=50,cmap="rainbow")
plot_svc_decision_function(clf)
           
支援向量機求解過程的可視化及其核函數kernel 【Tsai Tsai】

 2.2 參數說明

根據決策邊界,對x中樣本進行分類,傳回的結構為n_samples

clf.predict(x)
           
array([1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1,
       1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1,
       0, 1, 1, 0, 1, 0])      

傳回給定測試資料和标簽的平均準确度

clf.score(x,y)
           
1.0

      

傳回支援向量

clf.support_vectors_
           
array([[0.44359863, 3.11530945],
       [2.33812285, 3.43116792],
       [2.06156753, 1.96918596]])      

傳回每個類中支援向量的個數

clf.n_support_
           
array([2, 1])      

2.3 非線性資料集上的推廣與3D可視化

from sklearn.datasets import make_circles
x,y=make_circles(100,factor=0.1,noise=0.1)
plt.scatter(x[:,0],x[:,1],c=y,s=50,cmap="rainbow")
plt.show()
           
支援向量機求解過程的可視化及其核函數kernel 【Tsai Tsai】
clf=SVC(kernel="linear").fit(x,y)
plt.scatter(x[:,0],x[:,1],c=y,s=50,cmap="rainbow")
plot_svc_decision_function(clf)
clf.score(x,y)
           
支援向量機求解過程的可視化及其核函數kernel 【Tsai Tsai】
0.69      

定義一個由x計算出來的新次元r

r=np.exp(-(x**2).sum(1))
rlim=np.linspace(min(r),max(r),100)
from mpl_toolkits import mplot3d
# 定義一個繪制三維圖像的函數
# evel表示上下旋轉的角度
# azim表示平行旋轉的角度
def plot_3D(elev=30,azim=30,x=x,y=y):
    ax=plt.subplot(projection="3d")
    ax.scatter3D(x[:,0],x[:,1],r,c=y,s=50,cmap="rainbow")
    ax.view_init(elev=elev,azim=azim)
    ax.set_xlabel("x")
    ax.set_ylabel("y")
    ax.set_zlabel("r")
    plt.show()
plot_3D()
           
支援向量機求解過程的可視化及其核函數kernel 【Tsai Tsai】

此時我們的資料在三維空間中,我們的超平面就是一個二維平面。明顯我們可以用一個平面将兩類資料隔開,這個平面就是我們的超平面。我們剛才做的,計算r ,并将 r 作為資料的第三次元來講資料升維的過程,被稱為 “ 核變換” ,即是将資料投影到高維空間中,以尋找能夠将資料完美分割的超平面,而在高維空間中計算來找出超平面的函數就叫做核函數。在SVM 中,這個功能由參數 “kernel”控制。在這裡我們使用的是 “linear" ,線性核函數,隻能用于線性的情況。剛才我們使用的計算 r 的方法,其實是高斯徑向基核函數,在參數”kernel“ 中輸入 ”rbf“ 就可以使用。我們來看看模型找出的決策邊界時什麼樣:

clf=SVC(kernel="rbf").fit(x,y)
plt.scatter(x[:,0],x[:,1],c=y,s=50,cmap="rainbow")
plot_svc_decision_function(clf)
           
支援向量機求解過程的可視化及其核函數kernel 【Tsai Tsai】

3. SVC的重要參數kernel

3.1 在sklearn中實作SVC的基本流程:

from sklearn.svm import SVC #導入需要的子產品
clf = SVC()     #執行個體化
clf = clf.fit(X_train,y_train) #用訓練集資料訓練模型
result = clf.score(X_test,y_test) #導入測試集,從接口中調用需要的資訊
           

 3.2 kernel

作為 SVC 類最重要的參數之一, “kernel" 在 sklearn 中可選以下幾種選項:

輸入 含義 解決問題 參數gamma 參數degree 參數coef0
"linear" 線性核 線性 no no no
"poly" 多項式核 偏線性 yes yes yes
"sigmoid" 雙曲正切核 非線性 yes no yes
"rbf" 高斯徑向基 偏非線性 yes no no

3.3 月亮型、環型、簇型、分類型在4個核函數不同的表現

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
from sklearn import svm
from sklearn.datasets import make_circles, make_moons, make_blobs,make_classification
n_samples = 100
datasets = [
    make_moons(n_samples=n_samples, noise=0.2, random_state=0),
    make_circles(n_samples=n_samples, noise=0.2, factor=0.5, random_state=1),
    make_blobs(n_samples=n_samples, centers=2, random_state=5),
    make_classification(n_samples=n_samples,n_features = 
2,n_informative=2,n_redundant=0, random_state=5)
 ]
Kernel = ["linear","poly","rbf","sigmoid"] 
for X,Y in datasets:
    plt.figure(figsize=(5,4))
    plt.scatter(X[:,0],X[:,1],c=Y,s=50,cmap="rainbow")
           
支援向量機求解過程的可視化及其核函數kernel 【Tsai Tsai】
支援向量機求解過程的可視化及其核函數kernel 【Tsai Tsai】
支援向量機求解過程的可視化及其核函數kernel 【Tsai Tsai】
支援向量機求解過程的可視化及其核函數kernel 【Tsai Tsai】
# 建構子圖
nrows=len(datasets)
ncols=len(kernel)+1
fig,axes=plt.subplots(nrows,ncols,figsize=(20,16))
# 第一層循環:在不同的資料集中循環
for ds_cnt,(x,y) in enumerate(datasets):
    #在圖像的第一列,放置原資料的分布
    ax=axes[ds_cnt,0]
    if ds_cnt==0:
        ax.set_title("Input Data")
    ax.scatter(x[:,0],x[:,1],c=y,zorder=10,cmap=plt.cm.Paired,edgecolors="k") #zorder越大,圖像顯示越上面
    ax.set_xticks(())
    ax.set_yticks(())
    #第二層循環:在不同的核函數中循環
    #從圖像的第二列開始,一個個填充分類結果
    for est_idx,kernel in enumerate(kernel):
        #定義子圖位置
        ax=axes[ds_cnt,est_idx + 1]
        clf=svm.SVC(kernel=kernel,gamma=2).fit(x,y)
        score=clf.score(x,y)
        #繪制圖像本身分布的散點圖
        ax.scatter(x[:,0],x[:,1],c=y
                  ,zorder=10
                  ,cmap=plt.cm.Paired
                  ,edgecolors='k')
        #繪制支援向量
        ax.scatter(clf.support_vectors_[:,0],clf.support_vectors_[:,1],s=50,
                  facecolors='none',zorder=10,edgecolors='k')
        #繪制決策邊界
        x_min,x_max=x[:,0].min()-0.5,x[:,0].max()+0.5
        y_min,y_max=x[:,1].min()-0.5,x[:,1].max()+0.5
        #步長是複數,則其整數部分就是起始值和結束值之間建立的點的數量,并且結束值被包含在内
        XX,YY=np.mgrid[x_min:x_max:200j,y_min:y_max:200j]
        #np.c_類似于np.vstack的功能
        Z=clf.decision_function(np.c_[XX.ravel(),YY.ravel()]).reshape(XX.shape)
        #填充等高線不同區域的顔色
        ax.pcolormesh(XX,YY,Z>0,cmap=plt.cm.Paired)
        #繪制等高線
        ax.contour(XX,YY,Z,colors=['k','k','k'],linestyles=['--','-','--'],levels=[-1,0,1])
        
        ax.set_xticks(())
        ax.set_yticks(())
        
        #将标題放在第一行的頂上
        if ds_cnt==0:
            ax.set_title(kernel)
            
        #為每張圖添加分類的分數
        ax.text(0.95,0.06,('%.2f'%score).lstrip('0')
                ,size=15
                ,bbox=dict(boxstyle='round',alpha=0.8,facecolor='white')
                #為分數添加一個白色的格子作為底色
                ,transform=ax.transAxes #确定文字所對應的坐标軸,就是ax子圖的坐标軸本身
                ,horizontalalignment='right' #位于坐标軸的什麼方向
               )
plt.tight_layout()
plt.show()
           
支援向量機求解過程的可視化及其核函數kernel 【Tsai Tsai】

         可以觀察到,線性核函數和多項式核函數在非線性資料上表現會浮動,如果資料相對線性可分,則表現不錯,如果是像環形資料那樣徹底不可分的,則表現糟糕。線上性資料集上,線性核函數和多項式核函數即便有擾動項也可以表現不錯,可見多項式核函數是雖然也可以處理非線性情況,但更偏向于線性的功能。

        Sigmoid核函數就比較尴尬了,它在非線性資料上強于兩個線性核函數,但效果明顯不如rbf,它線上性資料上完全比不上線性的核函數們,對擾動項的抵抗也比較弱,是以它功能比較弱小,很少被用到。

        rbf,高斯徑向基核函數基本在任何資料集上都表現不錯,屬于比較萬能的核函數。我個人的經驗是,無論如何先試試看高斯徑向基核函數,它适用于核轉換到很高的空間的情況,在各種情況下往往效果都很不錯,如果rbf 效果不好,那我們再試試看其他的核函數。另外,多項式核函數多被用于圖像處理之中。

繼續閱讀