1、基本概念
向量的內積即一個向量在另一個向量上的投影乘上被投影向量的模,上圖不管是a投影在b上,還是b投影在a上,其結果是一樣的,原理參照 B站上 3Blue1Brown
a∙b = (a1e1 + a2e2)∙(b1e1 + b2e2)
= a1b1e1e1 + a1b2e1e2
+ a2b1e2e1 + a2b2e2e2
= a1b1 + a2b2
=aTb
對於一個超平面,其方程爲 wTx + b = 0 (二維的超平面就是線,而線的方程爲ax + by + c = 0 ),
假設A、B兩點在wTx + b = 0上
那麼向量 w 垂直於這個超平面。
2、線性可分支持向量機
假設對於如圖所示的訓練集,我們怎麼去尋找它的超平面呢?
無數條直線可以表示爲wTx(i)+ b,我們要做的就是在這麼多條的直線中找到一條比較好的超平面
因此首先需要求出所有樣本點到直線的距離d=|wTx(i)+ b| / ||w||,而如果我們定義實心點爲正例+1,空心點爲負例-1
由於 g(x(i)) = sign(wTx(i) + b),那麼當
-
wTx(i) + b > 0,將其分爲正例,y(i) = 1
-
wTx(i) + b < 0,將其分爲反例,y(i) = -1
-
根據上面兩種情況,關係式 y(i)(wTx(i) + b) > 0 永遠成立
d可寫爲d=y(i)(wTx(i)+ b) / ||w|| 即可以把絕對值去掉。
1)需所有的樣本點到直線的距離最短 min y(i)(wTx(i)+ b) / ||w|| (保證 直線中找到一條比較好的超平面)
2) 在1)的前提下,選出一條最大值max min y(i)(wTx(i)+ b) / ||w|| (保證間隔最大)
目標函數爲
現在有個問題,當 w 和
b 變成它們原來 0.01 倍或者 1000 倍時, wTx +
b = 0 代表的是同樣的超平面,展示如下:
wTx + b = 0
0.01wTx + 0.01b = 0
1000wTx + 1000b = 0
即我們在後面解出唯一的解時,可以縮放權重w和偏差b的比例.
證:
最終目標函數變爲:
上面連等式第一步是因爲最大化一個正函數就是最小化它的倒數;第二步是用了 ||w||
的定義;第三步純粹是爲了之後求導數容易,而且最小化一個函數跟最小化它的一半是完全等價的。
在
SVM 問題中,我們不但希望要最大化 margin,還希望每個點都有分類正確,因爲我們要解決下面優化問題
到此問題已經非常簡單了,目標函數是個二次凸函數,非常適合做優化;但是限制條件裏面有
min 出現,給優化問題造成了難度。因此我們將這一個帶 min 的限制條件轉換成 m 個“更鬆”的限制條件,如下
由限制條件可知,該問題是找一個超平面,線性分割所有點並使它們完全分類正確,
這種超平面稱爲線性硬分隔支撐向量機 (hard-margin SVM)
簡單的例子
二維問題來求解上面的原始問題
根據這個四個點展開限制條件得到
根據
(i) 和 (iii) 解得 w1 ≥
1,根據 (ii) 和 (iii) 解得 w2 ≤
-1,這意味着目標函數
最小值在 w1 =
1 和 w2 =
-1 時得到,很容易解得 b = -1。最優分割超平面在下圖展示:
對那些剛好滿足限制條件的點,他們到超平面的距離都等於
1/||w||。這些點也都在灰色緩衝帶的邊界上,稱爲支撐向量
(support vector),顧名思義,這些向量支撐着緩衝帶,防止它繼續向外展開。
軟間隔
SVM 原始問題
硬間隔 SVM 要求所有得數據都要分類正確,在此前提下再最小化 wTw,但是現實中這種事情很少發生 (即沒有這樣一個完美的數據讓你所有分類正確)。那麼你要放棄這個問題,還是要試試另外一種方法,即軟間隔 SVM?
軟間隔 SVM 就是爲了緩解“找不到完美分類數據”的問題,它會容忍一些錯誤的發生,將發生錯誤的情況加入到目標函數之中,希望能得到一個錯分類情況越少越好的結果。爲了能一目瞭然發現硬間隔和軟間隔 SVM 原始問題之間的相似和不同之處 (紅色標示),將它們的類比形式展示在下表:
由上發現硬間隔和軟間隔 SVM 的區別就是後者多了 ξ 和 C,參數ξ是衡量數據犯了多大的錯誤,而不是數據犯了幾個錯誤。爲了簡化令 ui = y(i)(wTx(i) + b):
-
當 ξi = 0 時,該點沒有違規而且分類正確,因爲 ui ≥ 1,該點離分隔線距離大於等於 margin
-
當 0 < ξi ≤ 1 時,該點違規了但還是分類正確,因爲 ui 大於“一個小於1”的正數,該點離分隔線距離小於 margin
-
當 ξi > 1 時,該點違規了並分類錯誤,因爲 ui 大於一個負數 (ui 可能小於0),我們知道只有 ui > 0 是分類正確
上述討論情況在下圖中展示
總結來說,用 鬆弛變量ξ 來記錄違規數據距離邊界的距離,並將這個距離納入到最佳化的標準裏面。但是我們不希望 ξ 太大,因爲這意味着有某個數據分類錯的太離譜,因此需要用 C 來懲罰太大的ξ。參數 C 控制“到底要多大的邊界”還是“更加在意違反邊界的情形越少越好”。(調參會用到)
-
越大的 C 代表,寧可邊界窄一點,也要違規 (甚至出錯) 數據少點
-
越小的 C 代表,寧可邊界寬一點,即便犧牲分類精度也無所謂
部分數據
代碼:
import numpy as np import pandas as pd import matplotlib as mpl import matplotlib.pyplot as plt from sklearn import svm from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score if __name__ == "__main__": iris_feature = '花萼長度', '花萼寬度', '花瓣長度', '花瓣寬度' path = 'E:\\machine learning\\code\\11.RandomForest\\iris.data' # 數據文件路徑 data = pd.read_csv(path, header=None) x, y = data[[0, 1]], pd.Categorical(data[4]).codes #即x爲iris.data裏的第一二列,y爲iris.data裏的第5列;pd.Categorical對花的類別進行編碼 x_train, x_test, y_train, y_test = train_test_split(x, y, random_state=1, train_size=0.6) # 分類器 # clf = svm.SVC(C=0.1, kernel='linear', decision_function_shape='ovr') #ovr one vs rest clf = svm.SVC(C=0.8, kernel='rbf', gamma=20, decision_function_shape='ovr') clf.fit(x_train, y_train.ravel())
random_state:是隨機數的種子。
隨機數種子:其實就是該組隨機數的編號,在需要重複試驗的時候,保證得到一組一樣的隨機數。比如你每次都填1,其他參數一樣的情況下你得到的隨機數組是一樣的。但填0或不填,每次都會不一樣。隨機數的產生取決於種子,隨機數和種子之間的關係遵從以下兩個規則:種子不同,產生不同的隨機數;種子相同,即使實例不同也產生相同的隨機數。
kernel='linear'時,爲線性核,C越大分類效果越好,但有可能會過擬合(defaul C=1)。
kernel='rbf'時(default),爲高斯核,gamma值越小,分類界面越連續;gamma值越大,分類界面越“散”,分類效果越好,但有可能會過擬合。
decision_function_shape='ovr'時,爲one v rest,即一個類別與其他類別進行劃分,
decision_function_shape='ovo'時,爲one v one,即將類別兩兩之間進行劃分,用二分類的方法模擬多分類的結果。
# 準確率 print(clf.score(x_train, y_train)) # 精度 print('訓練集準確率:', accuracy_score(y_train, clf.predict(x_train))) print(clf.score(x_test, y_test)) print('測試集準確率:', accuracy_score(y_test, clf.predict(x_test)))如果想查看決策函數,可以通過decision_function()實現
# decision_function print(x_train[:5]) print('decision_function:\n', clf.decision_function(x_train)) print('\npredict:\n', clf.predict(x_train))
繪製圖像
1.確定座標軸範圍,x,y軸分別表示兩個特徵
# 畫圖 x1_min, x2_min = x.min() x1_max, x2_max = x.max() x1, x2 = np.mgrid[x1_min:x1_max:500j, x2_min:x2_max:500j] # 生成網格採樣點 grid_test = np.stack((x1.flat, x2.flat), axis=1) # 測試點 grid_hat = clf.predict(grid_test) # 預測分類值 grid_hat = grid_hat.reshape(x1.shape) # 使之與輸入的形狀相同2.指定默認字體
mpl.rcParams['font.sans-serif'] = ['SimHei'] mpl.rcParams['axes.unicode_minus'] = False3.繪製
cm_light = mpl.colors.ListedColormap(['#A0FFA0', '#FFA0A0', '#A0A0FF']) cm_dark = mpl.colors.ListedColormap(['g', 'r', 'b']) plt.figure(facecolor='w') plt.pcolormesh(x1, x2, grid_hat, cmap=cm_light) plt.scatter(x[0], x[1], c=y, edgecolors='k', s=50, cmap=cm_dark) # 樣本 plt.scatter(x_test[0], x_test[1], s=120, facecolors='none', zorder=10) # 圈中測試集樣本 plt.xlabel(iris_feature[0], fontsize=13) plt.ylabel(iris_feature[1], fontsize=13) plt.xlim(x1_min, x1_max) plt.ylim(x2_min, x2_max) plt.title('鳶尾花SVM二特徵分類', fontsize=16) plt.grid(b=True, ls=':') plt.tight_layout(pad=1.5) plt.show()
pcolormesh(x,y,z,cmap)這裏參數代入x1,x2,grid_hat,cmap=cm_light繪製的是背景。
scatter中edgecolors是指描繪點的邊緣色彩,s指描繪點的大小,cmap指點的顏色。
xlim指圖的邊界。
結果爲:0.866666666667
訓練集準確率: 0.8666e=
6666667
0.65
測試集準確率: 0.65
decision_function:
[[ 2.45540648 0.80337522 -0.2587817 ]
[-0.4368348 2.31950945 1.11732536]
[-0.43793789 1.00917055 2.42876733]
[ 2.45555373 0.80242493 -0.25797866]
[ 2.46185007 0.80020899 -0.26205906]
[-0.4275673 0.97215049 2.4554168 ]
[ 2.4554096 0.80344613 -0.25885573]
[-0.42578192 2.23549613 1.19028579]
[-0.33298947 0.85928729 2.47370219]
predict:
[0 1 2 0 0 2 0 1 2 2 1 2 1 0 1 2 2 1 2 1 0 0 0 2 0 1 2 1 0 0 1 0 2 1 2 2 1
2 2 1 0 1 0 1 1 0 1 0 0 2 1 2 0 0 1 0 1 0 2 1 0 2 0 1 0 1 1 0 0 1 0 1 1 0
2 1 1 1 1 0 0 1 1 2 1 2 2 1 2 0]
============================================
本文是閱讀鄒博機器學習升級版SVM與微信公衆號——【王的機器】SVM之後,結合兩文寫出的一些心得,
但並未完全弄懂,相應知識點以後查缺補漏後再行補充。