一.svm概述
svm是一种二分类模型,学习策略是通过间隔最大化来实现分类的目的,最终转化为了凸二次规划求解,对于svm,处理的问题有三种情况,即:
1.线性可分问题:
为了高效简化计算参数w,b,将问题转化为:
2.线性不可分问题:(不可分是由于特异点outlier的存在引起的,故引入松弛变量)
C称为惩罚参数,C增大对误分类的惩罚就增大,转换为对偶问题有:
3.非线性问题:
二,求解参数
的确我们可以单纯的通过求解凸二次规划问题来获得答案,但是当训练样本量很大时,这些算法就会变的低效,从上面的公式就可以直观看出,有多少样例就有多少乘子,如何高效求解拉格朗日乘子成为了关键——smo。
浏览了很多博文总结一下具体的求解过程(smo):
1.寻找违背KKT条件的
,即:
其中:
2.寻找第二个乘子,通过:max|E1-E2|
3.求解约束前的
,公式为:
其中,
,Ei=f(xi)-yi
4.对
进行约束:
5.通过
求解
:
6.对b的更新
7.启发式迭代具体方法:看了很多博文,感觉讲的太抽象,自己尝试很久发现并没那么复杂,先遍历一遍全部样例,标注好违反kkt条件的样例,第二个alpha在这些违反kkt样例中找即可。
二.python实现
# -*- coding:utf-8 -*-
#svm.py
import numpy as np
import matplotlib.pyplot as plt
import random
def getdata(num): # 生成间隔大些的数据,需要输入数据量
xdata = []
for i in range(num):
idata = [random.randint(0, 20), random.randint(0, 20)]
if sum(idata) >= 20:
idata = [i + 5 for i in idata]
if not idata in xdata:
xdata.append(idata)
ydata = [1 if sum(i) >= 20 else -1 for i in xdata]
return xdata, ydata
class Svc(object):
def __int__(self,c=1000000,w=[0,0],b=0,xdata,ydata,alpha):#初始化函数
self.c=c
self.w=w
self.b=b
self.xdata=xdata
self.ydata=ydata
self.alpha=alpha
def kernels(self,x1,x2):#核函数,这里是线性可分因此就是普通的内积运算
a=x1[0]*x2[0]+x1[1]*x2[1]
return a
def kernelmat(self):#核矩阵
a=np.eye(len(self.xdata),len(self.xdata))
for i in range(len(self.xdata)):
for j in range(len(self.xdata)):
a[i][j]=self.kernels(self.xdata[i],self.xdata[j])
return a
def ui(self,i): # 求ui
a = 0
for j in range(len(self.xdata)):
a = a + self.alpha[j] * self.ydata[j] * (self.xdata[j][0] * self.xdata[i][0] + self.xdata[j][1] * self.xdata[i][1])
a = a + self.b
return a
def Ei(self,i): # 求Ei=ui-yi
a = self.ui(i) - self.ydata[i]
return a
def alpha2(self,i, tflist): # 找第二个alpha2在alpha向量中的位置,通过max|Ei-Ej|
ei = self.Ei(i)
a = 0
d = 0
for j in range(len(self.xdata)):
if tflist[j] == True:
ej = self.Ei(j)
bi = abs(ei - ej)
if bi > a:
a = bi
d = j
return d
def eta(self, i, j): # 求分母eta
a = self.xdata[i][0] ** 2 + self.xdata[i][1] ** 2 + self.xdata[j][0] ** 2 + self.xdata[j][1] ** 2 - 2 * (
self.xdata[i][0] * self.xdata[j][0] + self.xdata[i][1] * self.xdata[j][1])
return a
def alpha2new(self,i, j): # 求alpha2new,这里直接做约束
a = self.alpha[j] + self.ydata[j] * (self.Ei(i) - self.Ei(j)) / self.eta(i, j)
if self.ydata[i] == self.ydata[j]:
L = np.max([0, self.alpha[i] + self.alpha[j] - self.c])
H = np.min([self.c, self.alpha[i] + self.alpha[j]])
if a > H:
return H
elif a < L:
return L
else:
return a
else:
L = np.max([0, self.alpha[j] - self.alpha[i]])
H = np.min([self.c, self.c + self.alpha[j] - self.alpha[i]])
if a > H:
return H
elif a < L:
return L
else:
return a
def alpha1new(self,i, j): # 把alpha2new带进去求alpha1new
a = self.alpha[i] + self.ydata[i] * self.ydata[j] * (self.alpha[j] - self.alpha2new(i,j))
return a
def bnew(self,i,j): # 更新b
ei = self.Ei(i)
ej = self.Ei(j)
yi = self.ydata[i]
yj = self.ydata[j]
alphai = self.alpha1new(i,j)
alphaj = self.alpha2new(i,j)
b1 = self.b - ei - yi * (alphai - self.alpha[i]) * (self.xdata[i][0] ** 2 + self.xdata[i][1] ** 2) - yj * (alphaj - self.alpha[j]) * (self.xdata[j][0] * self.xdata[i][0] + self.xdata[j][1] * self.xdata[i][1])
b2 = self.b - ej - yi * (alphai - self.alpha[i]) * (self.xdata[i][0] * self.xdata[j][0] + self.xdata[i][1] * self.xdata[j][1]) - yj * (alphaj - self.alpha[j]) * (self.xdata[j][0] ** 2 + self.xdata[j][1] ** 2)
if alphai > 0 and alphai < self.c:
return b1
elif alphaj > 0 and alphaj < self.c:
return b2
else:
return (b1 + b2) / 2
def sign(self,x): # 符号函数
if x > 0:
return 1
elif x < 0:
return -1
else:
return 0
def acc(self): # 计算正确率,判断函数
a = 0
for i in range(len(self.xdata)):
a = a + (self.sign(self.w[0] * self.xdata[i][0] + self.w[1] * self.xdata[i][1] + self.b) == self.ydata[i])
return a / len(self.xdata)
def wb(self): # 训练函数输出w,b
alphav = self.alpha.copy()
while self.acc() < 0.99:
tflist = []
for i in range(len(self.alpha)):
tflist.append((self.ydata[i] * self.ui(i) == 1 and self.alpha[i] == 0) or (self.ydata[i] * self.ui(i) > 1 and self.alpha[i] != 0) or (self.ydata[i] * self.ui(i) < 1))
for i in range(len(self.alpha)):
if tflist[i] == True:
j = self.alpha2(i,tflist)
t = self.alpha2new(i,j)
alphav[j] = t
alphav[i] = self.alpha1new(i,j)
self.b = self.bnew(i,j)
self.alpha = alphav
self.w = [0, 0]
for i in range(len(self.alpha)):
self.w = self.w + self.alpha[i] * self.ydata[i] * np.array(self.xdata[i])
self.w = list(self.w)
return self.w,self.b
def pic(self):#画图
x=[1,25]
xdata1 = [i[0] for i in self.xdata]
xdata2 = [i[1] for i in self.xdata]
plt.scatter(xdata1, xdata2, c=["r" if i == 1 else "b" for i in self.ydata], s=5)
y = [((-1) * self.b - self.w[0] * x[0]) / self.w[1], ((-1) * self.b - self.w[0] * x[1]) / self.w[1]] # 计算y
plt.plot(x, y)
plt.show()
def test(self,point):#测试函数
a=self.sign(self.w[0]*point[0]+self.w[1]*point[1]+self.b)
return a
def main():
s=Svc()
s.xdata,s.ydata=getdata(100)
s.alpha=np.ones(len(s.xdata))#初始化alpha
s.w,s.b=s.wb()
s.pic()
for i in range(10):
print(s.test(s.xdata[i])==s.ydata[i])
if __name__=="__main__": main()
效果如图所示: