3.3 SVM分类器
3.3.1 原理概述
SVM(Support Vector Machine,支持向量机)是被广泛使用的分类算法,是神经网络热潮到来前应用最广泛的机器学习算法之一。SVM的数学描述一般是通过核空间的距离给出的,这里我们基于RBF(Radial Basis Function)核SVM的原理给出一个直觉上的解释,如果需要了解更加严格的理论分析,读者可以查阅本章参考文献。
SVM分类器根据物体的特征取值将其分为两类,图3-3给出了若干个物体样本在特征空间的位置,图中空心点和实心黑点分别表示两种不同的物体。
图3-3 物体样本在特征空间的位置
可以看出,空心点和实心黑点的分布位置具有一定的规律,从直觉上可以用图示虚线分离这两类点,在虚线上方的是第一类(空心点对应的类别),在虚线下方是第二类(实心黑点对应的类别)。
SVM通过构建分类判别函数f(x)实现在两类物体的特征空间取不同的符号,比如在第一类物体特征空间(曲线上方)取值为正,在第二类物体特征空间(曲线下方)取值为负。在这个例子中,代表特征空间的点的坐标。
一种构建判别函数f(x)的方法是基于图3-3中训练数据点经过“扩散”后求加权和实现的,即
其中是已知类别的N个“样本”点对应的特征空间的坐标;可以看成样本点对特征空间位置x的“影响力”,即:对x距离越近,越大(),表明x受的影响越严重。γ控制了的“影响力”作用距离,γ越大,对邻域影响力随距离衰减就越严重。决定了对类别判定的影响力,它可以是正数或者负数,的绝对值越大,表明对类别判定的影响越强。和b分别是训练得到的权重系数和偏置系数,通过将f(x)的数值和0比较(两种结果:大于或者小于等于),将x分类到两个类别,如图3-4所示。
图3-4 构建判别函数的示意图
图3-4中提到的“高斯形曲面”就是函数在二维情况下对应的曲面形状。把图3-4中各个“高斯曲面”叠加构成的完整曲面如图3-5所示。
图中虚线对应,它将特征平面分为两部分,可以看成“分类曲线”,需要判别的数据特征x落在该分类曲线上方(远端)时,对应,表明x对应的样本属于第一类物体,若x落在该分类曲线下方(近端)时,对应,表明x对应的样本属于第二类物体。
对于特征参数空间的任意一个点x,比如图3-3中×所在位置,我们计算分类判别函数时,需要计算它到每个训练样本点的距离。当训练样本点很多时,运算量大。图3-6给出了已知样本和待分类样本的特征距离计算示意图。
一个减少运算量的方法是从原始的训练样本点中挑选一小部分重要的点,忽略对分类影响不大的数据点。在SVM中,选择分类边界(图3-6中虚线)附近的点作为分类的关键点,忽略远离分类边界的点,这就可以降低点的数量,如图3-7所示。
图3-6 已知样本和需要分类样本的特征距离计算示意图
图3-7 SVM算法保留分类边界附近的数据样本
那些分类边界附近的点就称为“支持向量”。SVM算法中只考虑“支持向量”对应的一小部分“样本点”,因此可以大大降低运算量。其中支持向量、权重系数以及偏置系数b通过特定的SVM学习算法可以得到。
下面我们接着讨论SVM分类器的底层运算。
SVM的运算过程包括两部分:计算输入数据x的非线性映射,以及对映射结果y(向量)的线性分类。比如对于RBF核函数的二分类SVM,对于输入向量x,SVM分类器首先计算它的非线性映射:
其中是支持向量,计算得到构成向量:。然后应用线性分类算法计算,并根据是否大于0将输入的数据——向量x分为两类。
3.3.2 模型训练和推理
下面给出通过Scikit-Learn软件包实现SVM分类器训练的代码,训练算法的原理在这里不具体展开,感兴趣的读者可以参考文献[1][2]了解其中的理论细节。分类器训练分四部分:1)读取数据集;2)数据集分割;3)SVM模型训练;4)训练结果的测试。读者可以根据代码清单3-2中的注释区分这几部分。
代码清单3-2 SVM训练例程
# 读取数据集 from sklearn import datasets data = datasets.load_breast_cancer() x,y=data.data,data.target # 训练/测试数据集分离 from sklearn.model_selection import train_test_split train_x, test_x, train_y, test_y = train_test_split(x,y,test_size=0.3,shuffle=True) # SVM模型训练 from sklearn import svm model = svm.NuSVC(gamma=1.5e-4,kernel='rbf') model.fit(train_x, train_y) # 测试训练结果 y_pred = model.predict(test_x) print('[INF] num err:%d'%np.sum(y_pred!=test_y))
代码第一部分读取“乳腺癌”示例的分类数据集,该数据集包括569个人的体检数据,其中357人被诊断患有乳腺癌。每个人的一件数据包括30个指标(就是之前提到的“特征”)。
代码第二部分通过train_test_split函数调用把读取的数据集随机打乱后,按70%和30%的比例拆分为训练数据集和测试数据集。数组train_x和train_y分别存放训练数据特征和训练数据的分类答案;其中train_x是2维数组,每一行对应一个人的30个体检指标。数组test_x和test_y分别存放测试数据特征和测试数据的分类答案。
代码的第三部分是构建SVM训练器并训练,代码如下:
model = svm.NuSVC(gamma=1.5e-4,kernel='rbf')
此代码生成SVM训练器,指定核函数(即rbf)以及γ(即gamma)。训练过程如下:
model.fit(train_x, train_y)
训练结果存储在model中。其中model.dual_coef_存放权重系数wn,model.support_vectors_存放支持向量xn,model.intercept_存放模型参数b,model.gamma存放模型参数γ。
训练完成的模型通过下面的API调用实现分类推理:
y_pred = model.predict(test_x)
其中test_x是矩阵,它的每一行对应一组需要分类的测试指标,y_pred是分类结果,每个元素和text_x的行对应。