Jacob的 Python機(jī)器學(xué)習(xí)系列:
Python機(jī)器學(xué)習(xí)(一):kNN算法
Python機(jī)器學(xué)習(xí)(二):線性回歸算法
Python機(jī)器學(xué)習(xí)(三):梯度下降法
Python機(jī)器學(xué)習(xí)(四):PCA 主成分分析
Python機(jī)器學(xué)習(xí)(五):SVM 支撐向量機(jī)
這個(gè)系列拖了好久烫扼,當(dāng)然這段時(shí)間也不算荒廢吧塞关,主要是考試和各種課程設(shè)計(jì)的緣故茴扁,也接了一些小項(xiàng)目秒啦,所以機(jī)器學(xué)習(xí)這里就落下來了。現(xiàn)在基本所有事情都搞定了冈敛,所以一方面要趕緊把這個(gè)系列完結(jié)了贴唇,另一方面這個(gè)漫長假期(學(xué)校給我們放了兩個(gè)月的假丽柿。。蔗包。)也要給自己立一個(gè)大flag秉扑,趕緊把自己的能力提上去,畢竟下學(xué)期就要找實(shí)習(xí)了调限。OK舟陆,日常嘮叨完畢。
支撐向量機(jī)(Support Vector Machin耻矮,SVM)可以解決分類問題秦躯,也可以用于解決回歸問題。這里僅對分類問題進(jìn)行一些粗淺的討論淘钟。
感性理解
在分類問題中宦赠,分類算法會將數(shù)據(jù)空間劃分一個(gè)或多個(gè)決策邊界(高維時(shí)稱為超平面,為了簡化問題米母,這里僅對兩個(gè)特征的二分類問題進(jìn)行討論)勾扭,決策邊界的一邊是一類,另一邊是另一類铁瞒。了解邏輯回歸的應(yīng)該都能理解妙色。但是,不同的分類算法會對相同的數(shù)據(jù)生成不同的決策邊界慧耍。那么身辨,哪個(gè)決策邊界才是最好的呢丐谋?這個(gè)問題稱為不適定問題對于SVM來說,它的目的就是盡可能地找到一個(gè)合適的邊界煌珊,來提高算法的泛化能力号俐,即魯棒性。
那么定庵,SVM的具體操作是:尋找一個(gè)最優(yōu)的決策邊界吏饿,距離兩個(gè)類別的最近的樣本最遠(yuǎn)。最近的樣本點(diǎn)稱為支撐向量蔬浙。當(dāng)然猪落,這里討論的是線性可分的問題。當(dāng)線性不可分的時(shí)候畴博,有改進(jìn)的方法笨忌,下文中會提到。
Hard Margin SVM
上面的圖中俱病,樣本點(diǎn)是線性可分的官疲,即可以找到一條決策邊界使得分類不會出錯(cuò),這時(shí)候使用的SVM算法稱為Hard Magin SVM庶艾。此時(shí)的算法至少在訓(xùn)練集上是不會發(fā)生錯(cuò)誤的袁余。
感性理解之后就需要用數(shù)學(xué)語言來表達(dá)了≡圩幔回憶一下高中學(xué)的解析幾何颖榜,直線的一般式方程為
點(diǎn)到直線的距離為
那么拓展到n維空間中的話,可以寫成這種形式
同樣煤裙,距離公式為
我們定義類A的值為1掩完,類B的值為-1。在圖中可以看到硼砰,類A的支撐向量距離決策邊界的距離為d且蓬,類B上的支撐向量距離決策邊界的距離也為d。那么可以列寫這樣的方程
因?yàn)橄蛄?w 的模题翰,即方程左邊的分母為常數(shù)恶阴,距離 d 也為常數(shù),可以進(jìn)行這樣的簡化豹障,即 d 除過去并且換個(gè)符號冯事。下標(biāo)d說明截距和向量 w 已經(jīng)被 d 和 w 的模相除。
因?yàn)槎x了類A的值為1血公,類B的值為-1昵仅,那么可以寫成一個(gè)式子
對于所有支撐向量,滿足這樣的方程
即求模的最小值。實(shí)際工程中為了求導(dǎo)得到最小值摔笤,使用的是第二個(gè)式子
所以總結(jié)下就是這個(gè)目標(biāo)够滑。下方的式子是求解條件
Soft Margin SVM
如果數(shù)據(jù)線性不可分,那么如果要使用SVM算法吕世,就需要允許分類算法犯一定的錯(cuò)誤彰触,具體方法是讓限定條件變得寬松一點(diǎn)
那么隨之,求解目標(biāo)也會有所改變命辖。C是一個(gè)超參數(shù)渴析。C趨向于0的時(shí)候,允許無限大的誤差吮龄,趨向于無窮大的時(shí)候,算法本質(zhì)就是Hard Margin SVM咆疗。
編程實(shí)現(xiàn)
要自己實(shí)現(xiàn)SVM比較麻煩漓帚,這里只用sciki-learn中提供的函數(shù)去實(shí)現(xiàn)。
"""
Created by 楊幫杰 on 12/24/2018
Right to use this code in any way you want without
warranty, support or any guarantee of it working
E-mail: yangbangjie1998@qq.com
Association: SCAU 華南農(nóng)業(yè)大學(xué)
"""
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.preprocessing import StandardScaler
from sklearn.svm import LinearSVC
from matplotlib.colors import ListedColormap
# 使用鳶尾花數(shù)據(jù)集
iris = datasets.load_iris()
X = iris.data
y = iris.target
# 只使用兩個(gè)特征和兩個(gè)種類
X = X[y < 2, :2]
y = y[y < 2]
# 歸一化尺度
standardScaler = StandardScaler()
standardScaler.fit(X)
X_standard = standardScaler.transform(X)
# hard margin SVM
svc = LinearSVC(C=1e9)
svc.fit(X_standard, 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)
custom_cmap = ListedColormap(['#EF9A9A', '#FFF59D', '#90CAF9'])
plt.contourf(x0, x1, zz, linewidth=5, cmap=custom_cmap)
# 畫出svm決策邊界
def plot_svc_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)
custom_cmap = ListedColormap(['#EF9A9A', '#FFF59D', '#90CAF9'])
plt.contourf(x0, x1, zz, linewidth=5, cmap=custom_cmap)
w = model.coef_[0]
b = model.intercept_[0]
# w0 * x0 + w1 * x1 + b = 0
# => x1 = -w0/w1 * x0 - b/w1
plot_x = np.linspace(axis[0], axis[1], 200)
up_y = -w[0]/w[1] * plot_x - b/w[1] + 1/w[1]
down_y = -w[0]/w[1] * plot_x - b/w[1] - 1/w[1]
up_index = (up_y >= axis[2]) & (up_y <= axis[3])
down_index = (down_y >= axis[2]) & (down_y <= axis[3])
plt.plot(plot_x[up_index], up_y[up_index], color="black")
plt.plot(plot_x[down_index], down_y[down_index], color="black")
# 顯示數(shù)據(jù)和分類情況
plot_decision_boundary(svc, axis=[-3, 3, -3, 3])
plt.scatter(X_standard[y == 0, 0], X_standard[y == 0, 1])
plt.scatter(X_standard[y == 1, 0], X_standard[y == 1, 1])
plt.show()
plot_svc_decision_boundary(svc, axis=[-3, 3, -3, 3])
plt.scatter(X_standard[y == 0, 0], X_standard[y == 0, 1])
plt.scatter(X_standard[y == 1, 0], X_standard[y == 1, 1])
plt.show()
# soft margin SVM
svc2 = LinearSVC(C=0.01)
svc2.fit(X_standard, y)
plot_svc_decision_boundary(svc2, axis=[-3, 3, -3, 3])
plt.scatter(X_standard[y == 0, 0], X_standard[y == 0, 1])
plt.scatter(X_standard[y == 1, 0], X_standard[y == 1, 1])
plt.show()
如果想使用多項(xiàng)式特征的SVM午磁,可以給數(shù)據(jù)添加多項(xiàng)式特征后使用LinearSVC
"""
Created by 楊幫杰 on 12/24/2018
Right to use this code in any way you want without
warranty, support or any guarantee of it working
E-mail: yangbangjie1998@qq.com
Association: SCAU 華南農(nóng)業(yè)大學(xué)
"""
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.svm import LinearSVC
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import PolynomialFeatures, StandardScaler
from matplotlib.colors import ListedColormap
X, y = datasets.make_moons(noise=0.15, random_state=666)
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)
custom_cmap = ListedColormap(['#EF9A9A', '#FFF59D', '#90CAF9'])
plt.contourf(x0, x1, zz, linewidth=5, cmap=custom_cmap)
plot_decision_boundary(poly_svc, axis=[-1.5, 2.5, -1.0, 1.5])
plt.scatter(X[y == 0, 0], X[y == 0, 1])
plt.scatter(X[y == 1, 0], X[y == 1, 1])
plt.show()
在SVM實(shí)現(xiàn)中會使用核函數(shù)尝抖,它的目的是直接計(jì)算出兩者添加多項(xiàng)式特征之后的點(diǎn)乘,加快計(jì)算速度迅皇。并且昧辽,不同的核函數(shù),會對樣本點(diǎn)進(jìn)行不同的轉(zhuǎn)換登颓,得到不同的效果搅荞,比如高斯核函數(shù)(徑向基函數(shù))。這里不進(jìn)行討論框咙,讀者可以自行改變SVC函數(shù)中的kernel參數(shù)來進(jìn)行實(shí)驗(yàn)咕痛。
"""
Created by 楊幫杰 on 12/24/2018
Right to use this code in any way you want without
warranty, support or any guarantee of it working
E-mail: yangbangjie1998@qq.com
Association: SCAU 華南農(nóng)業(yè)大學(xué)
"""
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from matplotlib.colors import ListedColormap
from sklearn.svm import SVC
X, y = datasets.make_moons(noise=0.15, random_state=666)
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)
custom_cmap = ListedColormap(['#EF9A9A', '#FFF59D', '#90CAF9'])
plt.contourf(x0, x1, zz, linewidth=5, cmap=custom_cmap)
def PolynomialKernalSVC(degree, C=1.0):
return Pipeline([
("std_scaler", StandardScaler()),
("kernelSVC", SVC(kernel="poly", degree=degree, C=C))
])
poly_kernel_svc = PolynomialKernalSVC(degree=3)
poly_kernel_svc.fit(X, y)
plot_decision_boundary(poly_kernel_svc, axis=[-1.5, 2.5, -1.0, 1.5])
plt.scatter(X[y == 0, 0], X[y == 0, 1])
plt.scatter(X[y == 1, 0], X[y == 1, 1])
plt.show()
References:
Python3 入門機(jī)器學(xué)習(xí) 經(jīng)典算法與應(yīng)用 —— liuyubobobo
機(jī)器學(xué)習(xí)實(shí)戰(zhàn) —— Peter Harrington