感知器模型
感知器模型就是试图找到一条线,能够将所有的二元类别分割开。如果是高维空间就是找到一个超平面,能够将所有的二元类别分割开。算法是在数据中找出一个划分超平面,让尽可能的数据分布在这个平面的两侧。实际中符合要求的超平面是可能存在多个点的。
模型的核心思想,让距离超平面最近的点尽可能的远离超平面。
SVM几个重要名词解释
1,线性可分:在数据集中可以找出一个超平面,将两组数据分开,这个数据集叫线性可分数据
2,线性不可分:在数据集中,没法找出一个超平面,能够将两组数据分开,
3,分隔超平面:将数据分割开来的直线/平面
4,支持向量:离分隔超平面最近的那些点叫做支持向量
5,间隔,支持向量数据到分隔超平面的距离。
在svm中,支持向量到超平面的函数狐狸一般设置为1
6.2.2 SVM应用的一般框架
'''
SVM的一般流程
1)收集数据:可以使用任意方法。
2)准备数据:需要数值型数据。
3)分析数据:有助于可视化分隔超平面
4)训练算法:SVM的大部分时间都源自训练,改过程主要实现两个参数的调优
5)测试算法:十分简单的计算过程就可以实现
6)使用算法:几乎所有问题都可以使用svm,svm本身就是一个二类分类器,对多类问题应用svm需要对代码做修改
'''
6.3 SMO高效优化算法
所有需要做的围绕优化的事情就是训练分类器,一旦得到appha的最优值,我们就得到了分隔超平面并能够将之用于数据分类
6.3.1Platt的SMO算法
SMO表示序列最小化,将大优化问题分解为多个小优化问题来求解。目标是求出一系列alpha和b,通过alpha,计算出权重向量w并得到分隔超平面
SMO工作原理:每次循环中选择两个alpha进行优化处理。一旦找到一对合适的alpha,那么久增大其中一个同时减少另一个。
6.3.2 应用简化版SMO算法处理小规模数据集
'''
SMO算法的伪代码大致如下
创建一个alpha向量并将其初始化为0向量
当迭代次数小于最大迭代次数时(外循环)
对数据集中的每个数据向量(内循环)
如果该数据向量可以被优化
随机选择另外一个数据向量
同时优化这两个向量
如果两个向量都不能被优化,退出内循环
如果所有向量都没被优化,增加迭代数目,继续下一次循环
'''
#5个参数分别是数据集,类别标签,常数c,容错率toler,取消前最大循环次数
def smoSimple(dataMatIn, classLabels, C, toler, maxIter):
dataMatrix = mat(dataMatIn);
labelMat = mat(classLabels).transpose()#转置了类别标签,变为列向量
b = 0;
m, n = shape(dataMatrix)
alphas = mat(zeros((m, 1)))
iter = 0
while (iter < maxIter):
alphaPairsChanged = 0 #记录是否已经进行了优化
for i in range(m):
fXi = float(multiply(alphas, labelMat).T * (dataMatrix * dataMatrix[i, :].T)) + b #预测的类别
Ei = fXi - float(labelMat[i]) # if checks if an example violates KKT conditions
if ((labelMat[i] * Ei < -toler) and (alphas[i] < C)) or ((labelMat[i] * Ei > toler) and (alphas[i] > 0)):#对该值进行优化,位于此处
j = selectJrand(i, m) #随机选择第二个alpha
fXj = float(multiply(alphas, labelMat).T * (dataMatrix * dataMatrix[j, :].T)) + b
Ej = fXj - float(labelMat[j])
alphaIold = alphas[i].copy();
alphaJold = alphas[j].copy();
#保证alpha 在0和C之间
if (labelMat[i] != labelMat[j]):
L = max(0, alphas[j] - alphas[i])
H = min(C, C + alphas[j] - alphas[i])
else:
L = max(0, alphas[j] + alphas[i] - C)
H = min(C, alphas[j] + alphas[i])
if L == H: print("L==H"); continue
eta = 2.0 * dataMatrix[i, :] * dataMatrix[j, :].T - dataMatrix[i, :] * dataMatrix[i, :].T - dataMatrix[
j,
:] * dataMatrix[
j, :].T
if eta >= 0: print("eta>=0"); continue
alphas[j] -= labelMat[j] * (Ei - Ej) / eta
alphas[j] = clipAlpha(alphas[j], H, L)
if (abs(alphas[j] - alphaJold) < 0.00001): print("j not moving enough"); continue
#对i进行修改,修改量于j相同,但方向相反
alphas[i] += labelMat[j] * labelMat[i] * (alphaJold - alphas[j]) # update i by the same amount as j
# the update is in the oppostie direction
b1 = b - Ei - labelMat[i] * (alphas[i] - alphaIold) * dataMatrix[i, :] * dataMatrix[i, :].T - labelMat[
j] * (alphas[j] - alphaJold) * dataMatrix[i, :] * dataMatrix[j, :].T
#设置常数项
b2 = b - Ej - labelMat[i] * (alphas[i] - alphaIold) * dataMatrix[i, :] * dataMatrix[j, :].T - labelMat[
j] * (alphas[j] - alphaJold) * dataMatrix[j, :] * dataMatrix[j, :].T
if (0 < alphas[i]) and (C > alphas[i]):
b = b1
elif (0 < alphas[j]) and (C > alphas[j]):
b = b2
else:
b = (b1 + b2) / 2.0
alphaPairsChanged += 1
print("iter: %d i:%d, pairs changed %d" % (iter, i, alphaPairsChanged))
if (alphaPairsChanged == 0):
iter += 1
else:
iter = 0
print("iteration number: %d" % iter)
return b, alphas
dataarr, labelarr = loadDataSet('testSet.txt')
b,alphas=smoSimple(dataarr,labelarr,0.6,0.001,40)
print(b)rint(alphas[alphas>0])
print(shape(alphas[alphas>0])) //支持向量的个数
for i in range(100):
if alphas[i]>0:print(dataarr[i],labelarr[i])
....
iteration number: 39
j not moving enough
j not moving enough
iteration number: 40[-3.79937187]]
[[0.13280856 0.23133738 0.00221044 0.36635638]]
(1, 4)
[4.658191, 3.507396] -1.0
[3.457096, -0.082216] -1.0
[2.893743, -1.643468] -1.0
[6.080573, 0.418886] 1.0
基于上面数据,对支持向量画圈结果
from numpy import *
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.patches import Circle
xcord0 = []
ycord0 = []
xcord1 = []
ycord1 = []
markers =[]
colors =[]
fr = open('testSet.txt')#this file was generated by 2normalGen.py
for line in fr.readlines():
lineSplit = line.strip().split('\t')
xPt = float(lineSplit[0])
yPt = float(lineSplit[1])
label = int(lineSplit[2])
if (label == -1):
xcord0.append(xPt)
ycord0.append(yPt)
else:
xcord1.append(xPt)
ycord1.append(yPt)
fr.close()
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(xcord0,ycord0, marker='s', s=90)
ax.scatter(xcord1,ycord1, marker='o', s=50, c='red')
plt.title('Support Vectors Circled')
circle = Circle((4.658191, 3.507396), 0.5, facecolor='none', edgecolor=(0,0.8,0.8), linewidth=2, alpha=0.5)
ax.add_patch(circle)
circle = Circle((3.457096, -0.082216), 0.5, facecolor='none', edgecolor=(0,0.8,0.8), linewidth=3, alpha=0.5)
ax.add_patch(circle)
circle = Circle((2.893743, -1.643468), 0.5, facecolor='none', edgecolor=(0,0.8,0.8), linewidth=3, alpha=0.5)
ax.add_patch(circle)
circle = Circle((6.080573, 0.418886), 0.5, facecolor='none', edgecolor=(0,0.8,0.8), linewidth=3, alpha=0.5)
ax.add_patch(circle)
#plt.plot([2.3,8.5], [-6,6]) #seperating hyperplane
#plt.plot([2.3,8.5], [-6,5]) #seperating hyperplane
b = -3.75567; w0=0.8065; w1=-0.2761
x = arange(-2.0, 12.0, 0.1)
y = (-w0*x - b)/w1
ax.plot(x,y)
ax.axis([-2,12,-8,6])
plt.savefig('2.png');
plt.show()
6.4 利用完整Platt SMO 算法加速优化
...
6.5.1 利用核函数将数据映射到高维空间
将数据从一个特征空间转换到另一个特征空间,将这个空间称为从一个特征空间到另一个特征空间的映射。通常情况下,这种映射会将低维特征空间映射到高维空间
这种映射,是通过核函数来实现的
6.5.2径向基核函数
svm中常用的一个核函数,径向基函数是一个采用向量作为自变量的函数,能够基于向量距离运算输出一个标量。
#核转换函数
def kernelTrans(X, A, kTup): # calc the kernel or transform data to a higher dimensional space
m, n = shape(X)
K = mat(zeros((m, 1)))
if kTup[0] == 'lin':
K = X * A.T # linear kernel
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)) # divide in NumPy is element-wise not matrix like Matlab
else:
raise NameError('Houston We Have a Problem -- \
That Kernel is not recognized')
return K
6.6手写识别问题回顾
在选择算法的时候,如果在性能不变的情况下,使用更少的内存。可以考虑使用支持向量机,支持向量机只需要保存支持向量
通过改变径向基函数的参数,结论:
1,最小的训练错误率并不对应与最小的支持向量数目。
2,线性核函数的效果并不是特别的槽糕。可以牺牲线性核函数的错误率来换取分类速度的提高