当前位置:   article > 正文

支持向量机简介

支持向量机

支持向量机(SVM)是一类非常经典的算法,在深度学习成为主流之前,曾一度统治着机器学习界。支持向量机就是要在两个类别中找到一条分隔边界,这条分隔边界可以很好的分开两个类别,并且这个边界到两个类别的距离是最大的。其中两个类别中到这条边界最近的向量叫做支持向量。如下图所示:

支持向量机一般分为三种:线性可分支持向量机,线性支持向量机(也叫软间隔支持向量机,近似线性可分支持向量机),非线性支持向量机。

一般我们所说的就是线性可分支持向量机,事实上,现实生活中更多的是近似线性可分的情况。所谓的“软间隔”就是允许某些样本不满足约束条件,但是在最大化间隔的同时,不满足约束的样本应该尽可能少。

而非线性支持向量机,就是指两个类别的间隔不再是一条直线,而是非线性的,如下图所示:

由于支持向量机算法比较复杂,涉及数学知识较多,我这次就直接用sklearn实现了,从应用角度出发,数学原理方面的内容可以参考周志华的《机器学习》和李航的《统计学习方法》,里面讲的很清楚。

下面我们通过具体的程序来熟悉一下支持向量机的使用方法。首先,从sklearn自带的鸢尾花数据集开始:

  1. import numpy as np
  2. from sklearn import datasets
  3. from sklearn.pipeline import Pipeline
  4. from sklearn.preprocessing import StandardScaler
  5. from sklearn.svm import LinearSVC
  6. iris = datasets.load_iris()
  7. X = iris["data"][:,(2,3)] # 长度,宽度
  8. y = (iris["target"]==2).astype(np.float64) # 第二种类型的鸢尾花
  9. svm_clf = Pipeline([("scaler", StandardScaler()),
  10. ("linear_svc", LinearSVC(C=1, loss="hinge"))]) # pipeline是一种类似流水线的机制
  11. # LinearSVC,代表线性支持向量机,C是一个用来进行正则化的参数,C越小,错分会越多,
  12. # 但是泛化性较好;C越大,错分越少,但容易过拟合,
  13. # hinge损失函数
  14. svm_clf.fit(X,y)
  15. # 预测
  16. svm_clf.predict([[5.5,1.7]])
  17. # 输出:array([1.])

这里我们使用鸢尾花的长度和宽度特征来训练,标签值为是否是第二种类型的鸢尾花,1代表是,0代表否。使用pipeline机制对特征进行标准化,然后使用一个线性支持向量机来学习。可以看到,预测时候,给出特定的鸢尾花长度和宽度,即可判断该鸢尾花是否是第二种鸢尾花类型。

上次我们在介绍逻辑回归的时候使用过手写数字识别数据集,现在用支持向量机SVM来试试结果如何。

  1. from sklearn.svm import SVC
  2. digits = datasets.load_digits()
  3. x = digits['data']
  4. y = digits['target']
  5. svm_clf = SVC(kernel="poly",max_iter=1000) # 采用多项式核函数训练
  6. svm_clf.fit(x,y)
  7. # 预测
  8. x0 = x[5].reshape(1,-1)
  9. print(svm_clf.predict(x0))
  10. print(y[5])
  11. # 输出:[5] 5

我这里采用了支持向量机分类器来训练,SVC是通用的支持向量机分类器,可以通过指定参数kernel来设置所使用的核函数,SVC支持的核函数有以下几种:

’linear’:线性核函数

‘poly’:多项式核函数

‘rbf’:高斯核函数

‘sigmod’:sigmod核函数

‘precomputed’:核矩阵,表示自己提前计算好核函数矩阵,这时候算法内部就不再用核函数去计算核矩阵,而是直接用你给的核矩阵,核矩阵需要为n*n的。

我这里选择了多项式核函数进行训练。预测值为5,真实值也是5,可以看到预测的结果还是比较准确的。

  1. import matplotlib.pyplot as plt
  2. plt.imshow(digits['images'][5],cmap='gray') # 显示出数字图像
  3. plt.show()

