SVM的一些學習心得及案例(Python代碼)實現

1、基本概念

向量的內積即一個向量在另一個向量上的投影乘上被投影向量的模,上圖不管是a投影在b上,還是b投影在a上,其結果是一樣的,原理參照 B站上 3Blue1Brown

ab = (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,這意味着目標函數


最小值在 w= 1 和 w2 = -1 時得到,很容易解得 b = -1。最優分割超平面在下圖展示:


對那些剛好滿足限制條件的點,他們到超平面的距離都等於 1/||w||。這些點也都在灰色緩衝帶的邊界上,稱爲支撐向量 (support vector),顧名思義,這些向量支撐着緩衝帶,防止它繼續向外展開。


軟間隔 SVM 原始問題

硬間隔 SVM 要求所有得數據都要分類正確,在此前提下再最小化 wTw,但是現實中這種事情很少發生 (即沒有這樣一個完美的數據讓你所有分類正確)。那麼你要放棄這個問題,還是要試試另外一種方法,即軟間隔 SVM?


軟間隔 SVM 就是爲了緩解“找不到完美分類數據”的問題,它會容忍一些錯誤的發生,將發生錯誤的情況加入到目標函數之中,希望能得到一個錯分類情況越少越好的結果。爲了能一目瞭然發現硬間隔和軟間隔 SVM 原始問題之間的相似和不同之處 (紅色標示),將它們的類比形式展示在下表:


由上發現硬間隔和軟間隔 SVM 的區別就是後者多了 ξ 和 C,參數ξ是衡量數據犯了多大的錯誤,而不是數據犯了幾個錯誤。爲了簡化令 uy(i)(wTx(i) + b)

  • 當 ξi = 0 時,該點沒有違規而且分類正確,因爲 ui ≥ 1,該點離分隔線距離大於等於 margin

  • 當 0 < ξ≤ 1 時,該點違規了但還是分類正確,因爲 ui 大於“一個小於1”的正數,該點離分隔線距離小於 margin

  •  ξ> 1 時,該點違規了並分類錯誤,因爲 ui 大於一個負數 (ui 可能小於0),我們知道只有 ui > 0 是分類正確

上述討論情況在下圖中展示


總結來說,用 鬆弛變量ξ 來記錄違規數據距離邊界的距離,並將這個距離納入到最佳化的標準裏面。但是我們不希望 ξ 太大,因爲這意味着有某個數據分類錯的太離譜,因此需要用 C 來懲罰太大的ξ。參數 C 控制“到底要多大的邊界”還是“更加在意違反邊界的情形越少越好”。(調參會用到)

  • 越大的 C 代表,寧可邊界窄一點,也要違規 (甚至出錯) 數據少點

  • 越小的 C 代表,寧可邊界寬一點,即便犧牲分類精度也無所謂


iris數據SVM實例

部分數據


代碼:

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
    #xiris.data裏的第一二列,yiris.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'] = False
3.繪製

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之後,結合兩文寫出的一些心得,

但並未完全弄懂,相應知識點以後查缺補漏後再行補充。


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