《统计学习方法》-李航、《机器学习-西瓜书》-周志华总结+Python代码连载(七)--支持向量机SVM(Support vector machines)

一、支持向量机的概述

给定训练样本集D={(x_1,y_1),(x_2,y_2),...,(x_m,y_m)},y_i\in {-1,1},分类学习最基本的想法就是基于训练集D在样本空间中找到一个划分超平面,将不同样本分开,支持向量机就是讨论并解决怎么找到这样的超平面。

在样本空间中,划分超平面可通过如下线性方程来描述:

                                                                      w^T\cdot x+b=0

其中w=(w_1,w_2,...,w_d)为法向量,决定了超平面的方向;b为位移项,决定了超平面与原点之间的距离,显然超平面可以由w和b共同决定。样本空间中任意点x与超平面的距离可以写为:

                                                                  r=\frac{\left | w^T\cdot x+b \right |}{\left \| w \right \|}

距离超平面最近的几个训练样本点成为‘支持向量’,该点满足w^T\cdot x+b=0,因此正负支持向量的间隔为\gamma =\tfrac{2}{\left \| w \right \|}

二、支持向量机的原理

2.1 线性支持向量机硬间隔最大化

由一可知道欲找到具有‘最大间隔’的划分超平面,也就是找到满足下列式子的参数,即

                                                                    max_{w,b} \frac{2}{\left \| w \right \|}

                                                                    s.t. y_i(w^T\cdot x_i+b)\geq 1,i=1,2,3,...,m.

显然最大化间隔,可以等价于下面的式子

                                                                  min_{w,b} \frac{1}{2}\left \| w \right \|^2

                                                                 s.t. y_i(w^T\cdot x_i+b)\geq 1,i=1,2,3,...,m.

求解上式采用格拉朗日乘子法得到其的‘对偶问题’,对上述的每条约束条件添加拉格朗日乘子\alpha_i\geq 0,该式子的拉格朗日函数可写为

                                                       L(w,b,\alpha )=\frac{1}{2}\left \| w \right \|^2+\sum_{i=1}^m\alpha _i(1-y_i(w^T\cdot x_i+b))

其中\alpha =(\alpha_1,\alpha_2,..,\alpha_m),L对w,b分别求导后的偏导为零可得

                                                                     w=\sum_{i=1}^m\alpha _iy_ix_i

                                                                     0=\sum_{i=1}^m\alpha _iy_i.

代入原式子可以消去w和b,再得到对偶问题

                                                  max_{\alpha }=\sum_{i=1}^m\alpha _i-\frac{1}{2}\sum_{i=1}^m\sum_{j=1}^m\alpha _i\alpha _jy_iy_jx_i^Tx_j

                                                                     s.t. \sum_{i=1}^m\alpha _iy_i=0,

                                                                  \alpha _i\geqslant 0,i=1,2,...,m.

求解\alpha后,求出w,b即可得到模型

                                                    f(x)=w^T+b=\sum_{i=1}^m\alpha _iy_ix_i^Tx+b.

线性可分硬间隔最大化的支持向量机学习算法:


step1:构造并求解约束最优化问题

                                                               min_{\alpha }=\frac{1}{2}\sum_{i=1}^m\sum_{j=1}^m\alpha _i\alpha _jy_iy_jx_i^Tx_j-\sum_{i=1}^m\alpha _i

                                                               s.t. \sum_{i=1}^m\alpha _iy_i=0,

                                                              \alpha _i\geqslant 0,i=1,2,...,m.

求得最优解\alpha^*=(\alpha _1^*,\alpha _2^*,...,\alpha _m^*)^T

step2:计算 w^*=\sum_{i=1}^m\alpha _i^*y_ix_i,并选择\alpha^*的一个正分量\alpha _j^*>0,计算

                                                            b^*=y_j-\sum_{i=1}^m\alpha _i^*y_ix_i^Tx_j

step3:求得分离超平面

                                                             (w^*)^T\cdot x+b^*=0

分类决策函数:

                                                         f(x)=sign((w^*)^T\cdot x+b^*)


2.2 线性支持向量机软间隔最大化

有些训练数据集不是线性可分的,通常情况下是,将这些不可分的特异点去除,于是剩下大部分的样本点组成的集合是线性可分的。线性不可分意味着这些样本点不能满足函数间隔最大化大于等于1的约束条件

