复现经典:《统计学习方法》第 7 章 支持向量机
本文是李航老師的《統計學習方法》[1]一書的代碼復現。
作者:黃海廣[2]
備注:代碼都可以在github[3]中下載。
我將陸續將代碼發布在公眾號“機器學習初學者”,敬請關注。
代碼目錄
第 1 章 統計學習方法概論
第 2 章 感知機
第 3 章 k 近鄰法
第 4 章 樸素貝葉斯
第 5 章 決策樹
第 6 章 邏輯斯諦回歸
第 7 章 支持向量機
第 8 章 提升方法
第 9 章 EM 算法及其推廣
第 10 章 隱馬爾可夫模型
第 11 章 條件隨機場
第 12 章 監督學習方法總結
代碼參考:wzyonggege[4],WenDesi[5],火燙火燙的[6]
第 7 章 支持向量機
1.支持向量機最簡單的情況是線性可分支持向量機,或硬間隔支持向量機。構建它的條件是訓練數據線性可分。其學習策略是最大間隔法??梢员硎緸橥苟我巹潌栴},其原始最優化問題為
求得最優化問題的解為,,得到線性可分支持向量機,分離超平面是
分類決策函數是
最大間隔法中,函數間隔與幾何間隔是重要的概念。
線性可分支持向量機的最優解存在且唯一。位于間隔邊界上的實例點為支持向量。最優分離超平面由支持向量完全決定。二次規劃問題的對偶問題是
通常,通過求解對偶問題學習線性可分支持向量機,即首先求解對偶問題的最優值
,然后求最優值和,得出分離超平面和分類決策函數。
2.現實中訓練數據是線性可分的情形較少,訓練數據往往是近似線性可分的,這時使用線性支持向量機,或軟間隔支持向量機。線性支持向量機是最基本的支持向量機。
對于噪聲或例外,通過引入松弛變量,使其“可分”,得到線性支持向量機學習的凸二次規劃問題,其原始最優化問題是
求解原始最優化問題的解和,得到線性支持向量機,其分離超平面為
分類決策函數為
線性可分支持向量機的解唯一但不唯一。對偶問題是
線性支持向量機的對偶學習算法,首先求解對偶問題得到最優解,然后求原始問題最優解和,得出分離超平面和分類決策函數。
對偶問題的解中滿的實例點稱為支持向量。支持向量可在間隔邊界上,也可在間隔邊界與分離超平面之間,或者在分離超平面誤分一側。最優分離超平面由支持向量完全決定。
線性支持向量機學習等價于最小化二階范數正則化的合頁函數
3.非線性支持向量機
對于輸入空間中的非線性分類問題,可以通過非線性變換將它轉化為某個高維特征空間中的線性分類問題,在高維特征空間中學習線性支持向量機。由于在線性支持向量機學習的對偶問題里,目標函數和分類決策函數都只涉及實例與實例之間的內積,所以不需要顯式地指定非線性變換,而是用核函數來替換當中的內積。核函數表示,通過一個非線性轉換后的兩個實例間的內積。具體地,是一個核函數,或正定核,意味著存在一個從輸入空間 x 到特征空間的映射,對任意,有
對稱函數
,任意正整數,對稱函數對應的 Gram 矩陣是半正定的。所以,在線性支持向量機學習的對偶問題中,用核函數替代內積,求解得到的就是非線性支持向量機
4.SMO 算法
SMO 算法是支持向量機學習的一種快速算法,其特點是不斷地將原二次規劃問題分解為只有兩個變量的二次規劃子問題,并對子問題進行解析求解,直到所有變量滿足 KKT 條件為止。這樣通過啟發式的方法得到原二次規劃問題的最優解。因為子問題有解析解,所以每次計算子問題都很快,雖然計算子問題次數很多,但在總體上還是高效的。
分離超平面:
點到直線距離:
為 2-范數:
直線為超平面,樣本可表示為:
margin:
函數間隔:
幾何間隔:,當數據被正確分類時,幾何間隔就是點到超平面的距離
為了求幾何間隔最大,SVM 基本問題可以轉化為求解:(為幾何間隔,(為函數間隔)
分類點幾何間隔最大,同時被正確分類。但這個方程并非凸函數求解,所以要先 ① 將方程轉化為凸函數,② 用拉格朗日乘子法和 KKT 條件求解對偶問題。
① 轉化為凸函數:
先令,方便計算(參照衡量,不影響評價結果)
再將轉化成求解凸函數,1/2 是為了求導之后方便計算。
② 用拉格朗日乘子法和 KKT 條件求解最優值:
整合成:
推導:
根據 KKT 條件:
代入
再把 max 問題轉成 min 問題:
以上為 SVM 對偶問題的對偶形式
kernel
在低維空間計算獲得高維空間的計算結果,也就是說計算結果滿足高維(滿足高維,才能說明高維下線性可分)。
soft margin & slack variable
引入松弛變量,對應數據點允許偏離的 functional margin 的量。
目標函數:
對偶問題:
Sequential Minimal Optimization
首先定義特征到結果的輸出函數:.
因為
有
參考資料:
[1] :Lagrange Multiplier and KKT[7]
[2] :推導 SVM[8]
[3] :機器學習算法實踐-支持向量機(SVM)算法原理[9]
[4] :Python 實現 SVM[10]
import numpy as np import pandas as pd from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split import matplotlib.pyplot as plt %matplotlib inline # data def create_data():iris = load_iris()df = pd.DataFrame(iris.data, columns=iris.feature_names)df['label'] = iris.targetdf.columns = ['sepal length', 'sepal width', 'petal length', 'petal width', 'label']data = np.array(df.iloc[:100, [0, 1, -1]])for i in range(len(data)):if data[i, -1] == 0:data[i, -1] = -1# print(data)return data[:, :2], data[:, -1] X, y = create_data() X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25) plt.scatter(X[:50,0],X[:50,1], label='0') plt.scatter(X[50:,0],X[50:,1], label='1') plt.legend()class SVM:def __init__(self, max_iter=100, kernel='linear'):self.max_iter = max_iterself._kernel = kerneldef init_args(self, features, labels):self.m, self.n = features.shapeself.X = featuresself.Y = labelsself.b = 0.0# 將Ei保存在一個列表里self.alpha = np.ones(self.m)self.E = [self._E(i) for i in range(self.m)]# 松弛變量self.C = 1.0def _KKT(self, i):y_g = self._g(i) * self.Y[i]if self.alpha[i] == 0:return y_g >= 1elif 0 < self.alpha[i] < self.C:return y_g == 1else:return y_g <= 1# g(x)預測值,輸入xi(X[i])def _g(self, i):r = self.bfor j in range(self.m):r += self.alpha[j] * self.Y[j] * self.kernel(self.X[i], self.X[j])return r# 核函數def kernel(self, x1, x2):if self._kernel == 'linear':return sum([x1[k] * x2[k] for k in range(self.n)])elif self._kernel == 'poly':return (sum([x1[k] * x2[k] for k in range(self.n)]) + 1)**2return 0# E(x)為g(x)對輸入x的預測值和y的差def _E(self, i):return self._g(i) - self.Y[i]def _init_alpha(self):# 外層循環首先遍歷所有滿足0<a<C的樣本點,檢驗是否滿足KKTindex_list = [i for i in range(self.m) if 0 < self.alpha[i] < self.C]# 否則遍歷整個訓練集non_satisfy_list = [i for i in range(self.m) if i not in index_list]index_list.extend(non_satisfy_list)for i in index_list:if self._KKT(i):continueE1 = self.E[i]# 如果E2是+,選擇最小的;如果E2是負的,選擇最大的if E1 >= 0:j = min(range(self.m), key=lambda x: self.E[x])else:j = max(range(self.m), key=lambda x: self.E[x])return i, jdef _compare(self, _alpha, L, H):if _alpha > H:return Helif _alpha < L:return Lelse:return _alphadef fit(self, features, labels):self.init_args(features, labels)for t in range(self.max_iter):# traini1, i2 = self._init_alpha()# 邊界if self.Y[i1] == self.Y[i2]:L = max(0, self.alpha[i1] + self.alpha[i2] - self.C)H = min(self.C, self.alpha[i1] + self.alpha[i2])else:L = max(0, self.alpha[i2] - self.alpha[i1])H = min(self.C, self.C + self.alpha[i2] - self.alpha[i1])E1 = self.E[i1]E2 = self.E[i2]# eta=K11+K22-2K12eta = self.kernel(self.X[i1], self.X[i1]) + self.kernel(self.X[i2],self.X[i2]) - 2 * self.kernel(self.X[i1], self.X[i2])if eta <= 0:# print('eta <= 0')continuealpha2_new_unc = self.alpha[i2] + self.Y[i2] * (E1 - E2) / eta #此處有修改,根據書上應該是E1 - E2,書上130-131頁alpha2_new = self._compare(alpha2_new_unc, L, H)alpha1_new = self.alpha[i1] + self.Y[i1] * self.Y[i2] * (self.alpha[i2] - alpha2_new)b1_new = -E1 - self.Y[i1] * self.kernel(self.X[i1], self.X[i1]) * (alpha1_new - self.alpha[i1]) - self.Y[i2] * self.kernel(self.X[i2],self.X[i1]) * (alpha2_new - self.alpha[i2]) + self.bb2_new = -E2 - self.Y[i1] * self.kernel(self.X[i1], self.X[i2]) * (alpha1_new - self.alpha[i1]) - self.Y[i2] * self.kernel(self.X[i2],self.X[i2]) * (alpha2_new - self.alpha[i2]) + self.bif 0 < alpha1_new < self.C:b_new = b1_newelif 0 < alpha2_new < self.C:b_new = b2_newelse:# 選擇中點b_new = (b1_new + b2_new) / 2# 更新參數self.alpha[i1] = alpha1_newself.alpha[i2] = alpha2_newself.b = b_newself.E[i1] = self._E(i1)self.E[i2] = self._E(i2)return 'train done!'def predict(self, data):r = self.bfor i in range(self.m):r += self.alpha[i] * self.Y[i] * self.kernel(data, self.X[i])return 1 if r > 0 else -1def score(self, X_test, y_test):right_count = 0for i in range(len(X_test)):result = self.predict(X_test[i])if result == y_test[i]:right_count += 1return right_count / len(X_test)def _weight(self):# linear modelyx = self.Y.reshape(-1, 1) * self.Xself.w = np.dot(yx.T, self.alpha)return self.w svm = SVM(max_iter=200) svm.fit(X_train, y_train) 'train done!' svm.score(X_test, y_test)0.92
scikit-learn 實例
from sklearn.svm import SVC clf = SVC() clf.fit(X_train, y_train)SVC(C=1.0, cache_size=200, class_weight=None, coef0=0.0,decision_function_shape='ovr', degree=3, gamma='auto', kernel='rbf',max_iter=-1, probability=False, random_state=None, shrinking=True,tol=0.001, verbose=False) clf.score(X_test, y_test)0.96sklearn.svm.SVC
(C=1.0, kernel='rbf', degree=3, gamma='auto', coef0=0.0, shrinking=True, probability=False,tol=0.001, cache_size=200, class_weight=None, verbose=False, max_iter=-1, decision_function_shape=None,random_state=None)
參數:
C:C-SVC 的懲罰參數 C?默認值是 1.0
C 越大,相當于懲罰松弛變量,希望松弛變量接近 0,即對誤分類的懲罰增大,趨向于對訓練集全分對的情況,這樣對訓練集測試時準確率很高,但泛化能力弱。C 值小,對誤分類的懲罰減小,允許容錯,將他們當成噪聲點,泛化能力較強。
kernel :核函數,默認是 rbf,可以是‘linear’, ‘poly’, ‘rbf’, ‘sigmoid’, ‘precomputed’
– 線性:u'v
– 多項式:(gamma*u'*v + coef0)^degree
– RBF 函數:exp(-gamma|u-v|^2)
– sigmoid:tanh(gamma*u'*v + coef0)
degree :多項式 poly 函數的維度,默認是 3,選擇其他核函數時會被忽略。
gamma :‘rbf’,‘poly’ 和‘sigmoid’的核函數參數。默認是’auto’,則會選擇 1/n_features
coef0 :核函數的常數項。對于‘poly’和 ‘sigmoid’有用。
probability :是否采用概率估計?.默認為 False
shrinking :是否采用 shrinking heuristic 方法,默認為 true
tol :停止訓練的誤差值大小,默認為 1e-3
cache_size :核函數 cache 緩存大小,默認為 200
class_weight :類別的權重,字典形式傳遞。設置第幾類的參數 C 為 weight*C(C-SVC 中的 C)
verbose :允許冗余輸出?
max_iter :最大迭代次數。-1 為無限制。
decision_function_shape :‘ovo’, ‘ovr’ or None, default=None3
random_state :數據洗牌時的種子值,int 值
主要調節的參數有:C、kernel、degree、gamma、coef0。
參考資料
[1] 《統計學習方法》:?https://baike.baidu.com/item/統計學習方法/10430179
[2] 黃海廣:?https://github.com/fengdu78
[3] github:?https://github.com/fengdu78/lihang-code
[4] wzyonggege:?https://github.com/wzyonggege/statistical-learning-method
[5] WenDesi:?https://github.com/WenDesi/lihang_book_algorithm
[6] 火燙火燙的:?https://blog.csdn.net/tudaodiaozhale
[7]:[Lagrange Multiplier and KKT: http://blog.csdn.net/xianlingmao/article/details/7919597
[8]:[推導SVM: https://my.oschina.net/dfsj66011/blog/517766
[9]:[機器學習算法實踐-支持向量機(SVM)算法原理: http://pytlab.org/2017/08/15/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E7%AE%97%E6%B3%95%E5%AE%9E%E8%B7%B5-%E6%94%AF%E6%8C%81%E5%90%91%E9%87%8F%E6%9C%BA-SVM-%E7%AE%97%E6%B3%95%E5%8E%9F%E7%90%86/
[10]?:[Python實現SVM: http://blog.csdn.net/wds2006sdo/article/details/53156589
往期精彩回顧
那些年做的學術公益-你不是一個人在戰斗
適合初學者入門人工智能的路線及資料下載
吳恩達機器學習課程筆記及資源(github標星12000+,提供百度云鏡像)
吳恩達深度學習筆記及視頻等資源(github標星8500+,提供百度云鏡像)
《統計學習方法》的python代碼實現(github標星7200+)
機器學習的數學精華(在線閱讀版)
備注:加入本站微信群或者qq群,請回復“加群”
加入知識星球(4300+用戶,ID:92416895),請回復“知識星球”
總結
以上是生活随笔為你收集整理的复现经典:《统计学习方法》第 7 章 支持向量机的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 复现经典:《统计学习方法》第 6 章 逻
- 下一篇: 附笔记pdf下载,MIT中文线性代数课程