人工智能与机器学习人工智能/模式识别/机器学习精华专题支持向量机

Python机器学习(四):SVM 支撑向量机

2018-12-26  本文已影响2人  饮茶先啦靓仔

这个系列拖了好久,当然这段时间也不算荒废吧,主要是考试和各种课程设计的缘故,也接了一些小项目,所以机器学习这里就落下来了。现在基本所有事情都搞定了,所以一方面要赶紧把这个系列完结了,另一方面这个漫长假期(学校给我们放了两个月的假。。。)也要给自己立一个大flag,赶紧把自己的能力提上去,毕竟下学期就要找实习了。OK,日常唠叨完毕。

支撑向量机(Support Vector Machin,SVM)可以解决分类问题,也可以用于解决回归问题。这里仅对分类问题进行一些粗浅的讨论。

感性理解

在分类问题中,分类算法会将数据空间划分一个或多个决策边界(高维时称为超平面,为了简化问题,这里仅对两个特征的二分类问题进行讨论),决策边界的一边是一类,另一边是另一类。了解逻辑回归的应该都能理解。但是,不同的分类算法会对相同的数据生成不同的决策边界。那么,哪个决策边界才是最好的呢?这个问题称为不适定问题对于SVM来说,它的目的就是尽可能地找到一个合适的边界,来提高算法的泛化能力,即鲁棒性。


不同的决策边界

那么,SVM的具体操作是:寻找一个最优的决策边界,距离两个类别的最近的样本最远。最近的样本点称为支撑向量。当然,这里讨论的是线性可分的问题。当线性不可分的时候,有改进的方法,下文中会提到。


中间的直线就是SVM算法得到的决策边界

Hard Margin SVM

上面的图中,样本点是线性可分的,即可以找到一条决策边界使得分类不会出错,这时候使用的SVM算法称为Hard Magin SVM。此时的算法至少在训练集上是不会发生错误的。

感性理解之后就需要用数学语言来表达了。回忆一下高中学的解析几何,直线的一般式方程为

点到直线的距离为

那么拓展到n维空间中的话,可以写成这种形式


同样,距离公式为

我们定义类A的值为1,类B的值为-1。在图中可以看到,类A的支撑向量距离决策边界的距离为d,类B上的支撑向量距离决策边界的距离也为d。那么可以列写这样的方程


因为向量 w 的模,即方程左边的分母为常数,距离 d 也为常数,可以进行这样的简化,即 d 除过去并且换个符号。下标d说明截距和向量 w 已经被 dw 的模相除。

因为定义了类A的值为1,类B的值为-1,那么可以写成一个式子

对于所有支撑向量,满足这样的方程

即求模的最小值。实际工程中为了求导得到最小值,使用的是第二个式子

所以总结下就是这个目标。下方的式子是求解条件


Soft Margin SVM

如果数据线性不可分,那么如果要使用SVM算法,就需要允许分类算法犯一定的错误,具体方法是让限定条件变得宽松一点



那么随之,求解目标也会有所改变。C是一个超参数。C趋向于0的时候,允许无限大的误差,趋向于无穷大的时候,算法本质就是Hard Margin SVM。


L1正则化
L2正则化

编程实现

要自己实现SVM比较麻烦,这里只用sciki-learn中提供的函数去实现。

"""
Created by 杨帮杰 on 12/24/2018
Right to use this code in any way you want without
warranty, support or any guarantee of it working
E-mail: yangbangjie1998@qq.com
Association: SCAU 华南农业大学
"""
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.preprocessing import StandardScaler
from sklearn.svm import LinearSVC
from matplotlib.colors import ListedColormap

# 使用鸢尾花数据集
iris = datasets.load_iris()

X = iris.data
y = iris.target

# 只使用两个特征和两个种类
X = X[y < 2, :2]
y = y[y < 2]

# 归一化尺度
standardScaler = StandardScaler()
standardScaler.fit(X)
X_standard = standardScaler.transform(X)

# hard margin SVM
svc = LinearSVC(C=1e9)
svc.fit(X_standard, y)


# 画出决策边界
def plot_decision_boundary(model, axis):

    x0, x1 = np.meshgrid(
        np.linspace(axis[0], axis[1], int((axis[1] - axis[0])*100)).reshape(1, -1),
        np.linspace(axis[2], axis[3], int((axis[3] - axis[2])*100)).reshape(1, -1)
    )
    X_new = np.c_[x0.ravel(), x1.ravel()]

    y_predict = model.predict(X_new)
    zz = y_predict.reshape(x0.shape)

    custom_cmap = ListedColormap(['#EF9A9A', '#FFF59D', '#90CAF9'])
    plt.contourf(x0, x1, zz, linewidth=5, cmap=custom_cmap)


# 画出svm决策边界
def plot_svc_decision_boundary(model, axis):
    x0, x1 = np.meshgrid(
        np.linspace(axis[0], axis[1], int((axis[1] - axis[0]) * 100)).reshape(1, -1),
        np.linspace(axis[2], axis[3], int((axis[3] - axis[2]) * 100)).reshape(1, -1)
    )
    X_new = np.c_[x0.ravel(), x1.ravel()]

    y_predict = model.predict(X_new)
    zz = y_predict.reshape(x0.shape)

    custom_cmap = ListedColormap(['#EF9A9A', '#FFF59D', '#90CAF9'])
    plt.contourf(x0, x1, zz, linewidth=5, cmap=custom_cmap)

    w = model.coef_[0]
    b = model.intercept_[0]

    # w0 * x0 + w1 * x1 + b = 0
    # => x1 = -w0/w1 * x0 - b/w1
    plot_x = np.linspace(axis[0], axis[1], 200)
    up_y = -w[0]/w[1] * plot_x - b/w[1] + 1/w[1]
    down_y = -w[0]/w[1] * plot_x - b/w[1] - 1/w[1]

    up_index = (up_y >= axis[2]) & (up_y <= axis[3])
    down_index = (down_y >= axis[2]) & (down_y <= axis[3])
    plt.plot(plot_x[up_index], up_y[up_index], color="black")
    plt.plot(plot_x[down_index], down_y[down_index], color="black")


