SVM算法理解以及編程練習

支持向量機

支持向量機就算法作爲機器學習的經典算法,從被提出後快速發展,在很多場景和領域都取得了非常好的效果,同時兼有速度塊、支持數據量大等特點使其在工程實踐中得到廣泛應用。

首先我們先來看一個3維的平面方程:Ax+By+Cz+D=0
這就是我們中學所學的,從這個方程我們可以推導出二維空間的一條直線:Ax+By+D=0

那麼,依次類推,更高維的空間叫做一個超平面。
二維空間的幾何表示:
在這裏插入圖片描述
SVM的目標是找到一個超平面,這個超平面能夠很好的解決二分類問題,所以先找到各個分類的樣本點離這個超平面最近的點,使得這個點到超平面的距離最大化,最近的點就是虛線所畫的。由以上超平面公式計算得出大於1的就屬於打叉分類,如果小於0的屬於圓圈分類。

如何找到超平面

在超平面wx+b=0確定的情況下,|wx+b|能夠表示點x到距離超平面的遠近,而通過觀察wx+b的符號與類標記y的符號是否一致可判斷分類是否正確,所以,可以用(y(w*x+b))的正負性來判定或表示分類的正確性。於此,我們便引出了函數間隔(functional margin)的概念。定義函數間隔(用γ表示)爲:
在這裏插入圖片描述

但是這個函數間隔有個問題,就是我成倍的增加w和b的值,則函數值也會跟着成倍增加,但這個超平面沒有改變。所以有函數間隔還不夠,需要一個幾何間隔。

幾何間隔
我們把w做一個約束條件,假定對於一個點 x ,令其垂直投影到超平面上的對應點爲 x0 ,w 是垂直於超平面的一個向量,爲樣本x到超平面的距離,如下圖所示:
在這裏插入圖片描述

最大間隔分類器

對一個數據點進行分類,當超平面離數據點的“間隔”越大,分類的確信度(confidence)也越大。所以,爲了使得分類的確信度儘量高,需要讓所選擇的超平面能夠最大化這個“間隔”值。這個間隔就是下圖中的Gap的一半。

image

代碼練習理解

代碼1

import numpy as np
import matplotlib.pyplot as plt

from sklearn import datasets
from sklearn.preprocessing import StandardScaler
from sklearn.svm import LinearSVC

iris = datasets.load_iris()

X = iris.data
y = iris.target

X = X [y<2,:2] #只取y<2的類別,也就是0 1 並且只取前兩個特徵
y = y[y<2] # 只取y<2的類別

# 分別畫出類別0和1的點
plt.scatter(X[y==0,0],X[y==0,1],color='red') 
plt.scatter(X[y==1,0],X[y==1,1],color='blue')
plt.show()

# 標準化
standardScaler = StandardScaler()

standardScaler.fit(X) #計算訓練數據的均值和方差
X_standard = standardScaler.transform(X) #再用scaler中的均值和方差來轉換X,使X標準化

svc = LinearSVC(C=1e9) #線性SVM分類器
svc.fit(X_standard,y) # 訓練svm

這是未經過標準化的原始數據點分佈
在這裏插入圖片描述

代碼2

繪製決策邊界

def plot_decision_boundary(model, axis):
    
    x0, x1 = np.meshgrid(
        np.linspace(axis[0], axis[1], int((axis[1]-axis[0])*100)).reshape(-1,1),
        np.linspace(axis[2], axis[3], int((axis[3]-axis[2])*100)).reshape(-1,1)
    )
    X_new = np.c_[x0.ravel(), x1.ravel()]
    
    y_predict = model.predict(X_new)
    zz = y_predict.reshape(x0.shape)
    
    from matplotlib.colors import ListedColormap
    custom_cmap = ListedColormap(['#EF9A9A','#FFF59D','#90CAF9'])
    
    plt.contourf(x0, x1, zz, linewidth=5, cmap=custom_cmap)


# 繪製決策邊界
plot_decision_boundary(svc,axis=[-3,3,-3,3]) # x,y軸都在-3到3之間
# 繪製原始數據
plt.scatter(X_standard[y==0,0],X_standard[y==0,1],color='red') 
plt.scatter(X_standard[y==1,0],X_standard[y==1,1],color='blue')
plt.show()

在這裏插入圖片描述
上面說了CCC是控制正則項的重要程度,這裏我們再次實例化一個svc,並傳入一個較小的C。

代碼3

svc2 = LinearSVC(C=0.01)
svc2.fit(X_standard,y)
plot_decision_boundary(svc2,axis=[-3,3,-3,3]) # x,y軸都在-3到3之間
# 繪製原始數據
plt.scatter(X_standard[y==0,0],X_standard[y==0,1],color='red') 
plt.scatter(X_standard[y==1,0],X_standard[y==1,1],color='blue')
plt.show()

在這裏插入圖片描述
可以很明顯的看到和第一個決策邊界的不同,在這個決策邊界彙總,有一個紅點是分類錯誤的。
C越小容錯空間越大。

代碼4

使用多項式特徵和核函數

import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets

X, y = datasets.make_moons() #使用生成的數據
print(X.shape) # (100,2)
print(y.shape) # (100,)

生成月亮數據集

plt.scatter(X[y==0,0],X[y==0,1]) 
plt.scatter(X[y==1,0],X[y==1,1])
plt.show()

在這裏插入圖片描述

X, y = datasets.make_moons(noise=0.15,random_state=777) #隨機生成噪聲點,random_state是隨機種子,noise是方差

plt.scatter(X[y==0,0],X[y==0,1]) 
plt.scatter(X[y==1,0],X[y==1,1])
plt.show()

在這裏插入圖片描述
多項式特徵的SVM來對它進行分類。

from sklearn.preprocessing import PolynomialFeatures,StandardScaler
from sklearn.svm import LinearSVC
from sklearn.pipeline import Pipeline

def PolynomialSVC(degree,C=1.0):
    return Pipeline([
        ("poly",PolynomialFeatures(degree=degree)),#生成多項式
        ("std_scaler",StandardScaler()),#標準化
        ("linearSVC",LinearSVC(C=C))#最後生成svm
    ])
poly_svc = PolynomialSVC(degree=3)
poly_svc.fit(X,y)

plot_decision_boundary(poly_svc,axis=[-1.5,2.5,-1.0,1.5])
plt.scatter(X[y==0,0],X[y==0,1]) 
plt.scatter(X[y==1,0],X[y==1,1])
plt.show()

在這裏插入圖片描述
我們可以看到,生成的邊界不再是線性的直線了。

使用核技巧來對數據進行處理,使其維度提升,使原本線性不可分的數據,在高維空間變成線性可分的。再用線性SVM來進行處理。

from sklearn.svm import SVC

def PolynomialKernelSVC(degree,C=1.0):
    return Pipeline([
        ("std_scaler",StandardScaler()),
        ("kernelSVC",SVC(kernel="poly")) # poly代表多項式特徵
    ])

poly_kernel_svc = PolynomialKernelSVC(degree=3)
poly_kernel_svc.fit(X,y)

plot_decision_boundary(poly_kernel_svc,axis=[-1.5,2.5,-1.0,1.5])
plt.scatter(X[y==0,0],X[y==0,1]) 
plt.scatter(X[y==1,0],X[y==1,1])
plt.show()

在這裏插入圖片描述
可以看到這種方式也生成了一個非線性的邊界。

這裏SVC(kernel=“poly”)有個參數是kernel,就是核函數.

害!就這樣吧…
在這裏插入圖片描述

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