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

Jacob的 Python机器学习系列:
Python机器学习(一):kNN算法
Python机器学习(二):线性回归算法
Python机器学习(三):梯度下降法
Python机器学习(四):PCA 主成分分析
Python机器学习(五):SVM 支撑向量机

这个系列拖了好久,当然这段时间也不算荒废吧,主要是考试和各种课程设计的缘故,也接了一些小项目,所以机器学习这里就落下来了。现在基本所有事情都搞定了,所以一方面要赶紧把这个系列完结了,另一方面这个漫长假期(学校给我们放了两个月的假。。。)也要给自己立一个大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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270