# 显示数据和分类情况
plot_decision_boundary(svc, axis=[-3, 3, -3, 3])
plt.scatter(X_standard[y == 0, 0], X_standard[y == 0, 1])
plt.scatter(X_standard[y == 1, 0], X_standard[y == 1, 1])
plt.show()

plot_svc_decision_boundary(svc, axis=[-3, 3, -3, 3])
plt.scatter(X_standard[y == 0, 0], X_standard[y == 0, 1])
plt.scatter(X_standard[y == 1, 0], X_standard[y == 1, 1])
plt.show()

# soft margin SVM
svc2 = LinearSVC(C=0.01)
svc2.fit(X_standard, y)

plot_svc_decision_boundary(svc2, axis=[-3, 3, -3, 3])
plt.scatter(X_standard[y == 0, 0], X_standard[y == 0, 1])
plt.scatter(X_standard[y == 1, 0], X_standard[y == 1, 1])
plt.show()

Hard Margin SVM的分类结果 两条黑边是经过支撑向量上的直线
Soft Margin SVM

如果想使用多项式特征的SVM,可以给数据添加多项式特征后使用LinearSVC

"""
Created by 杨帮杰 on 12/24/2018
Right to use this code in any way you want without
warranty, support or any guarantee of it working
E-mail: yangbangjie1998@qq.com
Association: SCAU 华南农业大学
"""
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.svm import LinearSVC
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import PolynomialFeatures, StandardScaler
from matplotlib.colors import ListedColormap

X, y = datasets.make_moons(noise=0.15, random_state=666)


def PolynomialSVC(degree, C=1.0):
    return Pipeline([
        ("poly", PolynomialFeatures(degree=degree)),
        ("std_scaler", StandardScaler()),
        ("linearSVC", LinearSVC(C=C))
    ])


poly_svc = PolynomialSVC(degree=3)
poly_svc.fit(X, y)


def plot_decision_boundary(model, axis):

    x0, x1 = np.meshgrid(
        np.linspace(axis[0], axis[1], int((axis[1] - axis[0])*100)).reshape(1, -1),
        np.linspace(axis[2], axis[3], int((axis[3] - axis[2])*100)).reshape(1, -1)
    )
    X_new = np.c_[x0.ravel(), x1.ravel()]

    y_predict = model.predict(X_new)
    zz = y_predict.reshape(x0.shape)

    custom_cmap = ListedColormap(['#EF9A9A', '#FFF59D', '#90CAF9'])
    plt.contourf(x0, x1, zz, linewidth=5, cmap=custom_cmap)


plot_decision_boundary(poly_svc, axis=[-1.5, 2.5, -1.0, 1.5])
plt.scatter(X[y == 0, 0], X[y == 0, 1])
plt.scatter(X[y == 1, 0], X[y == 1, 1])
plt.show()
带多项式特征

在SVM实现中会使用核函数,它的目的是直接计算出两者添加多项式特征之后的点乘,加快计算速度。并且,不同的核函数,会对样本点进行不同的转换,得到不同的效果,比如高斯核函数(径向基函数)。这里不进行讨论,读者可以自行改变SVC函数中的kernel参数来进行实验。

"""
Created by 杨帮杰 on 12/24/2018
Right to use this code in any way you want without
warranty, support or any guarantee of it working
E-mail: yangbangjie1998@qq.com
Association: SCAU 华南农业大学
"""
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from matplotlib.colors import ListedColormap
from sklearn.svm import SVC

X, y = datasets.make_moons(noise=0.15, random_state=666)

def plot_decision_boundary(model, axis):

    x0, x1 = np.meshgrid(
        np.linspace(axis[0], axis[1], int((axis[1] - axis[0])*100)).reshape(1, -1),
        np.linspace(axis[2], axis[3], int((axis[3] - axis[2])*100)).reshape(1, -1)
    )
    X_new = np.c_[x0.ravel(), x1.ravel()]

    y_predict = model.predict(X_new)
    zz = y_predict.reshape(x0.shape)

    custom_cmap = ListedColormap(['#EF9A9A', '#FFF59D', '#90CAF9'])
    plt.contourf(x0, x1, zz, linewidth=5, cmap=custom_cmap)


def PolynomialKernalSVC(degree, C=1.0):
    return Pipeline([
        ("std_scaler", StandardScaler()),
        ("kernelSVC", SVC(kernel="poly", degree=degree, C=C))
    ])


poly_kernel_svc = PolynomialKernalSVC(degree=3)
poly_kernel_svc.fit(X, y)

plot_decision_boundary(poly_kernel_svc, axis=[-1.5, 2.5, -1.0, 1.5])
plt.scatter(X[y == 0, 0], X[y == 0, 1])
plt.scatter(X[y == 1, 0], X[y == 1, 1])
plt.show()

多项式核函数

References:
Python3 入门机器学习 经典算法与应用 —— liuyubobobo
机器学习实战 —— Peter Harrington

上一篇下一篇

猜你喜欢

热点阅读