文章目录
一、基于最大间隔分割数据
支持向量机
优点:泛化错误率低,计算开销不大,结果易解释
缺点:对参数调节和核函数的选择敏感,原始分类器不加修改仅适用于处理二类问题。
适用数据类型:数值型和标称型数据。
-
线性可分:可以很容易就在数据中给出一条直线将两组数据点分开
上图的数据都是混合在一起,也就是不能用一条直线进行分类的数据,所以也就是线性不可分数据。 -
分隔超平面:将数据集分割开来的直线
数据点在二维平面上,分隔超平面就只是一条直线,但数据集是三维时,那么分隔超平面就是一个平面。依此类推,如果数据集是 N ( N ≥ 2 N(N\geq2 N(N≥2)维时,那么就需要一个 N − 1 N-1 N−1维的对象来分隔数据。该对象被称为超平面,也就是分类的决策边界。
理想状态是分布在超平面一侧的所有数据都属于某个类别,而分布在另一侧的所有数据则属于另一个类别。
- 间隔:离分隔超平面最近的点,到分隔面的距离
间隔应该尽可能地大,这是因为如果我们犯错或者在有限数据上训练分类器的话,大的间隔可以增加分类器的鲁棒性。
- 支持向量:离分隔超平面最近的那些点
支持向量到分割面的距离应该最大化。
二、寻找最大间隔
如何求解数据集的最佳分隔直线?以下图为例:
分隔超平面的形式为: w τ x + b {\bf w^\tau x} + b wτx+b
点A到分隔超平面的法线长度: ∣ w τ A + b ∣ / ∣ ∣ w ∣ ∣ |{\bf w^\tau A} + b| / | | {\bf w} | | ∣wτA+b∣/∣∣w∣∣
最大化间隔的目标就是找出分类器定义中的w和b。为此,我们必须找到具有最小间隔的数据点,而这些数据点也就是前面提到的支持向量。一旦找到具有最小间隔的数据点,我们就需要对该间隔最大化。这就可以写作:
arg max w , b { min n ( l a b e l ⋅ ( w τ x + b ) ) ⋅ 1 ∣ ∣ w ∣ ∣ } {\underset {w, b}{\operatorname {arg\,max} }}\, \left\{ {\underset {n}{\operatorname {min} }}\, (label · ({\bf w^\tau x} + b)) · {\frac{1}{| | {\bf w} | |}} \right\} w,bargmax{
nmin(label⋅(wτx+b))⋅∣∣w∣∣1}
直接求解上述问题相当困难,所以我们将它转换成为另一种更容易求解的形式。
m a x α [ ∑ i = 1 m α − 1 2 ∑ i , j = 1 m l a b e l ( i ) ⋅ l a b e l ( j ) ⋅ a i ⋅ a j ⟨ x ( i ) , x ( j ) ⟩ ] {\underset {\alpha}{max}}\left[ \sum^{m}_{i=1}\alpha - \frac{1}{2}\sum^{m}_{i,j=1}label^{(i)} · label^{(j)} · a_i · a_j \left\langle x^{(i)}, x^{(j)} \right\rangle \right] αmax[i=1∑mα−21i,j=1∑mlabel(i)⋅label(j)⋅ai⋅aj⟨x(i),x(j)⟩]
约束条件为:
C ≥ α ≥ 0 和 ∑ i = 1 m α i ⋅ l a b e l ( i ) = 0 C \geq \alpha \geq 0 和 \sum^m_{i=1}\alpha_i · label^{(i)} = 0 C≥α≥0和i=1∑mαi⋅label(i)=0
其中常数C 用于控制 “最大化间隔” 和 “保证大部分点的函数间隔小于1.0” 这两个目标的权重。在优化算法的实现代码中,常数C 是一个参数,因此可以通过调节该参数得到不同的结果。一旦求出了所有的 α \alpha α,那么分隔超平面就可以通过这些 α \alpha α 来表达。
SVM的一般流程
(1) 收集数据:可以使用任意方法。
(2) 准备数据:需要数值型数据。
(3) 分析数据:有助于可视化分隔超平面。
(4) 训练算法:SVM的大部分时间都源自训练,该过程主要实现两个参数的调优。
(5) 测试算法:十分简单的计算过程就可以实现。
(6) 使用算法:几乎所有分类问题都可以使用SVM,值得一提的是,SVM本身是一个二类分类器,对多类问题应用SVM需要对代码做一些修改。
三、简化版SMO算法
简化版SMO算法,省略了确定要优化的最佳 α \alpha α 对的步骤,而是首先在数据集上进行遍历每一个 α \alpha α,再在剩余的数据集中找到另外一个 α \alpha α,构成要优化的 α \alpha α 对,同时对其进行优化,这里的同时是要确保公式: ∑ α i ∗ l a b e l ( i ) = 0 \sum\alpha_i * label^{(i)} = 0 ∑αi∗label(i)=0
所以改变一个 α \alpha α 显然会导致等式失效,所以这里需要同时改变两个 α \alpha α。代码如下:
from time import sleep
import matplotlib.pyplot as plt
import numpy as np
import random
import types
"""
函数说明:读取数据
Parameters:
fileName - 文件名
Returns:
dataMat - 数据矩阵
labelMat - 数据标签
"""
def loadDataSet(fileName):
dataMat = []; labelMat = []
fr = open(fileName)
for line in fr.readlines(): #逐行读取,滤除空格等
lineArr = line.strip().split('\t')
dataMat.append([float(lineArr[0]), float(lineArr[1])]) #添加数据
labelMat.append(float(lineArr[2])) #添加标签
return dataMat,labelMat
"""
函数说明:随机选择alpha
Parameters:
i - alpha
m - alpha参数个数
Returns:
j -
"""
def selectJrand(i, m):
j = i #选择一个不等于i的j
while (j == i):
j = int(random.uniform(0, m))
return j
"""
函数说明:修剪alpha
Parameters:
aj - alpha值
H - alpha上限
L - alpha下限
Returns:
aj - alpah值
"""
def clipAlpha(aj,H,L):
if aj > H:
aj = H
if L > aj:
aj = L
return aj
"""
函数说明:简化版SMO算法
Parameters:
dataMatIn - 数据矩阵
classLabels - 数据标签
C - 松弛变量
toler - 容错率
maxIter - 最大迭代次数
Returns:
无
"""
def smoSimple(dataMatIn, classLabels, C, toler, maxIter):
#转换为numpy的mat存储
dataMatrix = np.mat(dataMatIn); labelMat = np.mat(classLabels).transpose()
#初始化b参数,统计dataMatrix的维度
b = 0; m,n = np.shape(dataMatrix)
#初始化alpha参数,设为0
alphas = np.mat(np.zeros((m,1)))
#初始化迭代次数
iter_num = 0
#最多迭代matIter次
while (iter_num < maxIter):
alphaPairsChanged = 0
for i in range(m):
#步骤1:计算误差Ei
fXi = float(np.multiply(alphas,labelMat).T*(dataMatrix*dataMatrix[i,:].T)) + b
Ei = fXi - float(labelMat[i])
#优化alpha,更设定一定的容错率。
if ((labelMat[i]*Ei < -toler) and (alphas[i] < C)) or ((labelMat[i]*Ei > toler) and (alphas[i] > 0)):
#随机选择另一个与alpha_i成对优化的alpha_j
j = selectJrand(i,m)
#步骤1:计算误差Ej
fXj = float(np.multiply(alphas,labelMat).T*(dataMatrix*dataMatrix[j,:].T)) + b
Ej = fXj - float(labelMat[j])
#保存更新前的aplpha值,使用深拷贝
alphaIold = alphas[i].copy(); alphaJold = alphas[j].copy();
#步骤2:计算上下界L和H
if (labelMat[i] != labelMat[j]):
L = max(0, alphas[j] - alphas[i])
H = min(C, C + alphas