,为了解决这个问题,可以对每个样本点引进一个松弛因子\xi _j\geq 0,使得函数间隔加上松弛因子大于等于1,这样,约束条件变为

                                                                        y_i(w^T\cdot x_i+b)\geqslant 1-\xi _i

因此线性不可分软间隔最大化的支持向量机学习问题为:

                                                                    min_{w,b,\xi } \frac{1}{2}\left \| w \right \|^2+C\sum_{i=1}^m\xi _i

                                                                    s.t. y_i(w^T\cdot x_i+b)\geqslant 1-\xi _i,i=1,2,...,m 

                                                                         \xi \geqslant 0,i=1,2,...,m

对上式子采用拉格朗日乘子法所得到的拉格朗日函数是                                          L(w,b,\xi ,\alpha ,\mu )=\frac{1}{2}\left \|w \right \|^2+C\sum_{i=1}^m\xi _i-\sum_{i=1}^m\alpha _i(y_i(w^T\cdot x+b)-1+\xi _i)-\sum_{i=1}^m\mu _i\xi _i,其中\alpha_i\geqslant 0,\mu _i\geqslant 0

首先求上面拉格朗日函数对w,b,\xi的极小,拉格朗日函数分别对三者求偏导,该偏导数设为0:

                                                             \frac{\partial }{\partial w}L(w,b,\xi ,\alpha ,\mu )=w-\sum_{i=1}^m\alpha _iy_ix_i=0

                                                           \frac{\partial }{\partial b}L(w,b,\xi ,\alpha ,\mu )=\sum_{i=1}^m\alpha _iy_i=0

                                                          \frac{\partial }{\partial \xi _i}L(w,b,\xi ,\alpha ,\mu )=C-\alpha _i-\mu _i=0

可以得到

                                                                      w=\sum_{i=1}^m\alpha _iy_ix_i

                                                                      \sum_{i=1}^m\alpha _iy_i=0

                                                                      C-\alpha _i-\mu _i=0

代入化简后有           min_{w,b,\xi } L(w,b,\xi ,\alpha ,\mu )=-\frac{1}{2}\sum_{i=1}^m\sum_{j=1}^m\alpha _i\alpha _jy_iy_jx_i^T\cdot x_j+\sum_{i=1}^m\alpha _i

再对 min_{w,b,\xi } L(w,b,\xi ,\alpha ,\mu ) 求\alpha的极大,即得对偶问题:

                                                            max_{\alpha } -\frac{1}{2}\sum_{i=1}^m\sum_{j=1}^m\alpha _i\alpha _jy_iy_jx_i^Tx_j+\sum_{i=1}^m\alpha _i

                                                                 s.t. \sum_{i=1}^m\alpha _iy_i=0

                                                                      C-\alpha _i-\mu _i=0

                                                                       \alpha _i\geqslant 0

                                                                       \mu _i\geqslant 0,i=1,2,...,m

 

软间隔支持向量机学习算法:


step1:选择惩罚参数C>0,构造并求解带约束条件问题

                                                                 min_\alpha \frac{1}{2}\sum_{i=1}^m\sum_{j=1}^m\alpha _i\alpha _jy_iy_jx_i^Tx_j-\sum_{i=1}^m\alpha _i      

                                                                s.t.  \sum_{i=1}^m\alpha _iy_i=0

                                                                       0\leqslant \alpha_i\leqslant C,i=1,2,...,m

step2:计算w^*=\sum_{i=1}^m\alpha _i^*y_ix_i               

选择\alpha^*的一个分量\alpha_j^*的适合条件 0< \alpha _j^*< C,并计算 b^*=y_j-\sum_{i=1}^my_i\alpha _i^*x_i^Tx_j

step3:求得分离超平面 

                                                                   (w^*)^T\cdot x+b^*=0

分类决策函数:

                                                           f(x)=sign((w^*)^T\cdot x+b^*)

 


2.3 非线性支持向量机与核函数

对于分类非线性问题,可以使用非线性支持向量机,其主要特点是利用核技巧(kernel trick)。非线性问题往往不好求解,所以希望能用解线性分类问题来解决这个问题,其中采用一个方法就是进行一个非线性变换,将非线性问题转变成线性问题。