下面我们尝试一下非线性数据集,看看sklearn效果如何。我们先用sklearn自带的make_moons()函数生成数据集并可视化。

  1. from sklearn.datasets import make_moons
  2. from sklearn.pipeline import Pipeline
  3. from sklearn.preprocessing import PolynomialFeatures
  4. from matplotlib import pyplot as plt
  5. X,y = make_moons(n_samples=100, noise=0.15)
  6. print(X.shape)
  7. print(y.shape)
  8. # 输出:
  9. # (100, 2)
  10. # (100,)
  11. plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.Paired)

可见,模拟了100个数据,X的维度是(100,2),100行2列,我们把一列作为纵坐标,一列作为横坐标,y作为颜色值区分类型,画出了图形,可以看到有很明显的非线性特征,无法用一条直线将两个数据集分开。

下面我们尝试用支持向量机分隔两类数据。

  1. from sklearn.pipeline import Pipeline
  2. from sklearn.preprocessing import StandardScaler
  3. from sklearn.svm import LinearSVC
  4. from sklearn.svm import SVC
  5. svm_clf = Pipeline([("poly_features", PolynomialFeatures(degree=3)),
  6. ("sclaer",StandardScaler()),("svm_clf",LinearSVC(C=10, loss="hinge"))])
  7. svm_clf.fit(X,y)

这里我用了一个多项式回归特征,degree=3代表了生成的是三次多项式,最后通过线性SVC来进行训练,C是一个惩罚变量,如果C越大,分类会越精确但是可能存在过拟合的情况,C越小相当于允许犯错,泛化能力较强。我们把训练后的结果用等高线contourf画出来,可以清晰的看到分隔边界。np.meshgrid根据给出的x坐标点和y坐标点生成更多的点,用每个x去和每个y分别配对,得到更多的坐标,然后去预测得到结果,把结果z作为高度,画出等高线,也就得到了分隔边界。

  1. # 生成两个坐标轴的范围,生成密集可以看得更清楚
  2. x_min, x_max = X[:, 0].min() - .5, X[:, 0].max() + .5
  3. y_min, y_max = X[:, 1].min() - .5, X[:, 1].max() + .5
  4. h=0.01
  5. print(np.arange(y_min,y_max,h).shape) # 输出:(282,)
  6. xx,yy=np.meshgrid(np.arange(x_min,x_max,h),np.arange(y_min,y_max,h))
  7. print(xx.shape) # 输出:(282, 440)
  8. print(yy.shape) # 输出:(282, 440)
  9. # ravel表示把数组拉成一维数组,np.c_表示把按列拼接两个矩阵
  10. z=svm_clf.predict(np.c_[xx.ravel(),yy.ravel()])
  11. z=z.reshape(xx.shape) # 转换成跟xx一样的维度
  12. plt.contourf(xx, yy, z, cmap=plt.cm.Paired, alpha=0.8) # 按z来画出决策边界
  13. plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.Paired) # 按y来画出原始数据的散点图
  14. plt.show()

可以看到,划分的还是比较准确的。

如果采用高斯核函数的话,看看决策边界是怎么样的。

  1. rbf_svm_clf = Pipeline([("sclaer",StandardScaler()),
  2. ("svm_clf",SVC(kernel="rbf", gamma=5, C=0.001))])
  3. rbf_svm_clf.fit(X,y)
  4. # 生成两个坐标轴的范围,生成密集可以看得更清楚
  5. x_min, x_max = X[:, 0].min() - .5, X[:, 0].max() + .5
  6. y_min, y_max = X[:, 1].min() - .5, X[:, 1].max() + .5
  7. h=0.01
  8. print(np.arange(y_min,y_max,h).shape)
  9. xx,yy=np.meshgrid(np.arange(x_min,x_max,h),np.arange(y_min,y_max,h))
  10. print(xx.shape)
  11. print(yy.shape)
  12. # ravel表示把数组拉成一维数组,np.c_表示把按列拼接两个矩阵
  13. z=rbf_svm_clf.predict(np.c_[xx.ravel(),yy.ravel()])
  14. z=z.reshape(xx.shape) # 转换成跟xx一样的维度
  15. plt.contourf(xx, yy, z, cmap=plt.cm.Paired, alpha=0.8) # 按z来画出决策边界
  16. plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.Paired) # 按y来画出原始数据的散点图
  17. plt.show()

gamma类似于C,也是一个正则项参数,只对核函数是rbf和sigmoid时生效。gamma越大越容易过拟合。画出决策边界,如下图所示:

