本篇主要内容:SVM解决非线性可分,Kernel Function
添加多项式特征解决非线性可分问题
上篇我们介绍了SVM是如何生成线性决策边界解决线性可分或近似线性可分问题的,本篇将介绍SVM如何解决线性不可分问题。来看这样的一个数据集:
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
X, y = datasets.make_moons(noise=0.15,random_state=666)
plt.scatter(X[y==0,0],X[y==0,1])
plt.scatter(X[y==1,0],X[y==1,1])
plt.show()
moon
这是sklearn的datasets中的moon数据,这里给它加入了一定的噪音,显然这是一个线性不可分数据。在Logostic回归中,为了得到非线性决策边界,我们为数据加入了多项式特征,同样的思想,我们也可以在使用SVM时加入多项式特征,这样就可以解决一些非线性可分问题。针对上面的例子:
from sklearn.preprocessing import PolynomialFeatures,StandardScaler
from sklearn.svm import LinearSVC
from sklearn.pipeline import Pipeline
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)
这里将所有过程封装了一个Pipeline。使用它创建一个SVM分类器,给数据添加3次幂多项式特征,训练该分类器,并绘制决策边界:
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()
决策边界
可以看到决策边界已经是非线性的了,并且对于图中的两类样本也很好地进行了分类。
核函数(Kernel Function)
SVM本质上就是求解一个二次规划问题,不过一般我们并不直接求解:
而是求解它的对偶问题(具体求解翻阅机器学习或运筹学教材),在它的对偶问题中,会遇到不同特征向量的内积:
对偶问题如果要解决线性不可分问题,还需要SVM通过一个非线性变换 φ( x) ,将输入的低维特征映射到高维特征空间,特征空间的维数可能非常高。考虑到真正求解SVM的最优化问题的时候,往往只会用到两个高维特征空间特征的内积,如果SVM的求解只用到内积运算,而在低维输入空间又存在某个函数 K(x, x′) ,它恰好等于在高维空间中这个内积,即K( x, x′) =<φ( x)⋅φ( x′) > 。那么SVM就不用计算复杂的非线性变换,而由这个函数 K(x, x′) 直接得到非线性变换的内积,大大简化了计算。这样的函数 K(x, x′) 就被称为核函数。
以一个例子来展示一下核函数的效果,假设现在有两个二维空间中的数据点x=(x1,x2)和y=(y1,y2),考虑下面这个二元函数:
于是,
发现最后结果恰好是两个向量的内积,而且两个向量分别是二维空间数据点x和y在5维空间中的映射!想到刚才核函数的定义,我们很容易知道,K(x,y)就是一个核函数。它给出了一个二维的表达式,使得x,y代入即可求值,而不再需要先把x,y映射成5维空间中的向量p,q再求内积,这样大大简化了运算。
sklearn中常用的核函数:
常用核函数通过在SVM中使用这些核函数可以将原本线性不可分的数据映射到高维特征空间后变的线性可分。
sklearn中的RBF核
RBF(Radial Basis Function)中文叫径向基函数,其实就是Gauss核函数:
它可以将特征映射到无穷维空间,其中的是一个超参数,越大表示模型复杂度越高,相应的也就越容易出现过拟合,实际中要通过调参得到最好的。下面在moon数据集上使用Gauss核函数:
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC
from sklearn.pipeline import Pipeline
def RBFKernelSVC(gamma=1.0):
return Pipeline([
('std_scaler',StandardScaler()),
('svc',SVC(kernel='rbf',gamma=gamma))
])
接下来训练一个使用RBF核的SVM,此时默认是1,并绘制决策边界:
svc = RBFKernelSVC(gamma=1.0)
svc.fit(x,y)
plot_decision_boundary(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()
决策边界:
gamma=1可以看到时模型的效果还是不错的。
再来看一下的情况:
svc100 = RBFKernelSVC(gamma=100)#过拟合
svc100.fit(x,y)
plot_decision_boundary(svc100,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()
决策边界:
gamma=100此时明显出现了过拟合。
可以继续测试,当时会出现欠拟合,这里不再展示。
在sklearn中可以很方便地调用它提供的核函数,当然也可以自己编写核函数,在符合sklearn接口的情况下以下能够无缝衔接。
网友评论