【机器学习】最小二乘法支持向量机LSSVM的数学原理与Python实现
一、LSSVM数学原理
1. 感知机
SVM是从感知机发展而来。假设有m个训练样本
,
,
表示n维的训练样本输入向量。我们企图找到分隔超平面
能够分隔正负样本。
感知机直接将误分类的样本到分隔超平面之间的距离作为损失函数。在感知机模型求解过程中,分隔超平面权重W和偏差b的初始值选择不同,求出最终的分隔超平面是不同的。这就引出了SVM的模型训练思想:使得离分隔超平面最近的样本与分隔超平面的距离最远。下面介绍函数间隔与几何间隔的含义:
2. SVM
-
函数间隔:
对于训练集中的一个样本 ,其函数间隔为 。
训练集的函数间隔等于所有样本点的函数间隔的最小值:
函数间隔只能表示分类预测的正确性,不能表示样本到分隔超平面的准确距离。 -
几何距离
对于训练集中的一个样本 ,其函数间隔为 。
训练集的函数间隔等于所有样本点的函数间隔的最小值:
几何间隔不但可以表示分类预测的正确性还能准确地表示样本到分隔超平面的距离。
SVM的优化问题转化为最大化训练样本的几何距离:
函数间隔的取值不会影响最优问题的解,将训练样本的函数间隔取值为1,上述问题转化为
由此可知SVM的优化问题是一个带有分等式约束的QP(Quadratic Programming)问题,其具体求解过程比较复杂,可参考【1,2】。
3. LSSVM
LSSVM将SVM优化问题的非等式约束用等式约束替换。具体形式如下:
为了解决存在部分特异点的情况,给每一个样本引入误差变量 ,并在原始函数中加入误差变量的L2正则项。这样LSSVM的优化问题就转化为
其中, 为正则化参数。对于非线性可分的训练样本,可以将原始样本从映射到更高维的线性可分的空间中。 将 映射到更高维空间中
LSSVM的优化问题是一个带有等式约束的QP问题,为求解该问题(参考资料【3】),先列出上述优化问题的Lagrange函数: , 表示对应于 的lagrange乘子。Lagrange函数对各个变量进行求导,并使导数为零:
求解 和b可以通过如下等式
其中
,I是单位矩阵,K是m*m的核矩阵,
,其中
为核参数。
对于新的样本
,LSSVM模型的输出为:
4. LSSVM与SVM的区别
1.优化问题的约束条件不同
2.SVM中只有支持向量对应的Lagrange乘子为非零数值,但在LSSVM中Lagrange乘子序列
与误差序列
成正比。我们把LSSVM中Lagrange乘子序列
称为支持数值谱(参考资料【4】)。
3.LSSVM模型的求解是线性方程的求解,计算效率要高于SVM模型的求解。
4.LSSVM模型的缺点:LSSVM缺乏稀疏性,数据集中的所有样本对新样本的预测都有所贡献,贡献的大小是由对应的Lagrange乘子(支持数值)的大小决定的(参考资料【4】)。
5.个人理解:Lagrange乘子越大,对应的样本越靠近分隔超平面(对真正的分隔超平面的产生作用越大)。因为Lagrange乘子越大,对应的误差值越大,靠近分隔超平面的样本被误分类的可能性才越大。
二、LSSVM的python实现
代码及样本地址:https://github.com/shiluqiang/LSSVM_python_code
//
from numpy import *
def loadDataSet(filename):
'''导入数据
input: filename:文件名
output:dataMat(list)样本特征
labelMat(list)样本标签
'''
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
def kernelTrans(X,A,kTup):
'''数据集中每一个数据向量与数据A的核函数值
input: X--特征数据集
A--输入向量
kTup--核函数参量定义
output: K--数据集中每一个数据向量与A的核函数值组成的矩阵
'''
X = mat(X)
m,n = shape(X)
K = mat(zeros((m,1)))
if kTup[0] == 'lin':
K = X * A.T
elif kTup[0] == 'rbf':
for j in range(m):
deltaRow = X[j,:] - A
K[j] = deltaRow * deltaRow.T
K = exp(K/(-1 * kTup[1] ** 2))
else: raise NameError('Houston We Have a Problem -- That Kernel is not recognized')
return K
class optStruct:
def __init__(self,dataMatIn,classLabels,C,kTup):
self.X = dataMatIn
self.labelMat = classLabels
self.C = C
self.m = shape(dataMatIn)[0]
self.alphas = mat(zeros((self.m,1)))
self.b = 0
self.K = mat(zeros((self.m,self.m))) #特征数据集合中向量两两核函数值组成的矩阵,[i,j]表示第i个向量与第j个向量的核函数值
for i in range(self.m):
self.K[:,i] = kernelTrans(self.X, self.X[i,:], kTup)
def leastSquares(dataMatIn,classLabels,C,kTup):
'''最小二乘法求解alpha序列
input:dataMatIn(list):特征数据集
classLabels(list):分类标签集
C(float):参数,(松弛变量,允许有些数据点可以处于分隔面的错误一侧)
kTup(string): 核函数类型和参数选择
output:b(float):w.T*x+b=y中的b
alphas(mat):alphas序列
'''
oS = optStruct(mat(dataMatIn),mat(classLabels).transpose(),C,kTup)
##1.参数设置
unit = mat(ones((oS.m,1))) #[1,1,...,1].T
I = eye(oS.m)
zero = mat(zeros((1,1)))
upmat = hstack((zero,unit.T))
downmat = hstack((unit,oS.K + I/float(C)))
##2.方程求解
completemat = vstack((upmat,downmat)) #lssvm中求解方程的左边矩阵
rightmat = vstack((zero,oS.labelMat)) # lssvm中求解方程的右边矩阵
b_alpha = completemat.I * rightmat
oS.b = b_alpha[0,0]
for i in range(oS.m):
oS.alphas[i,0] = b_alpha[i+1,0]
return oS.alphas,oS.b,oS.K
def predict(alphas,b,dataMat,testVec):
'''预测结果
input:alphas(mat):Lagrange乘子序列
b(float):分隔超平面的偏置
dataMat()
output:sign(float(predict_value))(int):预测样本的类别
'''
Kx = kernelTrans(dataMat,testVec,kTup) #可以对alphas进行稀疏处理找到更准确的值
predict_value = Kx.T * alphas + b
# print('预测值为:%f'%predict_value)
# print('分类结果为:%f'%sign(float(predict_value)))
return sign(float(predict_value))
if __name__ == '__main__':
##1.导入数据
print('-----------------------------1.Load Data-------------------------------')
dataMat,labelMat = loadDataSet('testSetRBF.txt')
C = 0.6
k1 = 0.3
kernel = 'rbf'
kTup = (kernel,k1)
##2.训练模型
print('----------------------------2.Train Model------------------------------')
alphas,b,K = leastSquares(dataMat,labelMat,C,kTup)
##3.计算训练误差
print('----------------------------3.Calculate Train Error--------------------')
error = 0.0
for i in range(len(dataMat)):
test = predict(alphas,b,dataMat,dataMat[i])
if test != float(labelMat[i]):
error +=1.0
errorRate = error/len(dataMat)
print('---------------训练误差为:%f-------------------'%errorRate)
参考资料
【1】https://blog.csdn.net/liugan528/article/details/79448379
【2】https://blog.csdn.net/google19890102/article/details/35566949
【3】https://blog.csdn.net/hlx371240/article/details/41621723
【4】Suykens J A K , Vandewalle J . Least Squares Support Vector Machine Classifiers[M]. Kluwer Academic Publishers, 1999.