目錄
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.支援向量機分類器是如何工作的?
支援向量機所作的事情其實非常容易了解。先來看看下面這一組資料的分布,這是一組兩種标簽的資料,兩種标簽分别由圓和方塊代表。支援向量機的分類方法,是在這組分布中找出一個超平面作為決策邊界,使模型在資料上的分類誤差盡量接近于小,尤其是在未知資料集上的分類誤差(泛化誤差)盡量小。
超平面
在幾何中,超平面是一個空間的子空間,它是次元比所在空間小一維的空間。 如果資料空間本身是三維的,則其超平面是二維平面,而如果資料空間本身是二維的,則其超平面是一維的直線。
在二分類問題中,如果一個超平面能夠将資料劃分為兩個集合,其中每個集合中包含單獨的一個類别,我們就說這個超平面是資料的“決策邊界”。
決策邊界一側的所有點在分類為屬于一個類,而另一側的所有點分類屬于另一個類。如果我們能夠找出決策邊界,分類問題就可以變成探讨每個樣本對于決策邊界而言的相對位置。比如上面的資料分布,我們很容易就可以在方塊和圓的中間畫出一條線,并讓所有落在直線左邊的樣本被分類為方塊,在直線右邊的樣本被分類為圓。如果把資料當作我們的訓練集,隻要直線的一邊隻有一種類型的資料,就沒有分類錯誤,我們的訓練誤差就會為0。但是,對于一個資料集來說,讓訓練誤差為0的決策邊界可以有無數條。
但在此基礎上,我們無法保證這條決策邊界在未知資料集(測試集)上的表現也會優秀。對于現有的資料集來說,我們有
和
兩條可能的決策邊界。我們可以把決策邊界
向兩邊平移,直到碰到離這條決策邊界最近的方塊和圓圈後停下,形成兩個新的超平面,分别是
和
,并且我們将原始的決策邊界移動到
和
的中間,確定
到
和
的距離相等。在
和
中間的距離,叫做 這條決策邊界的邊際 (margin),通常記作
。對
也執行同樣的操作,然後我們來對比一下兩個決策邊界。現在兩條決策邊界右邊的資料都被判斷為圓,左邊的資料都被判斷為方塊,兩條決策邊界在現在的資料集上的訓練誤差都是0 ,沒有一個樣本被分錯。
我們引入和原本的資料集相同分布的測試樣本(紅色所示),平面中的樣本變多了,此時我們可以發現,對于
而言,依然沒有一個樣本被分錯,這條決策邊界上的泛化誤差也是0。但是對于
而言,卻有三個方塊被誤分類成了圓,而有兩個圓被誤分類成了方塊,這條決策邊界上的泛化誤差就遠遠大于
了。這個例子表現出,擁有更大邊際的決策邊界在分類中的泛化誤差更小,這一點可以由結構風險最小化定律來證明(SRM)。如果邊際很小,則任何輕微擾動都會對決策邊界的分類産生很大的影響。邊際很小的情況,是一種模型在訓練集上表現很好,卻在測試集上表現糟糕的情況,是以會“過拟合”。是以我們在找尋決策邊界的時候,希望邊際越大越好。
支援向量機,就是通過找出邊際最大的決策邊界,來對資料進行分類的分類器。 也是以,支援向量分類器又叫做最大邊際分類器。
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()
#畫決策邊界:制作網格
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>
重要接口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)
為了便于更好地了解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))
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=["--"])
對上述過程包裝成函數:
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)
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()
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)
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()
此時我們的資料在三維空間中,我們的超平面就是一個二維平面。明顯我們可以用一個平面将兩類資料隔開,這個平面就是我們的超平面。我們剛才做的,計算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)
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")
# 建構子圖
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()
可以觀察到,線性核函數和多項式核函數在非線性資料上表現會浮動,如果資料相對線性可分,則表現不錯,如果是像環形資料那樣徹底不可分的,則表現糟糕。線上性資料集上,線性核函數和多項式核函數即便有擾動項也可以表現不錯,可見多項式核函數是雖然也可以處理非線性情況,但更偏向于線性的功能。
Sigmoid核函數就比較尴尬了,它在非線性資料上強于兩個線性核函數,但效果明顯不如rbf,它線上性資料上完全比不上線性的核函數們,對擾動項的抵抗也比較弱,是以它功能比較弱小,很少被用到。
rbf,高斯徑向基核函數基本在任何資料集上都表現不錯,屬于比較萬能的核函數。我個人的經驗是,無論如何先試試看高斯徑向基核函數,它适用于核轉換到很高的空間的情況,在各種情況下往往效果都很不錯,如果rbf 效果不好,那我們再試試看其他的核函數。另外,多項式核函數多被用于圖像處理之中。