支持向量机实战(二)简易SMO算法实现---机器学习

版权声明:转载请注明出处: https://blog.csdn.net/weixin_42398658/article/details/83303061

该简易算法其实完成的任务很简单,就是违反KKT条件的alpha进行符合KKT条件,然后通过不在边界的点进行迭代,然后使其alpha稳定下来下面的代码基本每一句都进行了详细的解释,具体看代码,但是建议没搞懂参数是如何更新的同学,一旦把上一篇的参数更新方法彻底搞懂,下一篇将编写完整的SMO代码,然后在详细阐释其工作原理的来龙去脉,这个简单的smo算法主要集中在处理更新方面,并没有考虑优化计算速度,因此下一节将详细解释该算法的实现,本代码参考机器学习实战的代码,代码如下:

#!/usr/bin/env/python
# -*- coding: utf-8 -*-
# Author: 赵守风
# File name: SMO_simplify.py
# Time:2018/10/22
# Email:[email protected]
import numpy as np

#  数据处理
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


# 选择j不等于i的数据
def selectJrand(i, m):
    j = i #we want to select any J not equal to i
    while (j==i):
        j = int(np.random.uniform(0,m))
    return j


# smo的约束条件,前面详解了即    0 ≤alphas≤C,即边界限制
def clipAlpha(aj, H, L):
    if aj > H:
        aj = H
    if L > aj:
        aj = L
    return aj


# 简化版SMO算法
def smoSimple(dataMatIn, classLabels, C, toler, maxIter):
    dataMatrix = np.mat(dataMatIn)  # 把数据转换成numpy的矩阵形式
    labelMat = np.mat(classLabels).transpose()  # 同样处理,同时进行转置处理,此时的标签就是列向量了
    b = 0   # 初始化b
    m,n = np.shape(dataMatrix)  # 获得数据的维度即m行n列,也可以看成是m个样本数据,每个样本有n特征
    alphas = np.mat(np.zeros((m, 1)))  # 初始化alphas,因为alphas是针对每一个样本数据,因此需要m行1列
    iter = 0  # 迭代次数,即记录的是alpha没有任何改变的情况下遍历数据的次数
    while (iter < maxIter):  # 当遍历次数达到最大时退出循环
        alphaPairsChanged = 0  # 大循环,对整个数据集遍历,该变量记录alpha是否已经被优化,循环结束才能知道
        for i in range(m):
            # 下面的式子其实就是f(x) = ∑alpha_i*y_i*<x_i,x> + b,前面理论也详细讲了这一点,就是预测x的类别
            fXi = float(np.multiply(alphas,labelMat).T*(dataMatrix*dataMatrix[i,:].T)) + b
            # 下面就是求在真实值和预测值的差即:E_i = f(x_i) - y_i,其中f是预测值,y是真实分类值
            Ei = fXi - float(labelMat[i]) #if checks if an example violates KKT conditions
            # 下面就是界定非边界的alpha,正负间隔都会被测试,筛选满足KKT条件的,在边界上的alpha等于0或者C,则不再优化
            if ((labelMat[i]*Ei < -toler) and (alphas[i] < C)) or ((labelMat[i]*Ei > toler) and (alphas[i] > 0)):
                j = selectJrand(i,m) # 选择第二个alpha值
                # 下面是求预测值f(x_j)同上解释
                fXj = float(np.multiply(alphas,labelMat).T*(dataMatrix*dataMatrix[j,:].T)) + b
                # 下面是计算误差
                Ej = fXj - float(labelMat[j])
                # 存储更新前的alpha值,在更新时或者比较时使用
                alphaIold = alphas[i].copy()
                alphaJold = alphas[j].copy()
                # 判断选择的i,j数据的标签是否异号,开始计算L和H
                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  # 如果相等,直接跳出本次for循环,直接执行下一次for循环
                # eta,大家还记得上篇的参数更新里的:K_11 + K_22 - 2K_12吗,其实就是这个eta,有点不同,是因为假设条件不一样,但是原理相同
                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 # 满足KKT条件,跳出该循序
                alphas[j] -= labelMat[j]*(Ei - Ej)/eta # 否则更新alpha
                alphas[j] = clipAlpha(alphas[j],H,L)  # 加入约束条件
                if (abs(alphas[j] - alphaJold) < 0.00001):
                    # 查看更新后的alpha是否满足条件,即变化大不大,如果不大,说明该alpha已经趋于稳定了,不用继续更新了
                    print("j not moving enough")
                    continue
                # 同理对i也进行更新,不同的是更新方向不同,这里是加,而j是减,注意细节。忘记的查看一下上篇的alpha1是如何更新的
                alphas[i] += labelMat[j]*labelMat[i]*(alphaJold - alphas[j])#update i by the same amount as j
                                                                        #the update is in the oppostie direction
                # 更新b,和上篇的解释是一样的,看不懂的继续看理论
                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 # 当运行到这里说明alpha已经进行更新了,因此加一
                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

下面给出测试代码和测试结果:

#!/usr/bin/env/python
# -*- coding: utf-8 -*-
# Author: 赵守风
# File name: test.py
# Time:2018/10/23
# Email:[email protected]
import SMO_simplify

dataarr, labelarr = SMO_simplify.loadDataSet('testSet.txt')
print(labelarr)

b, alphas = SMO_simplify.smoSimple(dataarr, labelarr, 0.6, 0.0001, 40)
print('b 的值为:', b)
print('alphas[alphas>0]: ', alphas[alphas > 0])

for i in range(100):
    if alphas[i] > 0.0:
        print('dataarr[i],labelarr[i]为:', dataarr[i], labelarr[i])

测试结果:

......
........
........
iteration number: 38
j not moving enough
j not moving enough
iteration number: 39
j not moving enough
j not moving enough
iteration number: 40
b 的值为: [[-3.87921175]]
alphas[alphas>0]:  [[0.08331274 0.27824106 0.04908614 0.31246766]]
dataarr[i],labelarr[i]为: [4.658191, 3.507396] -1.0
dataarr[i],labelarr[i]为: [3.457096, -0.082216] -1.0
dataarr[i],labelarr[i]为: [5.286862, -2.358286] 1.0
dataarr[i],labelarr[i]为: [6.080573, 0.418886] 1.0

猜你喜欢

转载自blog.csdn.net/weixin_42398658/article/details/83303061