设原空间为\chi \subset R^2,x=(x^{(1)},x^{(2)})^T\subset \chi,新空间为\mathbb{Z}\subset R^2,z=(z^{(1)},z^{(2)})^T\subset \mathbb{Z},定义从原空间到新空间的变换:

                                                               z=\phi (x)=((x^{(1)})^{2},(x^{(2)})^{2})^T

原空间\chi \subset R^2变换为新空间\mathbb{Z}\subset R^2,则有原空间的椭圆:

                                                                   w_1(x^{(1)})^2+w_2(x^{(2)})^2+b=0

变换称为新空间中直线

                                                                      w_1z^{(1)}+w_2z^{(2)}+b=0

上面的例子说明,用线性分类方法求解非线性分类问题分为两步:首先使用一个变换将原空间的数据映射到新空间;然后在新空间里用线性分类学习方法从训练数据中学习分类模型,核技巧就属于这样的方法。

\chi是输入空间(欧氏空间R^n的子集或离散集合),又设H为特征空间(希尔伯特空间),如果存在一个从\chi到H的映射

                                                                            \phi (x):\chi \rightarrow H

使得对所有x,z\in \chi,函数K(x,z)满足条件

                                                                                    K(x,z) = \phi (x)\cdot \phi (z)

\phi (x)\phi (z)的内积。

在支持向量机中内积x_i\cdot x_j可以用核函数K(x_i,x_j)=\phi (x_i)\cdot \phi (x_j)来代替,此时对偶问题的目标函数为:

                                                          W(\alpha )=\tfrac{1}{2}\sum_{i=1}^N\sum_{j=1}^N\alpha _i\alpha _jy_iy_jK(x_i,x_j)-\sum_{i=1}^N\alpha _i

同样,分类决策函数中的内积也可以用核函数代替,于是分类决策函数成为:

                                                                            f(x)=sign(\sum_{i=1}^{N_s}\alpha _i^*y_iK(x_i,x)+b^*)

非线性支持向量机学习算法:


step1:选取适当的核函数K(x,z)和适当的参数C,构造并求解最优化问题

                                                                      min_\alpha \frac{1}{2}\sum_{i=1}^N\sum_{j=1}^N\alpha _i\alpha _jy_iy_jK(x_i,x_j)-\sum_{i=1}^N\alpha _i

                                                                     s.t.   \sum_{i=1}^N\alpha _iy_i=0

                                                                           0\leqslant \alpha _i\leqslant C,i=1,2,...,N

求得最优解\alpha ^*=(\alpha _1^*,...,\alpha _N^*)^T.

step2:选择\alpha^*的一个正分量0< \alpha _j^*< C,计算

                                                                   b^*=y_i-\sum_{i=1}^N\alpha _j^*y_iK(x_i,x_j)

step3:构造决策函数:

                                                             f(x)=sign(\sum_{i=1}^N\alpha _i^*y_iK(x_i,x_j)+b^*)                   


代码实现:

import numpy as np
import pandas as pd
from sklearn.datasets import load_iris
from sklearn.model_selection import  train_test_split
import matplotlib.pyplot as plt
%matplotlib inline
# data
def create_data():
    iris = load_iris()
    df = pd.DataFrame(iris.data, columns=iris.feature_names)
    df['label'] = iris.target
    df.columns = [
        'sepal length', 'sepal width', 'petal length', 'petal width', 'label'
    ]
    data = np.array(df.iloc[:100, [0, 1, -1]])
    for i in range(len(data)):
        if data[i, -1] == 0:
            data[i, -1] = -1
    # print(data)
    return data[:, :2], data[:, -1]

X, y = create_data()
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25)


plt.scatter(X[:50,0],X[:50,1], label='0')
plt.scatter(X[50:,0],X[50:,1], label='1')
plt.legend()



