【机器学习算法原理及其实现剖析】SVM篇

往往我们只是知道了简单的证明,应付了考试,但是却没有把所学的东西,真正的去实践和应用一番,又可能苦于代码或理论难理解,而选择直接放弃。

本主题【机器学习算法原理及其实现剖析】就是,把常用的机器学习算法的原理实现和对应理论支撑相结合,希望能提供一种不同的学习思路。

SVM

如果想明白一个东西,第一步就是知道它用来干什么的?也就是解决了什么问题。

1、解决什么问题?

最基本的应用是数据分类,特别是对于非线性不可分数据集。支持向量机不仅能对非线性可分数据集进行分类,对于非线性不可分数据集的也可以分类(这也是SVM应用广泛的重要原因)

现实场景一 :样本数据大部分是线性可分的,但是只是在样本中含有少量噪声或特异点,去掉这些噪声或特异点后线性可分 => 用支持向量机的软间隔方法进行分类;

现实场景二:样本数据完全线性不可分 => 引入核函数, 将低维不可分的非线性数据集转化为高维可分的数据集,用支持向量机的软间隔方法进行分类;

对分类器准确性有影响只有样本中的支持向量,支持向量机的名字由来也正是如此。
所以,问题就转化为获取离边界最近样本点(支持向量)到超平面最远距离。在代码中就是一个entireSet标志,地磁全部遍历,之后就变遍历支持向量,此处以SMO代码举例。

    # 全量遍历标志
    entireSet = True
    # alpha对是否优化标志
    alphaPairsChanged = 0
    # 外循环 终止条件:1.达到最大次数 或者 2.alpha对没有优化
    while (iter < maxInter) and ((alphaPairsChanged > 0) or (entireSet)):
        alphaPairsChanged = 0
        # 全量遍历 ,遍历每一行数据 alpha对有修改,alphaPairsChanged累加
        if entireSet:
            for i in range(oS.m):
                alphaPairsChanged += innerL(i, oS)
                print("fullSet, iter: %d i:%d, pairs changed %d" % (iter, i, alphaPairsChanged))
            iter += 1
        else:
            # 获取(0,C)范围内数据索引列表,也就是只遍历属于支持向量的数据
            nonBounds = np.nonzero((oS.alphas.A > 0) * (oS.alphas.A < C))[0]  # .A将mat转换为array
            for i in nonBounds:
                alphaPairsChanged += innerL(i, oS)
                print("non-bound, iter: %d i:%d, pairs changed %d" % (iter, i, alphaPairsChanged))
            iter += 1
        # 全量遍历->支持向量遍历
        if entireSet:
            entireSet = False
        # 支持向量遍历->全量遍历
        elif alphaPairsChanged == 0:  # 如果不存在成对值α可以改变,就全部遍历
            entireSet = True
        print("iteation number: %d" % iter)
        print("entireSet :%s" % entireSet)
        print("alphaPairsChanged :%d" % alphaPairsChanged)
    return oS.b, oS.alphas```

1.1 软间隔问题

但是这样就有一个问题(软间隔问题),如果支持向量是离群点的时候,就会影响最终结果,于是引入松弛变量。以hinge损失为例,
在这里插入图片描述
这个公式不好懂,没关系,我们可以可视化成下图这样,
在这里插入图片描述
因此,最终的优化目标函数变成了两个部分组成:距离函数和松弛变量误差
上式中,我们加入权重参数C,将其与目标函数中的松弛变量误差相乘,这样,就可以通过调整C来对二者的系数进行调和。
在这里插入图片描述
简单概括一下规律
(1) α \alpha α = C, 松弛系数在0-1之间,是分类没有合并的
(2)松弛系数为0,表示分类正确
(3) α \alpha α = C ,松弛系数大于1 表示分类错误。

1.2 非线性不可分问题

思想:对于在N维空间中线性不可分的数据,在N+1维以上的空间会有更大到可能变成线性可分的。
这样,我们将原问题变成了如何对原始数据进行映射,才能使其在新空间中线性可分。
为了实现这种映射,引入了核函数的方法。
那为什么核函数一定起作用呢?这里给出一段引用

RBF核函数可以将维度扩展到无穷维的空间,因此,理论上讲可以满足一切映射的需求。为什么会是无穷维呢?我以前都不太明白这一点。后来老师讲到,RBF对应的是泰勒级数展开,在泰勒级数中,一个函数可以分解为无穷多个项的加和,其中,每一个项可以看做是对应的一个维度,这样,原函数就可以看做是映射到了无穷维的空间中。这样,在实际应用中,RBF是相对最好的一个选择。当然,如果有研究的话,还可以选用其他核函数,可能会在某些问题上表现更好。但是,RBF是在对问题不了解的情况下,对最广泛问题效果都很不错的核函数。因此,使用范围也最广。
在这里插入图片描述

# 核转换函数(一个特征空间映射到另一个特征空间,低维空间映射到高维空间)
# 高维空间解决线性问题,低维空间解决非线性问题
# 线性内核 = 原始数据矩阵(100*2)与原始数据第一行矩阵转秩乘积(2*1) =>(100*1)
# 非线性内核公式:k(x,y) = exp(-||x - y||**2/2*(e**2))
# 1.原始数据每一行与原始数据第一行作差,
# 2.平方
'''
X: dataMat 原始特征数据
Y: rowDataMat 标签数据
kTup 指定核方式
'''
def kernelTrans(dataMat, rowDataMat, kTup):
    m , n =np.shape(dataMat)
    # 初始化核矩阵 m*1
    K = np.mat(np.zeros((m ,1)))
    # 线性核
    if kTup[0] == 'lin':
        K = dataMat * rowDataMat.T
    # 高斯核
    elif kTup[0] == 'rbf':  # 非线性核
        for j in range(m):
            # xi - xj
            deltaRow = dataMat[j ,:] - rowDataMat
            K[j] = deltaRow *deltaRow.T
        # 1*m m*1 => 1*1
        K = np.exp( K /(- 2 *kTup[1]**2))  # m个样本1列

    # 若需要不同核,下面可以继续添加

    else: raise NameError('Houston We Have a Problem -- That Kernel is not recognized')
    return K

至于拉格朗日对偶法转化目标函数,这里就不再做解释,直接给出优化目标。
在这里插入图片描述

2、实现

大致明白SVM解决什么问题之后,我们以其中一种简单实现SMO来看理论与实现的结合。
一句话,由于拉格朗日变换后的 α \alpha α比较多,SMO算法则采用了一种启发式的方法。它每次只优化两个变量,将其他的变量都视为常数。
换句话说,由于 ∑ i = 1 m α i y i = 0 ∑^m_{i=1}α_iy_i=0 i=1mαiyi=0.假如将 α 3 \alpha_3 α3 α 4 \alpha_4 α4 α m \alpha_m αm 固定,那么 α 1 \alpha_1 α1, α 2 \alpha_2 α2之间的关系也确定了。这样SMO算法将一个复杂的优化算法转化为一个比较简单的两变量优化问题。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值