前言

支持向量机在应用到二分类和多分类问题上, 很强大,
需要了解支持向量机的基础知识,请先自行学习

数据集介绍

线性数据集

:::success sklearn.datasets.make_blobs(n_samples=100, n_features=2, centers=3, cluster_std=1.0, center_box=(-10.0, 10.0), shuffle=True, random_state=None)
n_samples: 待生成的样本的总数
centers: 要生成的样本中心(类别)数,或者是确定的中心点
cluster_std: 每个类别的方差,例如我们希望生成2类数据,其中一类比另一类具有更大的方差,可以 cluster_std设置为[1.0,3.0] :::

圆形数据集

:::success sklearn.datasets.samples_generator.make_circles()
略 :::

使用支持向量机完成线性分类

导包

  1. import matplotlib.pyplot as plt
  2. from sklearn.datasets._samples_generator import make_blobs
  3. import numpy as np
  4. # "Support Vector Classifier" 支持向量机分类器
  5. from sklearn.svm import SVC

使用sklearn自带的数据集生成数据

  1. # n_samples=100 是取100个点
  2. # centers=2 是将数据分为2类
  3. data, target = make_blobs(n_samples=100, centers=2, random_state=3, cluster_std=0.7)
  4. # 将图片展示出来
  5. plt.scatter(data[:,0],data[:,1], c=target, s=10, cmap='autumn')
  6. plt.show()

:::tips X[:, 0]就是所有的x轴数据
X[:, 1]就是所有的y轴数据 :::

image.png

粗略的画出3条决策边界

  1. xfit = np.linspace(-5, 3, 5)
  2. # 先画出散点图
  3. plt.scatter(data[:,0], data[:,1], c=target, s=10, cmap='autumn')
  4. # 画三条线
  5. plt.plot(xfit, -xfit, '-k')
  6. plt.plot(xfit, -0.5*xfit+0.7, '-k')
  7. plt.plot(xfit, 1*xfit+2.9, '-k')
  8. plt.show()

image.png

训练一个SVC模型

  1. model = SVC(kernel='linear', C=1E10)
  2. model.fit(data, target)

SVC(C=10000000000.0, kernel=’linear’)

画出该SVC模型的图形

  1. def plot_svc_decision_function(model, data, target, ax=None, plot_support=True):
  2. # """Plot the decision function for a 2D SVC"""
  3. if ax is None:
  4. ax = plt.gca()
  5. xlim = (data[:,0].min(), data[:,0].max())
  6. ylim = (data[:,1].min(), data[:,1].max())
  7. x = np.linspace(xlim[0], xlim[1], 100)
  8. y = np.linspace(ylim[0], ylim[1], 100)
  9. Y1, X1 = np.meshgrid(y, x)
  10. xy = np.vstack([X1.ravel(), Y1.ravel()]).T
  11. P = model.decision_function(xy).reshape(X1.shape)
  12. ax.contour(X1, Y1, P, colors='k',levels=[-1, 0, 1], alpha=0.5,linestyles=['--', '-', '--'])
  13. if plot_support:
  14. ax.scatter(model.support_vectors_[:, 0],model.support_vectors_[:, 1],s=10, linewidth=1, facecolors='none');
  15. ax.set_xlim(xlim)
  16. ax.set_ylim(ylim)
  17. plt.scatter(data[:, 0], data[:, 1], c=target, s=10, cmap='autumn')
  1. plot_svc_decision_function(model, data, target);

image.png

