SVM 支持向量機(二)
1、支持向量機
這樣,由於初始值的不同,最後得到的分割超平面也有可能不同,那麼一定存在一個最優的超平面,這種方法就是支持向量機。
由上述可知感知機模型,即在數據集線性可分的條件下,利用分割超平面 把樣本點劃分爲兩類,通過計算誤分類點距離超平面距離總和作爲損失函數,使其最小化從而調整超平面,直至所有誤分類點被糾正正確後迭代結束。
因爲 的取值不同,所以得到的分割超平面也可能不相同,所以感知機模型得到的超平面可能有多個,這就是不適定問題。那麼,支持向量機模型就是找到一個最優的分割超平面。
SVM模型和感知機模型一樣。SVM模型的方法是:不僅要讓樣本點被分割超平面分開,還希望那些離分割超平面最近的點到分割超平面的距離最小。
SVM 和 線性分類器對比如下:
支持向量機(SVM)是一個功能強大並且全面的機器學習模型,它能夠執行線性或非線性分類問題、迴歸問題,甚至是異常值檢測任務。
支持向量機分爲兩種:硬間隔支持向量機和軟間隔支持向量機。
SVM的思想:不僅要讓樣本點被分割超平面分開,還要去離分割平面最近的點(min)到分割超平面的距離儘可能遠(max)。這些點稱爲支持向量,即支持向量到決策邊界的距離儘可能遠。
決策邊界和支持向量關係如下:
2、硬間隔支持向量機(Hard Margin SVM)
2.1、數學方法推導
2.1.1、點到直線的距離
由解析幾何的知識,可知,二維平面中,點到直線的距離爲:
則,拓展到 n 維空間中,,點到直線的距離爲:
其中, 或 也叫 “ L2範數 ”,也就是模。當數據爲 n 維時,直線就變成了平面, 可以表示超平面的法向量。
2.1.2、超平面的建立
對於給定樣本點:,這裏的 是指的樣本點的標籤,因爲 SVM 解決二分類問題,所以只有 ±1 兩種標籤。
如上圖,將兩類樣本點分開的直線有無數多條,那麼 SVM 選擇最優的一條直線即上述推導,使離分割超平面最近的點的距離最大,也就是幾何間隔最大化。從圖中觀察可知,中間最粗的黑色直線是當前最好的分割超平面。
2.1.3、兩種距離
1. 函數間隔
考慮樣本點求解到超平面的距離公式中,對於同一個超平面而言,距離公式中的分母都是相同的,即,所以,比較各個樣本點到超平面距離的遠近,只比較分子即可,即, 而對於函數值的正負號與類別標籤是否一致表示了分類正確性,即的符號。如果 與樣本點標籤 同號,則代表分類正確;反之,分類錯誤。而 ,所以可以使用 表示樣本點分類正確與否的判斷,將該式稱爲函數間隔:
對於數據集D,最終的目的是要選取所有樣本點中最小的函數間隔作爲整個數據集到超平面的函數間隔。目的是爲了找到數據集中的某些樣本點,這些樣本點滿足到超平面的距離最近,這些點就是支持向量。然後,使這些點到超平面距離最遠得到的參數 所對應的超平面就是最優的超平面。又因爲定義的分割超平面,所以支持向量距離最近,即對應的函數值 最小,因爲在分割超平面兩側都有支持向量,所以距離超平面遠近和正負號沒有關係,所以可以用函數間隔表示,即:
函數間隔,表徵的是樣本點在函數值上和分割超平面函數值(=0)的“遠近”
2. 幾何間隔
函數間隔無法決定所要選擇的超平面是哪一個。因爲當我們把 都擴大2倍,那麼函數間隔 (函數值)也會跟着擴大2倍,但是超平面並沒有發生改變,還是原來的超平面。所以需要增加一些某些約束,所以,採用幾何間隔。
由上可知,幾何間隔可以用函數間隔表示:
由於幾何間隔 是支持向量到超平面的距離,滿足最小,所以其他樣本點到超平面的幾何距離都大於等於 ,即:
幾何間隔,表徵的是樣本點到分割超平面實際距離的遠近
2.1.3、SVM 求解目標
根據 SVM 模型的思想,即需要求解以下優化問題:
即:
上式又等價於:
由於函數間隔的大小取值對最終優化問題不產生影響,因爲無論函數間隔取值多少,最終目的是爲了尋找最優的。所以爲了簡單,不妨直接取 。
所以上式可進一步變形:
等價於:
所以,上式即SVM最終需要優化求解的目標函數。
我們令,也就意味着到超平面的距離爲1的點都是支持向量,即下圖中畫圈的點,如下圖所示:
2.2、模型方法推導
對於上述數學推導較爲複雜,不易理解,根據支持向量機可以直觀的從SVM模型思想上進行推導。
所以,對於數據集的樣本點,有:
將上式分子分母同時除以,可得:
令 ,所以,可得下式:
不妨令 ,所以:
對於公式(6)可得;
-
對於分類正確的樣本點,有 恆成立,即:
- 時,有 ;
- 時,有 。
-
對於分類錯誤的樣本點,有 恆成立,即:
- 時,有 ;
- 時,有 。
所以,對於正確分類的樣本點,恆有:
所以,可得 SVM 最終優化目標:
結論同上。
2.3、硬間隔向量機求解
2.3.1、求最優解
利用拉個格朗日對偶性,使用求解對偶問題的方法可以得到問題的最優解。使用對偶問題求解的方法是因爲更容易求解,且在後面更容易引入核函數。
第一步,可以構造拉格朗日函數:
其中, 是拉格朗日乘子向量, 。
由拉格朗日對偶性,上述問題的對偶問題是求極大極小問題,即:
(1)、先求解
對 分別求偏導數,並令偏導數等於0,可得:
帶入拉格朗日函數,可得:
所以,最後可得:
從上式可知, 的結果只取決於 ,即兩個向量的點乘結果。
(2)、然後求解
將第一步求解的結果帶入對偶問題 ,可得對偶優化問題:
等價於:
2.3.2、求解
2.4、求解硬間隔支持向量機
輸入:線性可分訓練集,且。
輸出:分割超平面 和分類決策函數 。
求解步驟:
-
構造約束優化問題.
-
利用SMO算法求解上面的優化問題,得到向量的值, 是拉格朗日乘子向量, .
-
求解計算向量的值.
-
找到滿足 對應的支持向量點,從而求解計算 的值.
-
由 和 得到分割超平面 和分類決策函數 .
3、支持向量機實踐
3.1、簡單支持向量機
通過使用 sklearn.datasets 數據集實現簡單的 SVM 。
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
import seaborn as sns; sns.set()
# 1、創建模擬數據集
from sklearn.datasets.samples_generator import make_blobs
X, y = make_blobs(n_samples=50, centers=2,
random_state=0, cluster_std=0.60)
plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn');
輸出結果:
# 2、可以有多種方法分類
xfit = np.linspace(-1, 3.5)
plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
for m, b in [(1, 0.65), (0.5, 1.6), (-0.2, 2.9)]:
plt.plot(xfit, m * xfit + b, '-k')
plt.xlim(-1, 3.5);
輸出結果:
SVM 的思想: 假想每一條分割線是有寬度的。在SVM的框架下, 認爲最寬的線爲最優的分割線
# 3、繪製有寬帶的分割線
xfit = np.linspace(-1, 3.5)
plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
for m, b, d in [(1, 0.65, 0.33), (0.5, 1.6, 0.55), (-0.2, 2.9, 0.2)]:
yfit = m * xfit + b
plt.plot(xfit, yfit, '-k')
plt.fill_between(xfit, yfit - d, yfit + d, edgecolor='none',
color='#AAAAAA', alpha=0.4)
plt.xlim(-1, 3.5);
輸出結果:
訓練SVM
# 4、使用線性SVM和比較大的 C
from sklearn.svm import SVC # "Support vector classifier"
model = SVC(kernel='linear', C=1E10)
model.fit(X, y)
創建一個顯示SVM分割線的函數
# 5、定義 SVM 分割線函數
def plot_svc_decision_function(model, ax=None, plot_support=True):
"""Plot the decision function for a 2D SVC"""
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=['--', '-', '--'])
if plot_support:
ax.scatter(model.support_vectors_[:, 0],
model.support_vectors_[:, 1],
s=300, linewidth=1, facecolors='none');
ax.set_xlim(xlim)
ax.set_ylim(ylim)
plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
plot_svc_decision_function(model);
輸出結果:
3.2、非線性支持向量機
3.2.1、SVM 中使用多項式特徵
(1)、導入數據集
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
X, y = datasets.make_moons()
plt.scatter(X[y==0,0], X[y==0,1])
plt.scatter(X[y==1,0], X[y==1,1])
plt.show()
輸出結果:
(2)、通過增加噪聲,增大數據集標準差
X, y = datasets.make_moons(noise=0.15, random_state=666)
plt.scatter(X[y==0,0], X[y==0,1])
plt.scatter(X[y==1,0], X[y==1,1])
plt.show()
輸出結果:
(3)、使用多項式核函數的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))
])
poly_svc = PolynomialSVC(degree=3)
poly_svc.fit(X, y)
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)
輸出結果:
(4)、使用多項式核函數的SVM
from sklearn.svm import SVC
def PolynomialKernelSVC(degree, C=1.0):
return Pipeline([
("std_scaler", StandardScaler()),
("kernelSVC", SVC(kernel="poly", degree=degree, C=C))
])
poly_kernel_svc = PolynomialKernelSVC(degree=3)
poly_kernel_svc.fit(X, y)
輸出結果:
3.3、SVM 解決迴歸問題
SVM 解決迴歸問題和解決分類問題的思想正好相反。即SVM解決分類問題希望在margin中的數據點越少越好,而SVM解決迴歸問題則希望落在margin中的數據點越多越好。
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.svm import LinearSVR
from sklearn.svm import SVR
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
boston = datasets.load_boston()
X = boston.data
y = boston.target
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=666)
def StandardLinearSVR(epsilon=0.1):
return Pipeline([
('std_scaler', StandardScaler()),
('linearSVR', LinearSVR(epsilon=epsilon))
])
svr = StandardLinearSVR()
svr.fit(X_train, y_train)
svr.score(X_test, y_test)
輸出 output : 0.6358806887937369
3.4、SVM 實現人臉的分類識別
對輸入的人臉圖像使用PCA(主成分分析)將圖像(看作一維向量)進行了降維處理,然後將降維後的向量作爲支持向量機的輸入。PCA降維的目的可以看作是特徵提取, 將圖像裏面真正對分類有決定性影響的數據提取出來,從而實現支持向量機人臉的分類識別.。
(1)、導入人臉實例數據集
from sklearn.datasets import fetch_lfw_people
faces = fetch_lfw_people(min_faces_per_person=60)
# 人臉實例
fig, ax = plt.subplots(3, 5)
for i, axi in enumerate(ax.flat):
axi.imshow(faces.images[i], cmap='bone')
axi.set(xticks=[], yticks=[],
xlabel=faces.target_names[faces.target[i]])
每一幅圖的尺寸爲 [62×47] , 大約 3000 個像素值。
我們可以將整個圖像展平爲一個長度爲3000左右的一維向量, 然後使用這個向量做爲特徵. 通常更有效的方法是通過預處理提取圖像最重要的特徵. 一個重要的特徵提取方法是PCA(主成分分析), 可以將一副圖像轉換爲一個長度爲更短的(150)向量。
from sklearn.svm import SVC
from sklearn.decomposition import PCA
from sklearn.pipeline import make_pipeline
pca = PCA(n_components=150, whiten=True, random_state=42)
svc = SVC(kernel='linear', class_weight='balanced')
model = make_pipeline(pca, svc)
(2)、將數據分爲訓練和測試數據集
from sklearn.model_selection import train_test_split
Xtrain, Xtest, ytrain, ytest = train_test_split(faces.data, faces.target,
random_state=42)
(3)、調參
通過交叉驗證尋找最佳的 C (控制間隔的大小)
from sklearn.model_selection import GridSearchCV
param_grid = {'svc__C': [1, 5, 10, 50]}
grid = GridSearchCV(model, param_grid)
grid.fit(Xtrain, ytrain)
model = grid.best_estimator_
yfit = model.predict(Xtest)
(4)、使用訓練好的SVM做預測
fig, ax = plt.subplots(4, 6)
for i, axi in enumerate(ax.flat):
axi.imshow(Xtest[i].reshape(62, 47), cmap='bone')
axi.set(xticks=[], yticks=[])
axi.set_ylabel(faces.target_names[yfit[i]].split()[-1],
color='black' if yfit[i] == ytest[i] else 'red')
fig.suptitle('Predicted Names; Incorrect Labels in Red', size=14);
預測結果:
(5)、生成報告與混淆矩陣
from sklearn.metrics import classification_report
print(classification_report(ytest, yfit,
target_names=faces.target_names))
from sklearn.metrics import confusion_matrix
mat = confusion_matrix(ytest, yfit)
sns.heatmap(mat.T, square=True, annot=True, fmt='d', cbar=False,
xticklabels=faces.target_names,
yticklabels=faces.target_names)
plt.xlabel('true label')
plt.ylabel('predicted label');
報告數據
混淆矩陣
4、SVM總結
4.1、優點
- SVM模型具有很好的泛化能力,特別是在小樣本訓練集上能比其他算法得到好很多的結果。其本身的優化目標是結構化風險最小,通過引入 margin 的概念,可以得到數據分佈的結構化描述,降低了對數據規模和數據分佈的要求;
- 模型只需要保存支持向量, 模型佔用內存少, 預測快;
- SVM 模型具有較強的數據理論支撐;
- 分類只取決於支持向量, 適合數據的維度高的情況, 例如DNA數據;
- SVM 模型引入核函數之後可以解決非線性分類問題,並且 SVM 模型還可以解決迴歸問題。
4.2、缺點
- SVM 模型不適合解決多分類問題,對於多分類問題,只能採用一對多模式間接實現;
- SVM 模型存在兩個對結果影響比較大的超參數,如超參數懲罰項係數 c ,所以複雜度比一般非線性模型要高,需要做調參 當數據量大時非常耗時間;
- 訓練的時間複雜度爲 或者至少 , 當數據量巨大時候不合適使用。
4.4、SVM模型與 LR模型的異同
- SVM 與 LR 模型都是有監督的機器學習算法,且都屬於判別模型;
- 如果不考慮 SVM 核函數情況,兩者都屬於是線性分類算法 ;
- SVM模型與 LR模型的構造原理不同;
- SVM模型與 LR模型在學習時考慮的樣本點不同 ;
- SVM 模型中自帶正則化項 ,和 LR模型相比,不容易產生過擬合問題。
文中實例及參考:
- 劉宇波老師《Python入門機器學習》
- 《機器學習基礎》
- 貪心學院,https://www.greedyai.com