class SVM:
    def __init__(self, max_iter=100, kernel='linear'):
        self.max_iter = max_iter
        self._kernel = kernel

    def init_args(self, features, labels):
        self.m, self.n = features.shape
        self.X = features
        self.Y = labels
        self.b = 0.0

        # 将Ei保存在一个列表里
        self.alpha = np.ones(self.m)
        self.E = [self._E(i) for i in range(self.m)]
        # 松弛变量
        self.C = 1.0

    def _KKT(self, i):
        y_g = self._g(i) * self.Y[i]
        if self.alpha[i] == 0:
            return y_g >= 1
        elif 0 < self.alpha[i] < self.C:
            return y_g == 1
        else:
            return y_g <= 1

    # g(x)预测值,输入xi(X[i])
    def _g(self, i):
        r = self.b
        for j in range(self.m):
            r += self.alpha[j] * self.Y[j] * self.kernel(self.X[i], self.X[j])
        return r

    # 核函数
    def kernel(self, x1, x2):
        if self._kernel == 'linear':
            return sum([x1[k] * x2[k] for k in range(self.n)])
        elif self._kernel == 'poly':
            return (sum([x1[k] * x2[k] for k in range(self.n)]) + 1)**2

        return 0

    # E(x)为g(x)对输入x的预测值和y的差
    def _E(self, i):
        return self._g(i) - self.Y[i]

    def _init_alpha(self):
        # 外层循环首先遍历所有满足0= 0:
                j = min(range(self.m), key=lambda x: self.E[x])
            else:
                j = max(range(self.m), key=lambda x: self.E[x])
            return i, j

    def _compare(self, _alpha, L, H):
        if _alpha > H:
            return H
        elif _alpha < L:
            return L
        else:
            return _alpha

    def fit(self, features, labels):
        self.init_args(features, labels)

        for t in range(self.max_iter):
            # train
            i1, i2 = self._init_alpha()

            # 边界
            if self.Y[i1] == self.Y[i2]:
                L = max(0, self.alpha[i1] + self.alpha[i2] - self.C)
                H = min(self.C, self.alpha[i1] + self.alpha[i2])
            else:
                L = max(0, self.alpha[i2] - self.alpha[i1])
                H = min(self.C, self.C + self.alpha[i2] - self.alpha[i1])

            E1 = self.E[i1]
            E2 = self.E[i2]
            # eta=K11+K22-2K12
            eta = self.kernel(self.X[i1], self.X[i1]) + self.kernel(
                self.X[i2],
                self.X[i2]) - 2 * self.kernel(self.X[i1], self.X[i2])
            if eta <= 0:
                # print('eta <= 0')
                continue

            alpha2_new_unc = self.alpha[i2] + self.Y[i2] * (
                E1 - E2) / eta  #此处有修改,根据书上应该是E1 - E2,书上130-131页
            alpha2_new = self._compare(alpha2_new_unc, L, H)

            alpha1_new = self.alpha[i1] + self.Y[i1] * self.Y[i2] * (
                self.alpha[i2] - alpha2_new)

            b1_new = -E1 - self.Y[i1] * self.kernel(self.X[i1], self.X[i1]) * (
                alpha1_new - self.alpha[i1]) - self.Y[i2] * self.kernel(
                    self.X[i2],
                    self.X[i1]) * (alpha2_new - self.alpha[i2]) + self.b
            b2_new = -E2 - self.Y[i1] * self.kernel(self.X[i1], self.X[i2]) * (
                alpha1_new - self.alpha[i1]) - self.Y[i2] * self.kernel(
                    self.X[i2],
                    self.X[i2]) * (alpha2_new - self.alpha[i2]) + self.b

            if 0 < alpha1_new < self.C:
                b_new = b1_new
            elif 0 < alpha2_new < self.C:
                b_new = b2_new
            else:
                # 选择中点
                b_new = (b1_new + b2_new) / 2

            # 更新参数
            self.alpha[i1] = alpha1_new
            self.alpha[i2] = alpha2_new
            self.b = b_new

            self.E[i1] = self._E(i1)
            self.E[i2] = self._E(i2)
        return 'train done!'

    def predict(self, data):
        r = self.b
        for i in range(self.m):
            r += self.alpha[i] * self.Y[i] * self.kernel(data, self.X[i])

        return 1 if r > 0 else -1

    def score(self, X_test, y_test):
        right_count = 0
        for i in range(len(X_test)):
            result = self.predict(X_test[i])
            if result == y_test[i]:
                right_count += 1
        return right_count / len(X_test)

    def _weight(self):
        # linear model
        yx = self.Y.reshape(-1, 1) * self.X
        self.w = np.dot(yx.T, self.alpha)
        return self.w

svm = SVM(max_iter=200)
svm.fit(X_train, y_train)
svm.score(X_test, y_test)


#sklearn实例
from sklearn.svm import SVC
clf = SVC()
clf.fit(X_train, y_train)
clf.score(X_test, y_test)

 

连载GitHub同步更新:https://github.com/wenhan123/ML-Python-

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(Python与AI,学习笔记,机器学习)