2019.6.9svm之smo算法

SMO 高效优化算法
SVM有很多种实现,最流行的一种实现是: 序列最小优化(Sequential Minimal Optimization, SMO)算法。
下面还会介绍一种称为 核函数(kernel) 的方式将SVM扩展到更多数据集上。
注意:SVM几何含义比较直观,但其算法实现较复杂,牵扯大量数学公式的推导。
序列最小优化(Sequential Minimal Optimization, SMO)

创建作者:John Platt
创建时间:1996年
SMO用途:用于训练 SVM
SMO目标:求出一系列 alpha 和 b,一旦求出 alpha,就很容易计算出权重向量 w 并得到分隔超平面。
SMO思想:是将大优化问题分解为多个小优化问题来求解的。
SMO原理:每次循环选择两个 alpha 进行优化处理,一旦找出一对合适的 alpha,那么就增大一个同时减少一个。
这里指的合适必须要符合一定的条件
这两个 alpha 必须要在间隔边界之外
这两个 alpha 还没有进行过区间化处理或者不在边界上。
之所以要同时改变2个 alpha;原因是我们有一个约束条件: (\sum_{i=1}^{m} a_i·label_i=0);如果只是修改一个 alpha,很可能导致约束条件失效。
SMO 伪代码大致如下:
创建一个 alpha 向量并将其初始化为0向量
当迭代次数小于最大迭代次数时(外循环)
对数据集中的每个数据向量(内循环):
如果该数据向量可以被优化
随机选择另外一个数据向量
同时优化这两个向量
如果两个向量都不能被优化,退出内循环
如果所有向量都没被优化,增加迭代数目,继续下一次循环
SVM 算法特点
优点:泛化(由具体的、个别的扩大为一般的,就是说:模型训练完后的新样本)错误率低,计算开销不大,结果易理解。
缺点:对参数调节和核函数的选择敏感,原始分类器不加修改仅适合于处理二分类问题。
使用数据类型:数值型和标称型数据
文本文件格式:

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
准备数据

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
分析数据: 无

训练算法

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]*label[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

猜你喜欢

转载自blog.csdn.net/weixin_43732462/article/details/91355604