复现经典:《统计学习方法》第 7 章 支持向量机
本文是李航老師的《統(tǒng)計學(xué)習(xí)方法》[1]一書的代碼復(fù)現(xiàn)。
作者:黃海廣[2]
備注:代碼都可以在github[3]中下載。
我將陸續(xù)將代碼發(fā)布在公眾號“機(jī)器學(xué)習(xí)初學(xué)者”,敬請關(guān)注。
代碼目錄
第 1 章 統(tǒng)計學(xué)習(xí)方法概論
第 2 章 感知機(jī)
第 3 章 k 近鄰法
第 4 章 樸素貝葉斯
第 5 章 決策樹
第 6 章 邏輯斯諦回歸
第 7 章 支持向量機(jī)
第 8 章 提升方法
第 9 章 EM 算法及其推廣
第 10 章 隱馬爾可夫模型
第 11 章 條件隨機(jī)場
第 12 章 監(jiān)督學(xué)習(xí)方法總結(jié)
代碼參考:wzyonggege[4],WenDesi[5],火燙火燙的[6]
第 7 章 支持向量機(jī)
1.支持向量機(jī)最簡單的情況是線性可分支持向量機(jī),或硬間隔支持向量機(jī)。構(gòu)建它的條件是訓(xùn)練數(shù)據(jù)線性可分。其學(xué)習(xí)策略是最大間隔法??梢员硎緸橥苟我?guī)劃問題,其原始最優(yōu)化問題為
求得最優(yōu)化問題的解為,,得到線性可分支持向量機(jī),分離超平面是
分類決策函數(shù)是
最大間隔法中,函數(shù)間隔與幾何間隔是重要的概念。
線性可分支持向量機(jī)的最優(yōu)解存在且唯一。位于間隔邊界上的實例點(diǎn)為支持向量。最優(yōu)分離超平面由支持向量完全決定。二次規(guī)劃問題的對偶問題是
通常,通過求解對偶問題學(xué)習(xí)線性可分支持向量機(jī),即首先求解對偶問題的最優(yōu)值
,然后求最優(yōu)值和,得出分離超平面和分類決策函數(shù)。
2.現(xiàn)實中訓(xùn)練數(shù)據(jù)是線性可分的情形較少,訓(xùn)練數(shù)據(jù)往往是近似線性可分的,這時使用線性支持向量機(jī),或軟間隔支持向量機(jī)。線性支持向量機(jī)是最基本的支持向量機(jī)。
對于噪聲或例外,通過引入松弛變量,使其“可分”,得到線性支持向量機(jī)學(xué)習(xí)的凸二次規(guī)劃問題,其原始最優(yōu)化問題是
求解原始最優(yōu)化問題的解和,得到線性支持向量機(jī),其分離超平面為
分類決策函數(shù)為
線性可分支持向量機(jī)的解唯一但不唯一。對偶問題是
線性支持向量機(jī)的對偶學(xué)習(xí)算法,首先求解對偶問題得到最優(yōu)解,然后求原始問題最優(yōu)解和,得出分離超平面和分類決策函數(shù)。
對偶問題的解中滿的實例點(diǎn)稱為支持向量。支持向量可在間隔邊界上,也可在間隔邊界與分離超平面之間,或者在分離超平面誤分一側(cè)。最優(yōu)分離超平面由支持向量完全決定。
線性支持向量機(jī)學(xué)習(xí)等價于最小化二階范數(shù)正則化的合頁函數(shù)
3.非線性支持向量機(jī)
對于輸入空間中的非線性分類問題,可以通過非線性變換將它轉(zhuǎn)化為某個高維特征空間中的線性分類問題,在高維特征空間中學(xué)習(xí)線性支持向量機(jī)。由于在線性支持向量機(jī)學(xué)習(xí)的對偶問題里,目標(biāo)函數(shù)和分類決策函數(shù)都只涉及實例與實例之間的內(nèi)積,所以不需要顯式地指定非線性變換,而是用核函數(shù)來替換當(dāng)中的內(nèi)積。核函數(shù)表示,通過一個非線性轉(zhuǎn)換后的兩個實例間的內(nèi)積。具體地,是一個核函數(shù),或正定核,意味著存在一個從輸入空間 x 到特征空間的映射,對任意,有
對稱函數(shù)
,任意正整數(shù),對稱函數(shù)對應(yīng)的 Gram 矩陣是半正定的。所以,在線性支持向量機(jī)學(xué)習(xí)的對偶問題中,用核函數(shù)替代內(nèi)積,求解得到的就是非線性支持向量機(jī)
4.SMO 算法
SMO 算法是支持向量機(jī)學(xué)習(xí)的一種快速算法,其特點(diǎn)是不斷地將原二次規(guī)劃問題分解為只有兩個變量的二次規(guī)劃子問題,并對子問題進(jìn)行解析求解,直到所有變量滿足 KKT 條件為止。這樣通過啟發(fā)式的方法得到原二次規(guī)劃問題的最優(yōu)解。因為子問題有解析解,所以每次計算子問題都很快,雖然計算子問題次數(shù)很多,但在總體上還是高效的。
分離超平面:
點(diǎn)到直線距離:
為 2-范數(shù):
直線為超平面,樣本可表示為:
margin:
函數(shù)間隔:
幾何間隔:,當(dāng)數(shù)據(jù)被正確分類時,幾何間隔就是點(diǎn)到超平面的距離
為了求幾何間隔最大,SVM 基本問題可以轉(zhuǎn)化為求解:(為幾何間隔,(為函數(shù)間隔)
分類點(diǎn)幾何間隔最大,同時被正確分類。但這個方程并非凸函數(shù)求解,所以要先 ① 將方程轉(zhuǎn)化為凸函數(shù),② 用拉格朗日乘子法和 KKT 條件求解對偶問題。
① 轉(zhuǎn)化為凸函數(shù):
先令,方便計算(參照衡量,不影響評價結(jié)果)
再將轉(zhuǎn)化成求解凸函數(shù),1/2 是為了求導(dǎo)之后方便計算。
② 用拉格朗日乘子法和 KKT 條件求解最優(yōu)值:
整合成:
推導(dǎo):
根據(jù) KKT 條件:
代入
再把 max 問題轉(zhuǎn)成 min 問題:
以上為 SVM 對偶問題的對偶形式
kernel
在低維空間計算獲得高維空間的計算結(jié)果,也就是說計算結(jié)果滿足高維(滿足高維,才能說明高維下線性可分)。
soft margin & slack variable
引入松弛變量,對應(yīng)數(shù)據(jù)點(diǎn)允許偏離的 functional margin 的量。
目標(biāo)函數(shù):
對偶問題:
Sequential Minimal Optimization
首先定義特征到結(jié)果的輸出函數(shù):.
因為
有
參考資料:
[1] :Lagrange Multiplier and KKT[7]
[2] :推導(dǎo) SVM[8]
[3] :機(jī)器學(xué)習(xí)算法實踐-支持向量機(jī)(SVM)算法原理[9]
[4] :Python 實現(xiàn) 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)預(yù)測值,輸入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# 核函數(shù)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ù)測值和y的差def _E(self, i):return self._g(i) - self.Y[i]def _init_alpha(self):# 外層循環(huán)首先遍歷所有滿足0<a<C的樣本點(diǎn),檢驗是否滿足KKTindex_list = [i for i in range(self.m) if 0 < self.alpha[i] < self.C]# 否則遍歷整個訓(xùn)練集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是負(fù)的,選擇最大的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 #此處有修改,根據(jù)書上應(yīng)該是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:# 選擇中點(diǎn)b_new = (b1_new + b2_new) / 2# 更新參數(shù)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)
參數(shù):
C:C-SVC 的懲罰參數(shù) C?默認(rèn)值是 1.0
C 越大,相當(dāng)于懲罰松弛變量,希望松弛變量接近 0,即對誤分類的懲罰增大,趨向于對訓(xùn)練集全分對的情況,這樣對訓(xùn)練集測試時準(zhǔn)確率很高,但泛化能力弱。C 值小,對誤分類的懲罰減小,允許容錯,將他們當(dāng)成噪聲點(diǎn),泛化能力較強(qiáng)。
kernel :核函數(shù),默認(rèn)是 rbf,可以是‘linear’, ‘poly’, ‘rbf’, ‘sigmoid’, ‘precomputed’
– 線性:u'v
– 多項式:(gamma*u'*v + coef0)^degree
– RBF 函數(shù):exp(-gamma|u-v|^2)
– sigmoid:tanh(gamma*u'*v + coef0)
degree :多項式 poly 函數(shù)的維度,默認(rèn)是 3,選擇其他核函數(shù)時會被忽略。
gamma :‘rbf’,‘poly’ 和‘sigmoid’的核函數(shù)參數(shù)。默認(rèn)是’auto’,則會選擇 1/n_features
coef0 :核函數(shù)的常數(shù)項。對于‘poly’和 ‘sigmoid’有用。
probability :是否采用概率估計?.默認(rèn)為 False
shrinking :是否采用 shrinking heuristic 方法,默認(rèn)為 true
tol :停止訓(xùn)練的誤差值大小,默認(rèn)為 1e-3
cache_size :核函數(shù) cache 緩存大小,默認(rèn)為 200
class_weight :類別的權(quán)重,字典形式傳遞。設(shè)置第幾類的參數(shù) C 為 weight*C(C-SVC 中的 C)
verbose :允許冗余輸出?
max_iter :最大迭代次數(shù)。-1 為無限制。
decision_function_shape :‘ovo’, ‘ovr’ or None, default=None3
random_state :數(shù)據(jù)洗牌時的種子值,int 值
主要調(diào)節(jié)的參數(shù)有:C、kernel、degree、gamma、coef0。
參考資料
[1] 《統(tǒng)計學(xué)習(xí)方法》:?https://baike.baidu.com/item/統(tǒng)計學(xué)習(xí)方法/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]:[推導(dǎo)SVM: https://my.oschina.net/dfsj66011/blog/517766
[9]:[機(jī)器學(xué)習(xí)算法實踐-支持向量機(jī)(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實現(xiàn)SVM: http://blog.csdn.net/wds2006sdo/article/details/53156589
往期精彩回顧
那些年做的學(xué)術(shù)公益-你不是一個人在戰(zhàn)斗
適合初學(xué)者入門人工智能的路線及資料下載
吳恩達(dá)機(jī)器學(xué)習(xí)課程筆記及資源(github標(biāo)星12000+,提供百度云鏡像)
吳恩達(dá)深度學(xué)習(xí)筆記及視頻等資源(github標(biāo)星8500+,提供百度云鏡像)
《統(tǒng)計學(xué)習(xí)方法》的python代碼實現(xiàn)(github標(biāo)星7200+)
機(jī)器學(xué)習(xí)的數(shù)學(xué)精華(在線閱讀版)
備注:加入本站微信群或者qq群,請回復(fù)“加群”
加入知識星球(4300+用戶,ID:92416895),請回復(fù)“知識星球”
總結(jié)
以上是生活随笔為你收集整理的复现经典:《统计学习方法》第 7 章 支持向量机的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 复现经典:《统计学习方法》第 6 章 逻
- 下一篇: 附笔记pdf下载,MIT中文线性代数课程