Merge pull request #169 from jiangzhonglian/master

完成 第6章 支持向量机SVM的项目案例
This commit is contained in:
片刻
2017-09-22 12:34:19 +08:00
committed by GitHub
4 changed files with 434 additions and 61 deletions

View File

@@ -11,8 +11,8 @@
## 支持向量机 场景
* 如果把所有的点看作地雷,那么我们(超平面)得找到最近所有的地雷,并保证我们离它最远。
* 所以选择D会比B、C分隔的效果要好很多。
* 要给左右两边的点进行分类
* 明显发现选择D会比B、C分隔的效果要好很多。
![线性可分](/images/6.SVM/SVM_3_linearly-separable.jpg)
@@ -23,7 +23,7 @@
![k_2](/images/6.SVM/k_2.jpg "k_2")
对于上述的苹果和香蕉我们想象为2水果类型的炸弹。(保证距离最近的炸弹,距离它们最远)
对于上述的苹果和香蕉我们想象为2水果类型的炸弹。(保证距离最近的炸弹,距离它们最远)
1. 寻找最大分类间距
2. 转而通过拉格朗日函数求优化的问题
@@ -74,10 +74,9 @@ This is the simplest kind of SVM (Called an LSVM) Support Vectors are those data
* 类别标签用-1、1是为了后期方便 \\(lable*(w^Tx+b)\\) 的标识和距离计算;如果 \\(lable*(w^Tx+b)>0\\) 表示预测正确,否则预测错误。
* 现在目标很明确,就是要找到`w``b`,因此我们必须要找到最小间隔的数据点,也就是前面所说的`支持向量`
* 也就说,让最小的距离取最大.(最小的距离:就是最小间隔的数据点;最大:就是最大间距,为了找出最优超平面--最终就是支持向量)
* 怎么理解呢? 例如: 如果把所有的点看作地雷,那么我们(超平面)得找到最近所有的地雷,并保证我们离它最远。
* 目标函数:\\(arg: max\left( min(lable*(w^Tx+b))*\frac{1}{||w||} \right) \\)
* 目标函数:\\(arg: max_{关于w, b} \left( min[lable*(w^Tx+b)]*\frac{1}{||w||} \right) \\)
1. 如果 \\(lable*(w^Tx+b)>0\\) 表示预测正确,也称`函数间隔`\\(||w||\\) 可以理解为归一化,也称`几何间隔`
2. 令 \\(lable*(w^Tx+b)>=1\\) 因为-11之间得到的点是存在误判的可能性所以要保障min=1,才能更好降低噪音数据影响。
2. 令 \\(lable*(w^Tx+b)>=1\\) 因为01之间得到的点是存在误判的可能性所以要保障 \\(min[lable*(w^Tx+b)]=1\\),才能更好降低噪音数据影响。
3. 所以本质上是求 \\(arg: max_{关于w, b} \frac{1}{||w||} \\);也就说,我们约束(前提)条件是: \\(lable*(w^Tx+b)=1\\)
* 新的目标函数求解: \\(arg: max_{关于w, b} \frac{1}{||w||} \\)
* => 就是求: \\(arg: min_{关于w, b} ||w|| \\) (求矩阵会比较麻烦如果x只是 \\(\frac{1}{2}*x^2\\) 的偏导数,那么。。同样是求最小值)
@@ -88,32 +87,30 @@ This is the simplest kind of SVM (Called an LSVM) Support Vectors are those data
* 设g(x,y)=M-φ(x,y) # 临时φ(x,y)表示下文中 \\(label*(w^Tx+b)\\)
* 定义一个新函数: F(x,y,λ)=f(x,y)+λg(x,y)
* a为λa>=0代表要引入的拉格朗日乘子(Lagrange multiplier)
* 那么: \\(L(w,b,\alpha)=\frac{1}{2} * ||w||^2 + \sum_{i=1}^{n} \alpha * [1 - label * (w^Tx+b)]\\)
* 因为:\\(label*(w^Tx+b)>=1, \alpha>=0\\) , 所以 \\(\alpha*[1-label*(w^Tx+b)]<=0\\) , \\(\sum_{i=1}^{n} \alpha * [1-label*(w^Tx+b)]<=0\\)
* 那么: \\(L(w,b,\alpha)=\frac{1}{2} * ||w||^2 + \sum_{i=1}^{n} \alpha_i * [1 - label * (w^Tx+b)]\\)
* 因为:\\(label*(w^Tx+b)>=1, \alpha>=0\\) , 所以 \\(\alpha*[1-label*(w^Tx+b)]<=0\\) , \\(\sum_{i=1}^{n} \alpha_i * [1-label*(w^Tx+b)]<=0\\)
* 相当于求解: \\(max_{关于\alpha} L(w,b,\alpha) = \frac{1}{2} *||w||^2\\)
* 如果求: \\(min_{关于w, b} \frac{1}{2} *||w||^2\\) , 也就是要求: \\(min_{关于w, b} \left( max_{关于\alpha} L(w,b,\alpha)\right)\\)
* 现在转化到对偶问题的求解
* \\(min_{关于w, b} \left(max_{关于\alpha} L(w,b,\alpha) \right) \\) >= \\(max_{关于\alpha} \left(min_{关于w, b}\ L(w,b,\alpha) \right) \\)
* 现在分2步
* 先求: \\(min_{关于w, b} L(w,b,\alpha)=\frac{1}{2} * ||w||^2 + \sum_{i=1}^{n} \alpha * [1 - label * (w^Tx+b)]\\)
* 先求: \\(min_{关于w, b} L(w,b,\alpha)=\frac{1}{2} * ||w||^2 + \sum_{i=1}^{n} \alpha_i * [1 - label * (w^Tx+b)]\\)
* 就是求`L(w,b,a)`关于[w, b]的偏导数, 得到`w和b的值`,并化简为:`L和a的方程`
* 参考: 如果公式推导还是不懂,也可以参考《统计学习方法》李航-P103<学习的对偶算法>
![计算拉格朗日函数的对偶函数](/images/6.SVM/SVM_5_Lagrangemultiplier.png)
* 终于得到课本上的公式: \\(max\ \{\alpha\}\ \left( \sum_{i=1}^{m} \alpha_i - \frac{1}{2} \sum_{i, j=1}^{m} label_i·label_j·\alpha_i·\alpha_j·<x_i, x_j> \right) \\)
* 终于得到课本上的公式: \\(max_{关于\alpha} \left( \sum_{i=1}^{m} \alpha_i - \frac{1}{2} \sum_{i, j=1}^{m} label_i·label_j·\alpha_i·\alpha_j·<x_i, x_j> \right) \\)
* 约束条件: \\(a>=0\\) 并且 \\(\sum_{i=1}^{m} a_i·label_i=0\\)
> 松弛变量(slack variable)
* 我们知道几乎所有的数据都不那么干净, 通过引入松弛变量来`允许数据点可以处于分隔面错误的一侧`
* 约束条件: \\(C>=a>=0,\ and\ \sum_{i=1}^{m} a_i·label_i=0\\)
* 约束条件: \\(C>=a>=0\\) 并且 \\(\sum_{i=1}^{m} a_i·label_i=0\\)
* 这里常量C用于控制“最大化间隔”和“保证大部分点的函数间隔小于1.0” 这两个目标的权重。
* 常量C是一个常数我们通过调节该参数得到不同的结果。一旦求出了所有的alpha那么分隔超平面就可以通过这些alpha来表示。
* 这一结论十分直接SVM中的主要工作就是要求解 alpha.
### SMO高效优化算法
SMO 是 SVM最流行的一种实现
### SMO 高效优化算法
* SVM有很多种实现最流行的一种实现是 `序列最小优化(Sequential Minimal Optimization, SMO)算法`
* 下面还会介绍一种称为`核函数(kernel)`的方式将SVM扩展到更多数据集上。
@@ -164,14 +161,170 @@ SMO 是 SVM最流行的一种实现
使用数据类型:数值型和标称型数据。
```
> SVM简化版应用简化版SMO算法处理小规模数据集
### 课本案例(无核函数)
代码可参考 svm-simple.py
#### 项目概述
> SVM完整版使用完整 Platt SMO算法加速优化
对小规模数据点进行分类
代码可参考 svm-complete_Non-Kernel.py
* 优化点选择alpha的方式不同。
#### 开发流程
> 收集数据
文本文件格式:
```python
3.542485 1.977398 -1
3.018896 2.556416 -1
7.551510 -1.580030 1
2.114999 -0.004466 -1
8.127113 1.274372 1
```
> 准备数据
```python
def loadDataSet(fileName):
"""
对文件进行逐行解析,从而得到第行的类标签和整个特征矩阵
Args:
fileName 文件名
Returns:
dataMat 特征矩阵
labelMat 类标签
"""
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
```
> 分析数据: 无
> 训练算法
```python
def smoSimple(dataMatIn, classLabels, C, toler, maxIter):
"""smoSimple
Args:
dataMatIn 特征集合
classLabels 类别标签
C 松弛变量(常量值),允许有些数据点可以处于分隔面的错误一侧。
控制最大化间隔和保证大部分的函数间隔小于1.0这两个目标的权重。
可以通过调节该参数达到不同的结果。
toler 容错率(是指在某个体系中能减小一些因素或选择对某个系统产生不稳定的概率。)
maxIter 退出前最大的循环次数
Returns:
b 模型的常量值
alphas 拉格朗日乘子
"""
dataMatrix = mat(dataMatIn)
# 矩阵转置 和 .T 一样的功能
labelMat = mat(classLabels).transpose()
m, n = shape(dataMatrix)
# 初始化 b和alphas(alpha有点类似权重值。)
b = 0
alphas = mat(zeros((m, 1)))
# 没有任何alpha改变的情况下遍历数据的次数
iter = 0
while (iter < maxIter):
# w = calcWs(alphas, dataMatIn, classLabels)
# print("w:", w)
# 记录alpha是否已经进行优化每次循环时设为0然后再对整个集合顺序遍历
alphaPairsChanged = 0
for i in range(m):
# print 'alphas=', alphas
# print 'labelMat=', labelMat
# print 'multiply(alphas, labelMat)=', multiply(alphas, labelMat)
# 我们预测的类别 y[i] = w^Tx[i]+b; 其中因为 w = Σ(1~n) a[n]*lable[n]*x[n]
fXi = float(multiply(alphas, labelMat).T*(dataMatrix*dataMatrix[i, :].T)) + b
# 预测结果与真实结果比对计算误差Ei
Ei = fXi - float(labelMat[i])
# 约束条件 (KKT条件是解决最优化问题的时用到的一种方法。我们这里提到的最优化问题通常是指对于给定的某一函数求其在指定作用域上的全局最小值)
# 0<=alphas[i]<=C但由于0和C是边界值我们无法进行优化因为需要增加一个alphas和降低一个alphas。
# 表示发生错误的概率labelMat[i]*Ei 如果超出了 toler 才需要优化。至于正负号,我们考虑绝对值就对了。
'''
# 检验训练样本(xi, yi)是否满足KKT条件
yi*f(i) >= 1 and alpha = 0 (outside the boundary)
yi*f(i) == 1 and 0<alpha< C (on the boundary)
yi*f(i) <= 1 and alpha = C (between the boundary)
'''
if ((labelMat[i]*Ei < -toler) and (alphas[i] < C)) or ((labelMat[i]*Ei > toler) and (alphas[i] > 0)):
# 如果满足优化的条件我们就随机选取非i的一个点进行优化比较
j = selectJrand(i, m)
# 预测j的结果
fXj = float(multiply(alphas, labelMat).T*(dataMatrix*dataMatrix[j, :].T)) + b
Ej = fXj - float(labelMat[j])
alphaIold = alphas[i].copy()
alphaJold = alphas[j].copy()
# L和H用于将alphas[j]调整到0-C之间。如果L==H就不做任何改变直接执行continue语句
# labelMat[i] != labelMat[j] 表示异侧,就相减,否则是同侧,就相加。
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是alphas[j]的最优修改量如果eta==0需要退出for循环的当前迭代过程
# 参考《统计学习方法》李航-P125~P128<序列最小最优化算法>
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]值
alphas[j] -= labelMat[j]*(Ei - Ej)/eta
# 并使用辅助函数以及L和H对其进行调整
alphas[j] = clipAlpha(alphas[j], H, L)
# 检查alpha[j]是否只是轻微的改变如果是的话就退出for循环。
if (abs(alphas[j] - alphaJold) < 0.00001):
print("j not moving enough")
continue
# 然后alphas[i]和alphas[j]同样进行改变,虽然改变的大小一样,但是改变的方向正好相反
alphas[i] += labelMat[j]*labelMat[i]*(alphaJold - alphas[j])
# 在对alpha[i], alpha[j] 进行优化之后给这两个alpha值设置一个常数b。
# w= Σ[1~n] ai*yi*xi => b = yj- Σ[1~n] ai*yi(xi*xj)
# 所以: b1 - b = (y1-y) - Σ[1~n] yi*(a1-a)*(xi*x1)
# 为什么减2遍 因为是 减去Σ[1~n]正好2个变量i和j所以减2遍
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))
# 在for循环外检查alpha值是否做了更新如果在更新则将iter设为0后继续运行程序
# 知道更新完毕后iter次循环无变化才推出循环。
if (alphaPairsChanged == 0):
iter += 1
else:
iter = 0
print("iteration number: %d" % iter)
return b, alphas
```
[完整代码地址SVM简化版应用简化版SMO算法处理小规模数据集](https://github.com/apachecn/MachineLearning/blob/master/src/python/6.SVM/svm-simple.py): <https://github.com/apachecn/MachineLearning/blob/master/src/python/6.SVM/svm-simple.py>
[完整代码地址SVM完整版使用完整 Platt SMO算法加速优化优化点选择alpha的方式不同](https://github.com/apachecn/MachineLearning/blob/master/src/python/6.SVM/svm-complete_Non-Kernel.py): <https://github.com/apachecn/MachineLearning/blob/master/src/python/6.SVM/svm-complete_Non-Kernel.py>
## 核函数(kernel) 使用
@@ -184,24 +337,222 @@ SMO 是 SVM最流行的一种实现
* 如果觉得特征空间很装逼、很难理解。
* 可以把核函数想象成一个包装器(wrapper)或者是接口(interface),它能将数据从某个很难处理的形式转换成为另一个较容易处理的形式。
* 经过空间转换后:低维需要解决的非线性问题,就变成了高维需要解决的线性问题。
* SVM 优化特别好的地方,在于所有的运算都可以写成内积(inner product: 是指2个向量相乘得到单个标量 或者 数值);内替换成核函数的方式被称为`核技巧(kernel trick)`或者`核"变电"(kernel substation)`
* SVM 优化特别好的地方,在于所有的运算都可以写成内积(inner product: 是指2个向量相乘得到单个标量 或者 数值);内替换成核函数的方式被称为`核技巧(kernel trick)`或者`核"变电"(kernel substation)`
* 核函数并不仅仅应用于支持向量机,很多其他的机器学习算法也都用到核函数。最流行的核函数:径向基函数(radial basis function)
* 径向基函数的高斯版本,其具体的公式为:
![径向基函数的高斯版本](/images/6.SVM/SVM_6_radial-basis-function.jpg)
## 支持向量机 项目实战
### 项目案例: 手写数字识别的优化(有核函数)
### 项目实战: 手写数字识别的优化
#### 项目概述
```python
你的老板要求你写的那个手写识别程序非常好但是它占用内存太大顾客无法通过无线的方式下载我们的应用
所以我们可以考虑使用支持向量机保留支持向量就行knn需要保留所有的向量就可以获得非常好的效果
```
#### 开发流程
> 收集数据:提供的文本文件
```python
00000000000000001111000000000000
00000000000000011111111000000000
00000000000000011111111100000000
00000000000000011111111110000000
00000000000000011111111110000000
00000000000000111111111100000000
00000000000000111111111100000000
00000000000001111111111100000000
00000000000000111111111100000000
00000000000000111111111100000000
00000000000000111111111000000000
00000000000001111111111000000000
00000000000011111111111000000000
00000000000111111111110000000000
00000000001111111111111000000000
00000001111111111111111000000000
00000011111111111111110000000000
00000111111111111111110000000000
00000111111111111111110000000000
00000001111111111111110000000000
00000001111111011111110000000000
00000000111100011111110000000000
00000000000000011111110000000000
00000000000000011111100000000000
00000000000000111111110000000000
00000000000000011111110000000000
00000000000000011111110000000000
00000000000000011111111000000000
00000000000000011111111000000000
00000000000000011111111000000000
00000000000000000111111110000000
00000000000000000111111100000000
```
> 准备数据:基于二值图像构造向量
`将 32*32的文本转化为 1*1024的矩阵`
```python
def img2vector(filename):
returnVect = zeros((1, 1024))
fr = open(filename)
for i in range(32):
lineStr = fr.readline()
for j in range(32):
returnVect[0, 32 * i + j] = int(lineStr[j])
return returnVect
def loadImages(dirName):
from os import listdir
hwLabels = []
print(dirName)
trainingFileList = listdir(dirName) # load the training set
m = len(trainingFileList)
trainingMat = zeros((m, 1024))
for i in range(m):
fileNameStr = trainingFileList[i]
fileStr = fileNameStr.split('.')[0] # take off .txt
classNumStr = int(fileStr.split('_')[0])
if classNumStr == 9:
hwLabels.append(-1)
else:
hwLabels.append(1)
trainingMat[i, :] = img2vector('%s/%s' % (dirName, fileNameStr))
return trainingMat, hwLabels
```
> 分析数据:对图像向量进行目测
> 训练算法采用两种不同的核函数并对径向基核函数采用不同的设置来运行SMO算法
```python
def kernelTrans(X, A, kTup): # calc the kernel or transform data to a higher dimensional space
"""
核转换函数
Args:
X dataMatIn数据集
A dataMatIn数据集的第i行的数据
kTup 核函数的信息
Returns:
"""
m, n = shape(X)
K = mat(zeros((m, 1)))
if kTup[0] == 'lin':
# linear kernel: m*n * n*1 = m*1
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)) # 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
def smoP(dataMatIn, classLabels, C, toler, maxIter, kTup=('lin', 0)):
"""
完整SMO算法外循环与smoSimple有些类似但这里的循环退出条件更多一些
Args:
dataMatIn 数据集
classLabels 类别标签
C 松弛变量(常量值),允许有些数据点可以处于分隔面的错误一侧。
控制最大化间隔和保证大部分的函数间隔小于1.0这两个目标的权重。
可以通过调节该参数达到不同的结果。
toler 容错率
maxIter 退出前最大的循环次数
kTup 包含核函数信息的元组
Returns:
b 模型的常量值
alphas 拉格朗日乘子
"""
# 创建一个 optStruct 对象
oS = optStruct(mat(dataMatIn), mat(classLabels).transpose(), C, toler, kTup)
iter = 0
entireSet = True
alphaPairsChanged = 0
# 循环遍历循环maxIter次 并且 alphaPairsChanged存在可以改变 or 所有行遍历一遍)
while (iter < maxIter) and ((alphaPairsChanged > 0) or (entireSet)):
alphaPairsChanged = 0
# 当entireSet=true or 非边界alpha对没有了就开始寻找 alpha对然后决定是否要进行else。
if entireSet:
# 在数据集上遍历所有可能的alpha
for i in range(oS.m):
# 是否存在alpha对存在就+1
alphaPairsChanged += innerL(i, oS)
# print("fullSet, iter: %d i:%d, pairs changed %d" % (iter, i, alphaPairsChanged))
iter += 1
# 对已存在 alpha对选出非边界的alpha值进行优化。
else:
# 遍历所有的非边界alpha值也就是不在边界0或C上的值。
nonBoundIs = nonzero((oS.alphas.A > 0) * (oS.alphas.A < C))[0]
for i in nonBoundIs:
alphaPairsChanged += innerL(i, oS)
# print("non-bound, iter: %d i:%d, pairs changed %d" % (iter, i, alphaPairsChanged))
iter += 1
# 如果找到alpha对就优化非边界alpha值否则就重新进行寻找如果寻找一遍 遍历所有的行还是没找到,就退出循环。
if entireSet:
entireSet = False # toggle entire set loop
elif (alphaPairsChanged == 0):
entireSet = True
print("iteration number: %d" % iter)
return oS.b, oS.alphas
```
> 测试算法:便携一个函数来测试不同的和函数并计算错误率
```python
def testDigits(kTup=('rbf', 10)):
# 1. 导入训练数据
dataArr, labelArr = loadImages('input/6.SVM/trainingDigits')
b, alphas = smoP(dataArr, labelArr, 200, 0.0001, 10000, kTup)
datMat = mat(dataArr)
labelMat = mat(labelArr).transpose()
svInd = nonzero(alphas.A > 0)[0]
sVs = datMat[svInd]
labelSV = labelMat[svInd]
# print("there are %d Support Vectors" % shape(sVs)[0])
m, n = shape(datMat)
errorCount = 0
for i in range(m):
kernelEval = kernelTrans(sVs, datMat[i, :], kTup)
# 1*m * m*1 = 1*1 单个预测结果
predict = kernelEval.T * multiply(labelSV, alphas[svInd]) + b
if sign(predict) != sign(labelArr[i]): errorCount += 1
print("the training error rate is: %f" % (float(errorCount) / m))
# 2. 导入测试数据
dataArr, labelArr = loadImages('input/6.SVM/testDigits')
errorCount = 0
datMat = mat(dataArr)
labelMat = mat(labelArr).transpose()
m, n = shape(datMat)
for i in range(m):
kernelEval = kernelTrans(sVs, datMat[i, :], kTup)
# 1*m * m*1 = 1*1 单个预测结果
predict = kernelEval.T * multiply(labelSV, alphas[svInd]) + b
if sign(predict) != sign(labelArr[i]): errorCount += 1
print("the test error rate is: %f" % (float(errorCount) / m))
```
> 使用算法:一个图像识别的完整应用还需要一些图像处理的知识,这里并不打算深入介绍
[完整代码地址](https://github.com/apachecn/MachineLearning/blob/master/src/python/6.SVM/svm-complete.py): <https://github.com/apachecn/MachineLearning/blob/master/src/python/6.SVM/svm-complete.py>
* * *
* **作者:[片刻](http://www.apache.wiki/display/~jiangzhonglian) [geekidentity](http://www.apache.wiki/display/~houfachao)**
* **作者:[片刻](http://cwiki.apachecn.org/display/~jiangzhonglian) [geekidentity](http://cwiki.apachecn.org/display/~houfachao)**
* [GitHub地址](https://github.com/apachecn/MachineLearning): <https://github.com/apachecn/MachineLearning>
* **版权声明:欢迎转载学习 => 请标注信息来源于 [ApacheCN](http://www.apachecn.org/)**

View File

@@ -61,7 +61,7 @@ def kernelTrans(X, A, kTup): # calc the kernel or transform data to a higher di
m, n = shape(X)
K = mat(zeros((m, 1)))
if kTup[0] == 'lin':
# linear kernel: m*n * n*1 = m*1, 求y值
# linear kernel: m*n * n*1 = m*1
K = X * A.T
elif kTup[0] == 'rbf':
for j in range(m):
@@ -227,9 +227,15 @@ def innerL(i, oS):
# 求 Ek误差预测值-真实值的差
Ei = calcEk(oS, i)
# 约束条件 (KKT条件是解决最优化问题的时用到的一种方法。我们这里提到的最优化问题通常是指对于给定的某一函数求其在指定作用域上的全局最小值)
# 约束条件 (KKT条件是解决最优化问题的时用到的一种方法。我们这里提到的最优化问题通常是指对于给定的某一函数求其在指定作用域上的全局最小值)
# 0<=alphas[i]<=C但由于0和C是边界值我们无法进行优化因为需要增加一个alphas和降低一个alphas。
# 表示发生错误的概率labelMat[i]*Ei 如果超出了 toler 才需要优化。至于正负号,我们考虑绝对值就对了。
'''
# 检验训练样本(xi, yi)是否满足KKT条件
yi*f(i) >= 1 and alpha = 0 (outside the boundary)
yi*f(i) == 1 and 0<alpha< C (on the boundary)
yi*f(i) <= 1 and alpha = C (between the boundary)
'''
if ((oS.labelMat[i] * Ei < -oS.tol) and (oS.alphas[i] < oS.C)) or ((oS.labelMat[i] * Ei > oS.tol) and (oS.alphas[i] > 0)):
# 选择最大的误差对应的j进行优化。效果更明显
j, Ej = selectJ(i, oS, Ei)
@@ -244,7 +250,7 @@ def innerL(i, oS):
L = max(0, oS.alphas[j] + oS.alphas[i] - oS.C)
H = min(oS.C, oS.alphas[j] + oS.alphas[i])
if L == H:
print("L==H")
# print("L==H")
return 0
# eta是alphas[j]的最优修改量如果eta==0需要退出for循环的当前迭代过程
@@ -263,7 +269,7 @@ def innerL(i, oS):
# 检查alpha[j]是否只是轻微的改变如果是的话就退出for循环。
if (abs(oS.alphas[j] - alphaJold) < 0.00001):
print("j not moving enough")
# print("j not moving enough")
return 0
# 然后alphas[i]和alphas[j]同样进行改变,虽然改变的大小一样,但是改变的方向正好相反
@@ -274,7 +280,7 @@ def innerL(i, oS):
# 在对alpha[i], alpha[j] 进行优化之后给这两个alpha值设置一个常数b。
# w= Σ[1~n] ai*yi*xi => b = yi- Σ[1~n] ai*yi(xi*xj)
# 所以: b1 - b = (y1-y) - Σ[1~n] yi*(a1-a)*(xi*x1)
# 为什么减2遍 因为是 减去Σ[1~n]好2个变量i和j所以减2遍
# 为什么减2遍 因为是 减去Σ[1~n]好2个变量i和j所以减2遍
b1 = oS.b - Ei - oS.labelMat[i] * (oS.alphas[i] - alphaIold) * oS.K[i, i] - oS.labelMat[j] * (oS.alphas[j] - alphaJold) * oS.K[i, j]
b2 = oS.b - Ej - oS.labelMat[i] * (oS.alphas[i] - alphaIold) * oS.K[i, j] - oS.labelMat[j] * (oS.alphas[j] - alphaJold) * oS.K[j, j]
if (0 < oS.alphas[i]) and (oS.C > oS.alphas[i]):
@@ -321,7 +327,7 @@ def smoP(dataMatIn, classLabels, C, toler, maxIter, kTup=('lin', 0)):
for i in range(oS.m):
# 是否存在alpha对存在就+1
alphaPairsChanged += innerL(i, oS)
print("fullSet, iter: %d i:%d, pairs changed %d" % (iter, i, alphaPairsChanged))
# print("fullSet, iter: %d i:%d, pairs changed %d" % (iter, i, alphaPairsChanged))
iter += 1
# 对已存在 alpha对选出非边界的alpha值进行优化。
@@ -330,7 +336,7 @@ def smoP(dataMatIn, classLabels, C, toler, maxIter, kTup=('lin', 0)):
nonBoundIs = nonzero((oS.alphas.A > 0) * (oS.alphas.A < C))[0]
for i in nonBoundIs:
alphaPairsChanged += innerL(i, oS)
print("non-bound, iter: %d i:%d, pairs changed %d" % (iter, i, alphaPairsChanged))
# print("non-bound, iter: %d i:%d, pairs changed %d" % (iter, i, alphaPairsChanged))
iter += 1
# 如果找到alpha对就优化非边界alpha值否则就重新进行寻找如果寻找一遍 遍历所有的行还是没找到,就退出循环。
@@ -434,11 +440,12 @@ def testDigits(kTup=('rbf', 10)):
svInd = nonzero(alphas.A > 0)[0]
sVs = datMat[svInd]
labelSV = labelMat[svInd]
print("there are %d Support Vectors" % shape(sVs)[0])
# print("there are %d Support Vectors" % shape(sVs)[0])
m, n = shape(datMat)
errorCount = 0
for i in range(m):
kernelEval = kernelTrans(sVs, datMat[i, :], kTup)
# 1*m * m*1 = 1*1 单个预测结果
predict = kernelEval.T * multiply(labelSV, alphas[svInd]) + b
if sign(predict) != sign(labelArr[i]): errorCount += 1
print("the training error rate is: %f" % (float(errorCount) / m))
@@ -497,27 +504,32 @@ def plotfig_SVM(xArr, yArr, ws, b, alphas):
if __name__ == "__main__":
# 无核函数的测试
# 获取特征和目标变量
dataArr, labelArr = loadDataSet('input/6.SVM/testSet.txt')
# print labelArr
# # 无核函数的测试
# # 获取特征和目标变量
# dataArr, labelArr = loadDataSet('input/6.SVM/testSet.txt')
# # print labelArr
# b是常量值 alphas是拉格朗日乘子
b, alphas = smoP(dataArr, labelArr, 0.6, 0.001, 40)
print '/n/n/n'
print 'b=', b
print 'alphas[alphas>0]=', alphas[alphas > 0]
print 'shape(alphas[alphas > 0])=', shape(alphas[alphas > 0])
for i in range(100):
if alphas[i] > 0:
print dataArr[i], labelArr[i]
# 画图
ws = calcWs(alphas, dataArr, labelArr)
plotfig_SVM(dataArr, labelArr, ws, b, alphas)
# # b是常量值 alphas是拉格朗日乘子
# b, alphas = smoP(dataArr, labelArr, 0.6, 0.001, 40)
# print '/n/n/n'
# print 'b=', b
# print 'alphas[alphas>0]=', alphas[alphas > 0]
# print 'shape(alphas[alphas > 0])=', shape(alphas[alphas > 0])
# for i in range(100):
# if alphas[i] > 0:
# print dataArr[i], labelArr[i]
# # 画图
# ws = calcWs(alphas, dataArr, labelArr)
# plotfig_SVM(dataArr, labelArr, ws, b, alphas)
# # 有核函数的测试
# 有核函数的测试
# testRbf(0.8)
# 项目实战
# 示例:手写识别问题回顾
# testDigits(('rbf', 20))
testDigits(('rbf', 0.1))
testDigits(('rbf', 5))
testDigits(('rbf', 10))
testDigits(('rbf', 50))
testDigits(('rbf', 100))
testDigits(('lin'))

View File

@@ -176,9 +176,15 @@ def innerL(i, oS):
# 求 Ek误差预测值-真实值的差
Ei = calcEk(oS, i)
# 约束条件 (KKT条件是解决最优化问题的时用到的一种方法。我们这里提到的最优化问题通常是指对于给定的某一函数求其在指定作用域上的全局最小值)
# 约束条件 (KKT条件是解决最优化问题的时用到的一种方法。我们这里提到的最优化问题通常是指对于给定的某一函数求其在指定作用域上的全局最小值)
# 0<=alphas[i]<=C但由于0和C是边界值我们无法进行优化因为需要增加一个alphas和降低一个alphas。
# 表示发生错误的概率labelMat[i]*Ei 如果超出了 toler 才需要优化。至于正负号,我们考虑绝对值就对了。
'''
# 检验训练样本(xi, yi)是否满足KKT条件
yi*f(i) >= 1 and alpha = 0 (outside the boundary)
yi*f(i) == 1 and 0<alpha< C (on the boundary)
yi*f(i) <= 1 and alpha = C (between the boundary)
'''
if ((oS.labelMat[i] * Ei < -oS.tol) and (oS.alphas[i] < oS.C)) or ((oS.labelMat[i] * Ei > oS.tol) and (oS.alphas[i] > 0)):
# 选择最大的误差对应的j进行优化。效果更明显
j, Ej = selectJ(i, oS, Ei)
@@ -223,7 +229,7 @@ def innerL(i, oS):
# 在对alpha[i], alpha[j] 进行优化之后给这两个alpha值设置一个常数b。
# w= Σ[1~n] ai*yi*xi => b = yj Σ[1~n] ai*yi(xi*xj)
# 所以: b1 - b = (y1-y) - Σ[1~n] yi*(a1-a)*(xi*x1)
# 为什么减2遍 因为是 减去Σ[1~n]好2个变量i和j所以减2遍
# 为什么减2遍 因为是 减去Σ[1~n]好2个变量i和j所以减2遍
b1 = oS.b - Ei - oS.labelMat[i] * (oS.alphas[i] - alphaIold) * oS.X[i, :] * oS.X[i, :].T - oS.labelMat[j] * (oS.alphas[j] - alphaJold) * oS.X[i, :] * oS.X[j, :].T
b2 = oS.b - Ej - oS.labelMat[i] * (oS.alphas[i] - alphaIold) * oS.X[i, :] * oS.X[j, :].T - oS.labelMat[j] * (oS.alphas[j] - alphaJold) * oS.X[j, :] * oS.X[j, :].T
if (0 < oS.alphas[i]) and (oS.C > oS.alphas[i]):
@@ -260,6 +266,7 @@ def smoP(dataMatIn, classLabels, C, toler, maxIter):
alphaPairsChanged = 0
# 循环遍历循环maxIter次 并且 alphaPairsChanged存在可以改变 or 所有行遍历一遍)
# 循环迭代结束 或者 循环遍历所有alpha后alphaPairs还是没变化
while (iter < maxIter) and ((alphaPairsChanged > 0) or (entireSet)):
alphaPairsChanged = 0
@@ -271,7 +278,6 @@ def smoP(dataMatIn, classLabels, C, toler, maxIter):
alphaPairsChanged += innerL(i, oS)
print("fullSet, iter: %d i:%d, pairs changed %d" % (iter, i, alphaPairsChanged))
iter += 1
# 对已存在 alpha对选出非边界的alpha值进行优化。
else:
# 遍历所有的非边界alpha值也就是不在边界0或C上的值。

View File

@@ -14,11 +14,11 @@ import matplotlib.pyplot as plt
def loadDataSet(fileName):
"""
对文件进行逐行解析,从而得到第行的类标签和整个数据矩阵
对文件进行逐行解析,从而得到第行的类标签和整个特征矩阵
Args:
fileName 文件名
Returns:
dataMat 数据矩阵
dataMat 特征矩阵
labelMat 类标签
"""
dataMat = []
@@ -103,11 +103,15 @@ def smoSimple(dataMatIn, classLabels, C, toler, maxIter):
# 预测结果与真实结果比对计算误差Ei
Ei = fXi - float(labelMat[i])
# 约束条件 (KKT条件是解决最优化问题的时用到的一种方法。我们这里提到的最优化问题通常是指对于给定的某一函数求其在指定作用域上的全局最小值)
# 约束条件 (KKT条件是解决最优化问题的时用到的一种方法。我们这里提到的最优化问题通常是指对于给定的某一函数求其在指定作用域上的全局最小值)
# 0<=alphas[i]<=C但由于0和C是边界值我们无法进行优化因为需要增加一个alphas和降低一个alphas。
# 表示发生错误的概率labelMat[i]*Ei 如果超出了 toler 才需要优化。至于正负号,我们考虑绝对值就对了。
# 如果你大于 负错误的概率那么要求你小于C这样才可以只优化一边
# 如果你大于 正错误的概率那么要求你大于0这样才可以只优化一边
'''
# 检验训练样本(xi, yi)是否满足KKT条件
yi*f(i) >= 1 and alpha = 0 (outside the boundary)
yi*f(i) == 1 and 0<alpha< C (on the boundary)
yi*f(i) <= 1 and alpha = C (between the boundary)
'''
if ((labelMat[i]*Ei < -toler) and (alphas[i] < C)) or ((labelMat[i]*Ei > toler) and (alphas[i] > 0)):
# 如果满足优化的条件我们就随机选取非i的一个点进行优化比较
@@ -133,7 +137,7 @@ def smoSimple(dataMatIn, classLabels, C, toler, maxIter):
# eta是alphas[j]的最优修改量如果eta==0需要退出for循环的当前迭代过程
# 参考《统计学习方法》李航-P125~P128<序列最小最优化算法>
eta = 2.0 * dataMatrix[i, :]*dataMatrix[j, :].T - dataMatrix[i, :]*dataMatrix[i,:].T - dataMatrix[j, :]*dataMatrix[j, :].T
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
@@ -151,9 +155,9 @@ def smoSimple(dataMatIn, classLabels, C, toler, maxIter):
# 在对alpha[i], alpha[j] 进行优化之后给这两个alpha值设置一个常数b。
# w= Σ[1~n] ai*yi*xi => b = yj- Σ[1~n] ai*yi(xi*xj)
# 所以: b1 - b = (y1-y) - Σ[1~n] yi*(a1-a)*(xi*x1)
# 为什么减2遍 因为是 减去Σ[1~n]好2个变量i和j所以减2遍
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
# 为什么减2遍 因为是 减去Σ[1~n]好2个变量i和j所以减2遍
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]):