机器学习—支撑向量机SVM
10.支撑向量机SVM
10.1支撑向量机SVM的实现
10.1.1什么是支撑向量机
支撑向量机的英文全称是Support Vector Machine,其思想可以解决分类问题,也可以解决回归问题。
对于二分类问题,我们需要找到一条直线,将标签为0的样本划分在直线的一侧,标签为1的样本划分在直线的另一侧,这条直线称为“线性分类器”。对于训练数据集,其线性分类器有无数条,如何找到一条泛化性能最好的线性分类器呢?
解决思想:
(1)逻辑回归
通过定义一个概率函数sigmoid,当概率p >= 0.5,其预测标签值为1;当概率p <= 0.5,其预测标签值为0。根据这样的变化关系得到了损失函数J,通过求损失函数的极小值从而得到一条决策边界。
(2)支撑向量机
为了得到好的泛化性能,线性分类器不应偏向任何一类样本,即离两个类的样本尽可能的远。SVM的目标就是寻找一个最优的决策边界,其距离两个类别的最近样本最远,如下所示。
图中,红色和蓝色样本有3个样本离分类直线最近,这3个样本称为支撑向量。把同一类型的最近样本连接起来形成两条平行直线,分类直线位于其中间;直线之间的距离称为margin。
SVM的目标就是要最大化margin。对于线性可分的问题,使用Hard Margin SVM;对于线性不可分的问题,使用Soft Margin SVM。
10.1.2 Hard Margin SVM实现思想
对于线性可分的问题,支撑向量机的目标是寻找一个分类的超平面,它不仅能正确的分类每一个样本,并且要使每一类样本中距离超平面最近的样本到超平面的距离最远。
在n维空间中,,其中
在支撑向量机中,换一种表示形式:
其中,
因此,决策边界(超平面)为wTx+b=0。
根据点到平面的距离公式,得出:
由于margin=2d,因此最大化margin就是最大化d。
在支撑向量机中,我们把标签值取为+1和-1,分别对应正样本和负样本。通过d可以得出:
->
由于超平面参数进行缩放变换,还是原来的超平面,由上式得出:
->
如下图所示:
对于开始需要求的,即求
->
总结:对于线性可分的问题,Hard Margin SVM的原理是在条件下,求出
。对于有条件的优化问题,可以使用拉格朗日对偶将其转换为对偶问题,再进行求解,
由于过程过于复杂,这里不再具体推导。
10.1.3 Soft Margin SVM实现思想
表示所有的样本都在
的外面,若有一个样本在两条直线的中间,则无法求解。
因此,我们需要给条件增加一个松弛变量ξ,即:
为了控制ξ的大小,在中加一个L1正则项,即:
若加L2正则项,则表示为
对于线性不可分的问题,就是使用松弛变量ξ和惩罚因子C对违反不等式约束的样本进行惩罚。
总结:s.t. ,ξ>=0;求
10.1.4使用sklearn来实现SVM
#程序10-1:Hard Margin
import numpy as np
from sklearn import datasets
iris = datasets.load_iris()
X = iris.data
y = iris.target
#去鸢尾花的前2个类别,前2个特征
X = X[y<2,:2]
y = y[y<2]
from sklearn.preprocessing import StandardScaler
#由于SVM需要求距离,因此需要标准化
std_sca = StandardScaler()
std_sca.fit(X)
X_std = std_sca.transform(X)
#LinearSVC表示分类问题
from sklearn.svm import LinearSVC
#Hard Margin
Lin_svc = LinearSVC(C=1e8)
Lin_svc.fit(X_std,y)
import matplotlib as mpl
import matplotlib.pyplot as plt
def plot_decision_boundary(clf, X, y, num_row=100, num_col=100):
"""
绘制决策边界
:param clf: 分类器, 即训练后的模型
:param X: 输入的训练数据X
:param y: 输入真实的训练数据y
:param num_row: 单位行数据生成的基本个数
:param num_col: 单位列数据生成的基本个数
"""
#以训练数据集X的第一个特征作为x轴,第二个特征作为y轴,生成坐标轴的间距
sigma = 1 #防止数据在图形的边上而加上的一个偏移量,设定一个较小的值即可
x_min, x_max = np.min(X[:, 0])-sigma, np.max(X[:, 0])+sigma
y_min, y_max = np.min(X[:, 1])-sigma, np.max(X[:, 1])+sigma
#对间距进行等分成t1、t2,并t1按照t2的列数进行行变换、t2按照t1的行数进行列变换
t1 = np.linspace(x_min, x_max, int(x_max-x_min)*num_row).reshape(-1,1)
t2 = np.linspace(y_min, y_max, int(y_max-y_min)*num_col).reshape(-1,1)
x_copy, y_copy = np.meshgrid(t1, t2)
#将变换后的x_,y_生成坐标轴的每个点
xy_all = np.stack((x_copy.reshape(-1,), y_copy.reshape(-1,)), axis=1)
#对坐标轴的点进行预测,并将预测结果变换为对应点的结果
y_predict = clf.predict(xy_all)
y_predict = y_predict.reshape(x_copy.shape)
#设置使用的颜色colors
cm_dark = mpl.colors.ListedColormap(['#EF9A9A','#FFF59D','#90CAF9'])
#绘制等高线,x_copy和y_copy种对应的点
#若y_predict为0绘制#FFA0A0,若y_predict为1绘制#A0A0FF,等高线绘制#A0FFA0
plt.contourf(x_copy, y_copy, y_predict,cmap=cm_dark) #绘制底色
plot_decision_boundary(Lin_svc,X_std,y)
plt.scatter(X_std[y==0,0],X_std[y==0,1])
plt.scatter(X_std[y==1,0],X_std[y==1,1])
plt.show()
运行结果:
#程序10-2:Soft Margin
import numpy as np
from sklearn import datasets
iris = datasets.load_iris()
X = iris.data
y = iris.target
#去鸢尾花的前2个类别,前2个特征
X = X[y<2,:2]
y = y[y<2]
from sklearn.preprocessing import StandardScaler
#由于SVM需要求距离,因此需要标准化
std_sca = StandardScaler()
std_sca.fit(X)
X_std = std_sca.transform(X)
#LinearSVC表示分类问题
from sklearn.svm import LinearSVC
#Hard Margin
Lin_svc = LinearSVC(C=0.01)
Lin_svc.fit(X_std,y)
import matplotlib as mpl
import matplotlib.pyplot as plt
def plot_decision_boundary(clf, X, y, num_row=100, num_col=100):
"""
绘制决策边界
:param clf: 分类器, 即训练后的模型
:param X: 输入的训练数据X
:param y: 输入真实的训练数据y
:param num_row: 单位行数据生成的基本个数
:param num_col: 单位列数据生成的基本个数
"""
#以训练数据集X的第一个特征作为x轴,第二个特征作为y轴,生成坐标轴的间距
sigma = 1 #防止数据在图形的边上而加上的一个偏移量,设定一个较小的值即可
x_min, x_max = np.min(X[:, 0])-sigma, np.max(X[:, 0])+sigma
y_min, y_max = np.min(X[:, 1])-sigma, np.max(X[:, 1])+sigma
#对间距进行等分成t1、t2,并t1按照t2的列数进行行变换、t2按照t1的行数进行列变换
t1 = np.linspace(x_min, x_max, int(x_max-x_min)*num_row).reshape(-1,1)
t2 = np.linspace(y_min, y_max, int(y_max-y_min)*num_col).reshape(-1,1)
x_copy, y_copy = np.meshgrid(t1, t2)
#将变换后的x_,y_生成坐标轴的每个点
xy_all = np.stack((x_copy.reshape(-1,), y_copy.reshape(-1,)), axis=1)
#对坐标轴的点进行预测,并将预测结果变换为对应点的结果
y_predict = clf.predict(xy_all)
y_predict = y_predict.reshape(x_copy.shape)
#设置使用的颜色colors
cm_dark = mpl.colors.ListedColormap(['#EF9A9A','#FFF59D','#90CAF9'])
#绘制等高线,x_copy和y_copy种对应的点
#若y_predict为0绘制#FFA0A0,若y_predict为1绘制#A0A0FF,等高线绘制#A0FFA0
plt.contourf(x_copy, y_copy, y_predict,cmap=cm_dark) #绘制底色
plot_decision_boundary(Lin_svc,X_std,y)
plt.scatter(X_std[y==0,0],X_std[y==0,1])
plt.scatter(X_std[y==1,0],X_std[y==1,1])
plt.show()
运行结果:
C越小,松弛变量ξ就越大,模型就存在一定的容错率。
10.1.5 SVM和多项式特征
#程序10-3
import numpy as np
from sklearn import datasets
X,y = datasets.make_moons(noise=0.1,random_state=123456)
import matplotlib as mpl
import matplotlib.pyplot as plt
def plot_decision_boundary(clf, X, y, num_row=100, num_col=100):
"""
绘制决策边界
:param clf: 分类器, 即训练后的模型
:param X: 输入的训练数据X
:param y: 输入真实的训练数据y
:param num_row: 单位行数据生成的基本个数
:param num_col: 单位列数据生成的基本个数
"""
#以训练数据集X的第一个特征作为x轴,第二个特征作为y轴,生成坐标轴的间距
sigma = 1 #防止数据在图形的边上而加上的一个偏移量,设定一个较小的值即可
x_min, x_max = np.min(X[:, 0])-sigma, np.max(X[:, 0])+sigma
y_min, y_max = np.min(X[:, 1])-sigma, np.max(X[:, 1])+sigma
#对间距进行等分成t1、t2,并t1按照t2的列数进行行变换、t2按照t1的行数进行列变换
t1 = np.linspace(x_min, x_max, int(x_max-x_min)*num_row).reshape(-1,1)
t2 = np.linspace(y_min, y_max, int(y_max-y_min)*num_col).reshape(-1,1)
x_copy, y_copy = np.meshgrid(t1, t2)
#将变换后的x_,y_生成坐标轴的每个点
xy_all = np.stack((x_copy.reshape(-1,), y_copy.reshape(-1,)), axis=1)
#对坐标轴的点进行预测,并将预测结果变换为对应点的结果
y_predict = clf.predict(xy_all)
y_predict = y_predict.reshape(x_copy.shape)
#设置使用的颜色colors
cm_dark = mpl.colors.ListedColormap(['#EF9A9A','#FFF59D','#90CAF9'])
#绘制等高线,x_copy和y_copy种对应的点
#若y_predict为0绘制#FFA0A0,若y_predict为1绘制#A0A0FF,等高线绘制#A0FFA0
plt.contourf(x_copy, y_copy, y_predict,cmap=cm_dark) #绘制底色
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler,PolynomialFeatures
from sklearn.svm import LinearSVC
def polynomia_linearSVC(degree,C=1.0,max_iter=1000):
return Pipeline([
('pol_fea',PolynomialFeatures(degree=degree)),
('std_sca',StandardScaler()),
('lin_svc',LinearSVC(C=C,max_iter=max_iter))
])
pol_linsvc = polynomia_linearSVC(3,C=100,max_iter=1e6)
pol_linsvc.fit(X,y)
plot_decision_boundary(pol_linsvc,X,y)
plt.scatter(X[y==0,0],X[y==0,1])
plt.scatter(X[y==1,0],X[y==1,1])
plt.show()
运行结果:
10.2核函数
10.2.1什么是核函数
若样本是线性不可分的,可以对样本的特征向量映射到更高维的空间,使得它在该空间中线性可分。如下所示:
=>
这种方法在机器学习中称为核技巧,核映射φ将特征向量变换到更高维的空间,即φ(x)。
核函数表示将两个向量的内积转化为高维向量的内积,即:
核函数是先对向量求内积,再使用函数K进行变换;等价于先对向量进行核映射(升维),然后求内积。使用核函数的方式有效的简化了问题的求解。因此,核函数分为线性核、多项式核、高斯核、sigmoid核等。
在前面的线性不可分问题中,虽然加入了松弛变量和惩罚因子来处理线性不可分问题,但其本质上还是线性分类器,只是允许一定的容错率。核函数可以使支持向量机称为非线性分类器,决策边界不再是线性的超平面,而是形状复杂的曲面。
在线性不可分问题中,使用拉格朗日对偶将其转换为对偶问题:
其中,α、β为拉格朗日乘子。在求解过程中,它会转化为
约束条件为:
将xixj转换为核函数,即等价于先将样本的特征向量高维化,再求内积。如下所示:
其思想类似于多项式回归;这样就可以解决线性不可分问题。
10.2.2核函数实现-多项式核
#程序10-4
import numpy as np
from sklearn import datasets
X,y = datasets.make_moons(noise=0.1,random_state=123456)
import matplotlib as mpl
import matplotlib.pyplot as plt
def plot_decision_boundary(clf, X, y, num_row=100, num_col=100):
"""
绘制决策边界
:param clf: 分类器, 即训练后的模型
:param X: 输入的训练数据X
:param y: 输入真实的训练数据y
:param num_row: 单位行数据生成的基本个数
:param num_col: 单位列数据生成的基本个数
"""
#以训练数据集X的第一个特征作为x轴,第二个特征作为y轴,生成坐标轴的间距
sigma = 1 #防止数据在图形的边上而加上的一个偏移量,设定一个较小的值即可
x_min, x_max = np.min(X[:, 0])-sigma, np.max(X[:, 0])+sigma
y_min, y_max = np.min(X[:, 1])-sigma, np.max(X[:, 1])+sigma
#对间距进行等分成t1、t2,并t1按照t2的列数进行行变换、t2按照t1的行数进行列变换
t1 = np.linspace(x_min, x_max, int(x_max-x_min)*num_row).reshape(-1,1)
t2 = np.linspace(y_min, y_max, int(y_max-y_min)*num_col).reshape(-1,1)
x_copy, y_copy = np.meshgrid(t1, t2)
#将变换后的x_,y_生成坐标轴的每个点
xy_all = np.stack((x_copy.reshape(-1,), y_copy.reshape(-1,)), axis=1)
#对坐标轴的点进行预测,并将预测结果变换为对应点的结果
y_predict = clf.predict(xy_all)
y_predict = y_predict.reshape(x_copy.shape)
#设置使用的颜色colors
cm_dark = mpl.colors.ListedColormap(['#EF9A9A','#FFF59D','#90CAF9'])
#绘制等高线,x_copy和y_copy种对应的点
#若y_predict为0绘制#FFA0A0,若y_predict为1绘制#A0A0FF,等高线绘制#A0FFA0
plt.contourf(x_copy, y_copy, y_predict,cmap=cm_dark) #绘制底色
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
#分类问题+核函数使用SVC
from sklearn.svm import SVC
#kernel参数
#linear:线性核函数;poly:多项式核函数;rbf:高斯核;sigmod:sigmod核函数;precomputed:核矩阵
def standard_SVC(degree,C=1.0,max_iter=1000):
return Pipeline([
('std_sca',StandardScaler()),
('svc',SVC(C=C,kernel='poly',degree=degree,max_iter=max_iter))
])
std_svc = standard_SVC(3,C=1e4,max_iter=1e8)
std_svc.fit(X,y)
plot_decision_boundary(std_svc,X,y)
plt.scatter(X[y==0,0],X[y==0,1])
plt.scatter(X[y==1,0],X[y==1,1])
plt.show()
运行结果:
多项式核:
10.2.3核函数的实现-高斯核
高斯核:,高斯核又被称为径向基函数核。其中,γ越大,高斯分布越窄;γ越小,高斯分布越宽。
#程序10-5
import numpy as np
from sklearn import datasets
X,y = datasets.make_moons(noise=0.1,random_state=123456)
import matplotlib as mpl
import matplotlib.pyplot as plt
def plot_decision_boundary(clf, X, y, num_row=100, num_col=100):
"""
绘制决策边界
:param clf: 分类器, 即训练后的模型
:param X: 输入的训练数据X
:param y: 输入真实的训练数据y
:param num_row: 单位行数据生成的基本个数
:param num_col: 单位列数据生成的基本个数
"""
#以训练数据集X的第一个特征作为x轴,第二个特征作为y轴,生成坐标轴的间距
sigma = 1 #防止数据在图形的边上而加上的一个偏移量,设定一个较小的值即可
x_min, x_max = np.min(X[:, 0])-sigma, np.max(X[:, 0])+sigma
y_min, y_max = np.min(X[:, 1])-sigma, np.max(X[:, 1])+sigma
#对间距进行等分成t1、t2,并t1按照t2的列数进行行变换、t2按照t1的行数进行列变换
t1 = np.linspace(x_min, x_max, int(x_max-x_min)*num_row).reshape(-1,1)
t2 = np.linspace(y_min, y_max, int(y_max-y_min)*num_col).reshape(-1,1)
x_copy, y_copy = np.meshgrid(t1, t2)
#将变换后的x_,y_生成坐标轴的每个点
xy_all = np.stack((x_copy.reshape(-1,), y_copy.reshape(-1,)), axis=1)
#对坐标轴的点进行预测,并将预测结果变换为对应点的结果
y_predict = clf.predict(xy_all)
y_predict = y_predict.reshape(x_copy.shape)
#设置使用的颜色colors
cm_dark = mpl.colors.ListedColormap(['#EF9A9A','#FFF59D','#90CAF9'])
#绘制等高线,x_copy和y_copy种对应的点
#若y_predict为0绘制#FFA0A0,若y_predict为1绘制#A0A0FF,等高线绘制#A0FFA0
plt.contourf(x_copy, y_copy, y_predict,cmap=cm_dark) #绘制底色
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
#分类问题+核函数使用SVC
from sklearn.svm import SVC
#kernel参数
#linear:线性核函数;poly:多项式核函数;rbf:高斯核;sigmod:sigmod核函数;precomputed:核矩阵
def standard_SVC(gamma,C=1.0,max_iter=-1):
return Pipeline([
('std_sca',StandardScaler()),
('svc',SVC(gamma=gamma,C=C,kernel='rbf',max_iter=max_iter))
])
std_svc = standard_SVC(1)
std_svc.fit(X,y)
plot_decision_boundary(std_svc,X,y)
plt.scatter(X[y==0,0],X[y==0,1])
plt.scatter(X[y==1,0],X[y==1,1])
plt.show()
std_svc2 = standard_SVC(40)
std_svc2.fit(X,y)
plot_decision_boundary(std_svc2,X,y)
plt.scatter(X[y==0,0],X[y==0,1])
plt.scatter(X[y==1,0],X[y==1,1])
plt.show()
运行结果:
10.3 SVM解决回归问题
#程序10-6
import numpy as np
from sklearn import datasets
boston = datasets.load_boston()
X = boston.data
y = boston.target
from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test = train_test_split(X,y,random_state=123456)
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.svm import LinearSVR
#epsilon参数
def standard_LinearSVR(epsilon=0.1,C=1.0):
return Pipeline([
('std_sca',StandardScaler()),
('lin_svr',LinearSVR(epsilon=epsilon,C=C))
])
svr = standard_LinearSVR(epsilon=0.01)
svr.fit(X_train,y_train)
score = svr.score(X_test,y_test)
print(score)
运行结果:
0.7038286472455404
总结:对于分类问题,在margin范围内,要求样本越少越好;对于回归问题,在margin范围内,要求样本越多越好。