:::tips 这个画图的代码比复杂, 我稍微解释一下

  1. 第5行是获取整个X数据的x轴最小值和最大值
  2. 第6行是获取整个X数据的y轴最小值和最大值
  3. 第9行是根据xlim和ylim获取坐标轴
  4. 第11~14行, 我也看不懂, 也懒得看, 反正能跑出来
  5. 第17行是画散点图的 ::: :::info 其实在边界上的两个红点和一个黄点在决策边界上,是支持向量,其支持向量机实战教程1 - 图4值不为0。这三个点的坐标可以由model.support_vectors_得出。
    这个分类器的成功的关键在于:为了拟合,只有支持向量的位置是重要的;远离任何边距的点,都不会影响拟合。边界之外的点无论有多少都不会对其造成影响,下面来对比一下数据为60和120时的区别。 :::

    对比60和120个样本点的差距

    1. def plot_svm(N=10, ax=None):
    2. data, target = make_blobs(n_samples=200, centers=2, random_state=0, cluster_std=0.60)
    3. data = data[:N]
    4. target = target[:N]
    5. model = SVC(kernel='linear', C=1E10)
    6. model.fit(data, target)
    7. if ax is None:
    8. ax = plt.gca()
    9. ax.scatter(data[:, 0], data[:, 1], c=target, s=50, cmap='autumn')
    10. plot_svc_decision_function(model, data, target, ax)
    1. fig, ax = plt.subplots(2, 3, figsize=(16, 6))
    2. fig.subplots_adjust(left=0.0625, right=0.95, wspace=0.1)
    3. for axi, N in zip(ax.flatten(), [30, 60, 90, 120, 150, 180]):#左侧的数据60个样本点,右侧的数据为120样本点。
    4. plot_svm(N, axi)
    5. axi.set_title('N = {0}'.format(N))
    6. plt.show()

    image.png :::tips plt.subplots(2, 3, figsize=(16, 6))这行代码返回的是fig和一个numpy.ndarray类型的变量,如果是建立的2行3列的大图,最后在使用zip变量进行遍历的时候,要记得将shape=(2, 3)的axes拉伸成shape=(1, 6)的数组才可以 ::: :::info 由上图可以看到,相邻密度构成的决策边界是大致一样的。这就意味着样本的多少对决策边界是影响不大的(是有影响的),这主要是因为没有引入足够的新的支持向量,意思就是说只要边界上的点不变就不会对决策边界造成影响。 :::

    线性分类的总代码

    1. import matplotlib.pyplot as plt
    2. from sklearn.datasets._samples_generator import make_blobs
    3. import numpy as np
    4. # "Support Vector Classifier" 支持向量机分类器
    5. from sklearn.svm import SVC
    6. # n_samples=100 是取100个点
    7. # centers=2 是将数据分为2类
    8. X ,y = make_blobs(n_samples=100, centers=2, random_state=3, cluster_std=0.7)
    9. # 将图片展示出来
    10. plt.scatter(X[:,0],X[:,1], c=y, s=10, cmap='autumn')
    11. plt.show()
    12. xfit = np.linspace(-5, 3, 5)
    13. # 先画出散点图
    14. plt.scatter(X[:,0], X[:,1], c=y, s=10, cmap='autumn')
    15. # 画三条线
    16. plt.plot(xfit, -xfit, '-k')
    17. plt.plot(xfit, -0.5*xfit+0.7, '-k')
    18. plt.plot(xfit, 1*xfit+2.9, '-k')
    19. plt.show()
    20. model = SVC(kernel='linear', C=1E10)
    21. model.fit(X, y)
    22. def plot_svc_decision_function(model, data, target, ax=None, plot_support=True):
    23. # """Plot the decision function for a 2D SVC"""
    24. if ax is None:
    25. ax = plt.gca()
    26. xlim = (data[:,0].min(), data[:,0].max())
    27. ylim = (data[:,1].min(), data[:,1].max())
    28. x = np.linspace(xlim[0], xlim[1], 100)
    29. y = np.linspace(ylim[0], ylim[1], 100)
    30. Y1, X1 = np.meshgrid(y, x)
    31. xy = np.vstack([X1.ravel(), Y1.ravel()]).T
    32. P = model.decision_function(xy).reshape(X1.shape)
    33. ax.contour(X1, Y1, P, colors='k',levels=[-1, 0, 1], alpha=0.5,linestyles=['--', '-', '--'])
    34. if plot_support:
    35. ax.scatter(model.support_vectors_[:, 0],model.support_vectors_[:, 1],s=10, linewidth=1, facecolors='none');
    36. ax.set_xlim(xlim)
    37. ax.set_ylim(ylim)
    38. plt.scatter(data[:, 0], data[:, 1], c=target, s=10, cmap='autumn')
    39. plot_svc_decision_function(model, data, target);
    40. def plot_svm(N=10, ax=None):
    41. data, target = make_blobs(n_samples=200, centers=2, random_state=0, cluster_std=0.60)
    42. data = data[:N]
    43. target = target[:N]
    44. model = SVC(kernel='linear', C=1E10)
    45. model.fit(data, target)
    46. if ax is None:
    47. ax = plt.gca()
    48. ax.scatter(data[:, 0], data[:, 1], c=target, s=50, cmap='autumn')
    49. plot_svc_decision_function(model, data, target, ax)
    50. fig, ax = plt.subplots(2, 3, figsize=(16, 6))
    51. fig.subplots_adjust(left=0.0625, right=0.95, wspace=0.1)
    52. for axi, N in zip(ax.flatten(), [30, 60, 90, 120, 150, 180]):#左侧的数据60个样本点,右侧的数据为120样本点。
    53. plot_svm(N, axi)
    54. axi.set_title('N = {0}'.format(N))
    55. plt.show()

    使用支持向量机完成核函数分类

    接下来引入核函数, 来看看核函数的威力, 真的感觉好厉害~!
    下面是具体的讲解流程

    导包

    1. import matplotlib.pyplot as plt
    2. from sklearn.datasets._samples_generator import make_circles
    3. import numpy as np
    4. # "Support Vector Classifier" 支持向量机分类器
    5. from sklearn.svm import SVC

    引入一个线性不可分的数据集

    :::success sklearn.datasets.make_circles(n_samples=100, *, shuffle=True, noise=None, random_state=None, factor=0.8)
    n_samples:生成样本的数量,默认为10
    shuffle: 是否乱序生成, 默认为True
    noise: 添加到数据中的高斯噪声的标准偏差
    random_state: int, RandomState instance or None, default=None
    fctor: Scale factor between inner and outer circle in the range (0, 1) ::: :::success matplotlib.pyplot.subplots_adjust(left=None, bottom=None, right=None, top=None, wspace=None, hspace=None)
    left = 0.125 # 子图(subplot)距画板(figure)左边的距离
    right = 0.9 # 右边
    bottom = 0.1 # 底部
    top = 0.9 # 顶部
    wspace = 0.2 # 子图水平间距
    hspace = 0.2 # 子图垂直间距 :::

    1. fig, axs = plt.subplots(2, 3, figsize=(10, 5))
    2. fig.subplots_adjust(left=0.0625, right=0.95, wspace=0.3, hspace=0.3)
    3. for axi, n_samples in zip(axs.flatten(), [30, 50, 70, 90, 110, 130]):
    4. data, target = make_circles(n_samples, factor=.1, noise=.1)
    5. axi.scatter(data[:,0], data[:,1], c=target, s=3)
    6. axi.set_title('generate {} sample points'.format(n_samples))
    7. plt.show()

    image.png

    使用线性分类对圆形数据集进行分类

    1. def plot_svc_decision_function(model, data, target, ax=None, plot_support=True):
    2. # """Plot the decision function for a 2D SVC"""
    3. if ax is None:
    4. ax = plt.gca()
    5. xlim = (data[:,0].min(), data[:,0].max())
    6. ylim = (data[:,1].min(), data[:,1].max())
    7. x = np.linspace(xlim[0], xlim[1], 100)
    8. y = np.linspace(ylim[0], ylim[1], 100)
    9. Y1, X1 = np.meshgrid(y, x)
    10. xy = np.vstack([X1.ravel(), Y1.ravel()]).T
    11. P = model.decision_function(xy).reshape(X1.shape)
    12. ax.contour(X1, Y1, P, colors='k',levels=[-1, 0, 1], alpha=0.5,linestyles=['--', '-', '--'])
    13. if plot_support:
    14. ax.scatter(model.support_vectors_[:, 0],model.support_vectors_[:, 1],s=10, linewidth=1, facecolors='none');
    15. ax.set_xlim(xlim)
    16. ax.set_ylim(ylim)
    17. plt.scatter(data[:, 0], data[:, 1], c=target, s=10, cmap='autumn')

    :::tips 注意:上面的plot_svc_decision_function()函数是上节已经定义过的函数 :::

    1. data, target = make_circles(n_samples, factor=.1, noise=.1)
    2. model = SVC(kernel='linear').fit(data, target)
    3. plot_svc_decision_function(model, data, target)

    image.png :::info 我们生成了一个圆形数据集,并且使用了线性分类进行分类, 可以看到线性分类并不能将圆形数据集进行正确的分类, 因此我们考虑使用核函数对圆形数据集进行分类 :::

    先看看数据在高纬空间下的映射

    1. from mpl_toolkits import mplot3d
    2. r=np.exp(-(data**2).sum(1))
    3. def plot_3D(elev=30, azim=30, X=data, y=target):
    4. ax = plt.subplot(projection='3d')
    5. ax.scatter3D(X[:, 0], X[:, 1], r, c=y, s=50, cmap='autumn')
    6. ax.view_init(elev=elev, azim=azim)
    7. ax.set_xlabel('x')
    8. ax.set_ylabel('y')
    9. ax.set_zlabel('z')
    10. plot_3D(elev=45,azim=45,X=data,y=target)
    11. plt.show()

    image.png

    引入径向基函数,进行核变换

    见证核函数变换的威力的时刻到了~!

    1. clf = SVC(kernel='rbf', C=1E6) #引入径向基 函数
    2. clf.fit(data, target)
    3. plt.scatter(data[:, 0], data[:, 1], c=target, s=50, cmap='autumn')
    4. plot_svc_decision_function(clf, data, target)
    5. plt.scatter(clf.support_vectors_[:, 0], clf.support_vectors_[:, 1], s=300, lw=1, facecolors='none');
    6. plt.show()

    image.png

    核函数分类的总代码

    1. import matplotlib.pyplot as plt
    2. from sklearn.datasets._samples_generator import make_circles
    3. import numpy as np
    4. # "Support Vector Classifier" 支持向量机分类器
    5. from sklearn.svm import SVC
    6. fig, axs = plt.subplots(2, 3, figsize=(10, 5))
    7. fig.subplots_adjust(left=0.0625, right=0.95, wspace=0.3, hspace=0.3)
    8. for axi, n_samples in zip(axs.flatten(), [30, 50, 70, 90, 110, 130]):
    9. data, target = make_circles(n_samples, factor=.1, noise=.1)
    10. axi.scatter(data[:,0], data[:,1], c=target, s=3)
    11. axi.set_title('generate {} sample points'.format(n_samples))
    12. plt.show()
    13. def plot_svc_decision_function(model, data, target, ax=None, plot_support=True):
    14. # """Plot the decision function for a 2D SVC"""
    15. if ax is None:
    16. ax = plt.gca()
    17. xlim = (data[:,0].min(), data[:,0].max())
    18. ylim = (data[:,1].min(), data[:,1].max())
    19. x = np.linspace(xlim[0], xlim[1], 100)
    20. y = np.linspace(ylim[0], ylim[1], 100)
    21. Y1, X1 = np.meshgrid(y, x)
    22. xy = np.vstack([X1.ravel(), Y1.ravel()]).T
    23. P = model.decision_function(xy).reshape(X1.shape)
    24. ax.contour(X1, Y1, P, colors='k',levels=[-1, 0, 1], alpha=0.5,linestyles=['--', '-', '--'])
    25. if plot_support:
    26. ax.scatter(model.support_vectors_[:, 0],model.support_vectors_[:, 1],s=10, linewidth=1, facecolors='none');
    27. ax.set_xlim(xlim)
    28. ax.set_ylim(ylim)
    29. plt.scatter(data[:, 0], data[:, 1], c=target, s=10, cmap='autumn')
    30. data, target = make_circles(n_samples, factor=.1, noise=.1)
    31. model = SVC(kernel='linear').fit(data, target)
    32. plot_svc_decision_function(model, data, target)
    33. from mpl_toolkits import mplot3d
    34. r=np.exp(-(data**2).sum(1))
    35. def plot_3D(elev=30, azim=30, X=data, y=target):
    36. ax = plt.subplot(projection='3d')
    37. ax.scatter3D(X[:, 0], X[:, 1], r, c=y, s=50, cmap='autumn')
    38. ax.view_init(elev=elev, azim=azim)
    39. ax.set_xlabel('x')
    40. ax.set_ylabel('y')
    41. ax.set_zlabel('z')
    42. plot_3D(elev=45,azim=45,X=data,y=target)
    43. plt.show()
    44. clf = SVC(kernel='rbf', C=1E6) #引入径向基 函数
    45. clf.fit(data, target)
    46. plt.scatter(data[:, 0], data[:, 1], c=target, s=50, cmap='autumn')
    47. plot_svc_decision_function(clf, data, target)
    48. plt.scatter(clf.support_vectors_[:, 0], clf.support_vectors_[:, 1], s=300, lw=1, facecolors='none');
    49. plt.show()

    调节参数-软间隔问题

    :::info SVM模型有两个非常重要的参数C与gamma。
    其中 C是惩罚系数,即对误差的宽容度。
    c越高,说明越不能容忍出现误差,容易过拟合。
    C越小,容易欠拟合。C过大或过小,泛化能力变差
    gamma是选择RBF函数作为kernel后,该函数自带的一个参数。
    隐含地决定了数据映射到新的特征空间后的分布,
    gamma越大,支持向量越少,
    gamma值越小,支持向量越多。 ::: 分别调节C和gamma来看一下对结果的影响:

    引入一个离散度很大的数据集

    1. # 将离散度cluster_std改为0.8
    2. data, target = make_blobs(n_samples=100, centers=2, random_state=0, cluster_std=0.8)
    3. plt.scatter(data[:,0], data[:,1], c=target)
    4. plt.show()

    image.png

    调节参数C

    :::tips C趋近于无穷大时,意味着分类严格不能有错误。
    C趋于很小时,意味着可以有更大的容忍。 :::

    1. data, target = make_blobs(n_samples=100, centers=2,random_state=0, cluster_std=0.8)
    2. fig, ax = plt.subplots(1, 2, figsize=(16, 6))
    3. fig.subplots_adjust(left=0.0625, right=0.95, wspace=0.1)
    4. for axi, C in zip(ax, [20, 0.2]): #将C分别设定为20和0.2,看其对结果的影响。
    5. model = SVC(kernel='linear', C=C).fit(X, y)
    6. axi.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
    7. plot_svc_decision_function(model, data, target, axi)
    8. axi.scatter(model.support_vectors_[:, 0],model.support_vectors_[:, 1],s=300, lw=1, facecolors='none');
    9. axi.set_title('C = {0:.1f}'.format(C), size=14)
    10. plt.show()

    image.png :::info 可以看出:
    当C=20较大时,对分类错误的容忍是很低的
    当C=0.2较小时,对分类错误的容忍是较高的
    ——————————————————————————————————————————————————————————-
    左边这幅图C值比较大,要求比较严格,不能分错东西,隔离带中没有进入任何一个点,但是隔离带的距离比较小,泛化能力比较差。右边这幅图C值比较小,要求相对来说比较松一些,隔离带较大,但是隔离带中进入了很多的黄点和红点。那么C大一点好还是小一点好呢?这需要考虑实际问题,可以进行K折交叉验证来得出最合适的C值。 :::

    调节参数gamma

    1. data, target = make_blobs(n_samples=100, centers=2,random_state=0, cluster_std=1.1)
    2. fig, ax = plt.subplots(1, 2, figsize=(16, 6))
    3. fig.subplots_adjust(left=0.0625, right=0.95, wspace=0.1)
    4. for axi, gamma in zip(ax, [20, 0.1]): #比较了一下gamma为20和0.1对结果的影响
    5. model = SVC(kernel='rbf', gamma=gamma).fit(X, y)
    6. axi.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
    7. plot_svc_decision_function(model, data, target, axi)
    8. axi.scatter(model.support_vectors_[:, 0],model.support_vectors_[:, 1],s=300, lw=1, facecolors='none');
    9. axi.set_title('gamma = {0:.1f}'.format(gamma), size=14)
    10. plt.show()

    image.png :::info 左边的图边界比较复杂,这也意味着泛化能力更弱,右边的图比较精简,泛化能力较强。一般会选择泛化能力较强的。 :::

    参考链接

  6. https://blog.csdn.net/weixin_44010678/article/details/87016753

  7. https://blog.csdn.net/v_july_v/article/details/7624837