可以看到,gamma值越大,模型越倾向于准确划分每个训练数据,从而会导致过拟合的问题,所以需要通过C来做正则化,在精度和泛化性中做权衡。

最后,我们尝试用sklearn提供的人脸数据集实现一个简单的人脸识别程序,看看SVM的效果。

  1. from sklearn.datasets import fetch_lfw_people
  2. lfw_people = fetch_lfw_people(min_faces_per_person=70, resize=0.4)
  3. n_samples, h, w = lfw_people.images.shape
  4. n_samples,h,w # 输出:(1288, 50, 37)
  5. X = lfw_people.data
  6. n_features = X.shape[1]
  7. # the label to predict is the id of the person
  8. y = lfw_people.target
  9. target_names = lfw_people.target_names
  10. n_classes = target_names.shape[0]
  11. print("Total dataset size:")
  12. print("n_samples: %d" % n_samples)
  13. print("n_features: %d" % n_features)
  14. print("n_classes: %d" % n_classes)
  1. # 输出:
  2. Total dataset size:
  3. n_samples: 1288
  4. n_features: 1850
  5. n_classes: 7

由于LFW数据比较大,所以并不能像鸢尾花数据集iris那样直接导入,需要额外下载,如果下载不了可以百度搜索一下,通过国内的某些链接来进行下载和部署。

下载后就可以正常使用了。可以看到人脸数据集一共有1288张图像,经过处理后的大小为50*37=1850个像素。类别为7类,也就是7个人。下面列出数据集中的前10张图片以及标签,也就是人名。

  1. import matplotlib.pyplot as plt
  2. fig = plt.figure(figsize=(20,20))
  3. for i in range(1,11):
  4. ax = fig.add_subplot(1,10,i)
  5. ax.imshow(images[i-1],cmap='gray')
  6. ax.axis('off') # 关闭坐标轴
  7. ax.set_title(target_names[y[i-1]])
  8. plt.show()

先进行训练:

  1. from sklearn.model_selection import RandomizedSearchCV, train_test_split
  2. from sklearn.svm import SVC
  3. from sklearn.pipeline import Pipeline
  4. from sklearn.preprocessing import StandardScaler
  5. X_train, X_test, y_train, y_test = train_test_split(
  6. X, y, test_size=0.25, random_state=42
  7. )
  8. svm_clf = Pipeline([("scaler",StandardScaler()),
  9. ("svm_clf",SVC(kernel="linear", gamma=5, C=0.001))])
  10. svm_clf.fit(X_train, y_train)

验证预测的精度:

  1. y_pred = svm_clf.predict(X_test)
  2. correct = 0
  3. for i in range(len(y_test)):
  4. if y_pred[i] == y_test[i]:
  5. correct += 1
  6. print("accuracy: %f" % (correct/len(y_test)))
  7. # 输出:accuracy: 0.835404

总体精度84%,下面打印出分类预测报告:

  1. from sklearn.metrics import ConfusionMatrixDisplay, classification_report
  2. print(classification_report(y_test, y_pred, target_names=target_names))
  1. precision recall f1-score support
  2. Ariel Sharon 0.57 0.62 0.59 13
  3. Colin Powell 0.77 0.88 0.82 60
  4. Donald Rumsfeld 0.68 0.63 0.65 27
  5. George W Bush 0.89 0.92 0.90 146
  6. Gerhard Schroeder 0.88 0.84 0.86 25
  7. Hugo Chavez 0.92 0.73 0.81 15
  8. Tony Blair 0.93 0.69 0.79 36
  9. accuracy 0.84 322
  10. macro avg 0.80 0.76 0.78 322
  11. weighted avg 0.84 0.84 0.83 322

我们下面展示前十张图片,以及图片的真实人物名字和预测的人物名字:

  1. fig = plt.figure(figsize=(20,10))
  2. for i in range(1,11):
  3. ax = fig.add_subplot(2,5,i)
  4. ax.imshow(X_test[i-1].reshape(h,w),cmap='gray')
  5. ax.axis('off') # 关闭坐标轴
  6. ax.set_title("pred:{}\ntrue:{}".format(target_names[y_pred[i-1]],target_names[y_test[i-1]]))
  7. plt.show()

可以看到,基本还是比较准确的。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/article/detail/53391
推荐阅读
相关标签
  

闽ICP备14008679号