上期回顾:
支持向量机(Support Vector Machine, SVM)是一类按监督学习(supervised learning)方式对数据进行的广义线性分类器(generalized linear classifier),其是对学习样本求解的最大边距超平面(maximum-margin hyperplane) 。
SVM使用铰链损失函数(hinge loss)计算经验风险(empirical risk)并在求解系统中加入了正则化项以优化结构风险(structural risk),是一个具有稀疏性和稳健性的分类器 。SVM可以通过核方法(kernel method)进行非线性分类,是常见的核学习(kernel learning)方法之一 。
(1)线性SVM分类
通过建立一个超平面,优化模型使最靠近超平面的点的间隔最大,称之为大间隔分类。由于SVM对特征的缩放特别敏感,对函数间隔影响较大,所以要对特征先进行标准化,用于特征的缩放。
如果将实例严格的分类,让所有的实例都处于正确的一方,称之为硬间隔分类;反之,称之为软间隔分类。
(2)非线性SVM分类
处理方法1:使用PolynomialFeatures让高维的特征变成新的特征
处理方法2:使用支持向量机中的核技巧
本期讨论SVM中参数问题与人脸识别的实战训练
参数比较:这里使用的是sklearn.datasets包中的make_blobs来构建数据, 先模拟量三条线,这里通过对x变量进行构建,然后通过zip方法进行函数的模拟, 然后使用画出图像的最大间隔线,这里使用的是svm自带的decision_function函数进行画图,然后进行训练模型,这里使用的核函数是linear,通过改变数据的数量进行绘图,发现只要最大间隔上面的点不改变,画出的线不改变。
- ##导入数据包
- %matplotlib inline
- import numpy as np
- import matplotlib.pyplot as plt
- from scipy import stats
- import seaborn as sns;sns.set()
- from sklearn.datasets import make_blobs
- X,y=make_blobs(n_samples=50,centers=2,random_state=0,cluster_std=0.6)##cluster_std用于描述数据离散程度
- plt.scatter(X[:,0],X[:,1],c=y,s=50,cmap='autumn')##参数解释c:色彩序列,s:数组标量,cmap:图谱
- xfit =np.linspace(-1,3.5)
- plt.scatter(X[:,0],X[:,1],c=y,s=50,cmap='autumn')
- for m ,b in[(1,0.65),(0.5,1.6),(-0.2,2.9)]:
- plt.plot(xfit,m*xfit+b,'-k')
- plt.xlim(-1,3.5)
- def plot_svc_decision_function(model,ax=None,plot_support=True):
- if ax is None:
- ax=plt.gca()##用于确定坐标轴
- xlim=ax.get_xlim()
- ylim =ax.get_ylim()
- x = np.linspace(xlim[0],xlim[1],30)
- y = np.linspace(ylim[0],ylim[1],30)
- Y,X = np.meshgrid(y,x)##用x,y两个轴上的点画网格
- xy =np.vstack([X.ravel(),Y.ravel()]).T
- P =model.decision_function(xy).reshape(X.shape)
- ax.contour(X,Y,P,color='k',
- levels=[-1,0,1],alpha=0.5,
- linestyle=['--','-','--'])
- if plot_support:
- ax.scatter(model.support_vectors_[:,0],model.support_vectors_[:,1],
- s=300,linewidth=1,facecolors='none')
- ax.set_xlim(xlim)
- ax.set_ylim(ylim)
- plt.scatter(X[:,0],X[:,1],c=y,s=50,cmap='autumn')
- plot_svc_decision_function(model)
- from sklearn.svm import SVC
- model =SVC(kernel='linear')
- model.fit(X,y)
- model.support_vectors_##这个是最大间隔上的点
- def plot_svm(N=10, ax=None):
- X, y = make_blobs(n_samples=200, centers=2,
- random_state=0, cluster_std=0.60)
- X = X[:N]
- y = y[:N]
- model = SVC(kernel='linear', C=1E10)
- model.fit(X, y)
-
- ax = ax or plt.gca()
- ax.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
- ax.set_xlim(-1, 4)
- ax.set_ylim(-1, 6)
- plot_svc_decision_function(model, ax)
- # 分别对不同的数据点进行绘制
- fig, ax = plt.subplots(1, 2, figsize=(16, 6))
- fig.subplots_adjust(left=0.0625, right=0.95, wspace=0.1)
- for axi, N in zip(ax, [60, 120]):
- plot_svm(N, axi)
- axi.set_title('N = {0}'.format(N))
这条线就是我们希望得到的决策边界啦
观察发现有3个点做了特殊的标记,它们恰好都是边界上的点
它们就是我们的support vectors(支持向量)
在Scikit-Learn中, 它们存储在这个位置 support_vectors_
(一个属性)
改变成为非线性模型继续测试,并测试核函数变化的情况,切换成为高纬度进行测试, 调节C(惩罚项)参数,当C趋近于无穷大时:意味着分类严格不能有错误,当C趋近于很小的时:意味着可以有更大的错误容忍,调节gamma参数:gamma越大,模型复杂度越高。
- from mpl_toolkits import mplot3d
- r = np.exp(-(X ** 2).sum(1))
- # 可以想象一下在三维中把环形数据集进行上下拉伸
- def plot_3D(elev=30, azim=30, X=X, y=y):
- ax = plt.subplot(projection='3d')
- ax.scatter3D(X[:, 0], X[:, 1], r, c=y, s=50, cmap='autumn')
- ax.view_init(elev=elev, azim=azim)
- ax.set_xlabel('x')
- ax.set_ylabel('y')
- ax.set_zlabel('r')
-
- plot_3D(elev=45, azim=45, X=X, y=y)
- clf = SVC(kernel='rbf')
- clf.fit(X, y)
- plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
- plot_svc_decision_function(clf)
- plt.scatter(clf.support_vectors_[:, 0], clf.support_vectors_[:, 1],
- s=300, lw=1, facecolors='none');
- X, y = make_blobs(n_samples=100, centers=2,
- random_state=0, cluster_std=0.8)
- plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
- X, y = make_blobs(n_samples=100, centers=2,
- random_state=0, cluster_std=0.8)
-
- fig, ax = plt.subplots(1, 2, figsize=(16, 6))
- fig.subplots_adjust(left=0.0625, right=0.95, wspace=0.1)
- # 选择两个C参数来进行对别实验,分别为10和0.1
- for axi, C in zip(ax, [10.0, 0.1]):
- model = SVC(kernel='linear', C=C).fit(X, y)
- axi.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
- plot_svc_decision_function(model, axi)
- axi.scatter(model.support_vectors_[:, 0],
- model.support_vectors_[:, 1],
- s=300, lw=1, facecolors='none');
- axi.set_title('C = {0:.1f}'.format(C), size=14)
- X, y = make_blobs(n_samples=100, centers=2,
- random_state=0, cluster_std=1.1)
-
- fig, ax = plt.subplots(1, 2, figsize=(16, 6))
- fig.subplots_adjust(left=0.0625, right=0.95, wspace=0.1)
- # 选择不同的gamma值来观察建模效果
- for axi, gamma in zip(ax, [10.0, 0.1]):
- model = SVC(kernel='rbf', gamma=gamma).fit(X, y)
- axi.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
- plot_svc_decision_function(model, axi)
- axi.scatter(model.support_vectors_[:, 0],
- model.support_vectors_[:, 1],
- s=300, lw=1, facecolors='none');
- axi.set_title('gamma = {0:.1f}'.format(gamma), size=14)
上述代码分别通过设置松弛因子控制参数C的值分别为10,0.1,gamma值分别为10,0.1,可以通过以下的图片进行判断,对于参数C来说,C=10时相当于对分类的要求比较严格,在分类对的前提下,再去寻找最宽的决策边界,虽然分类完全正确,但是决策边界太窄,最大间隔太小,而C=0.1时,虽然有点错误,但是决策边界大。
而当gamma=10时,训练后的模型十分复杂,虽然分类全部正确,但是决策边界来看可以得出过拟合的几率比较大,但是gamma=0.1时,模型比较简单,虽然出现部分错误分类,但是决策边界比较稳定
人脸识别:这里读取了sklearn.datasets.fetch_lfw_people的数据,为了使每个人的样本不至于过少,这里选取了每个人可以有60个样本。
- ##进行数据读取
- from sklearn.datasets import fetch_lfw_people
- faces = fetch_lfw_people(min_faces_per_person=60)
- print(faces.target_names)
- print(faces.images.shape)
- ##数据预处理
- from sklearn.model_selection import train_test_split
- X_train,X_test,Y_train,Y_test = train_test_split(faces.data,faces.target,random_state=40)
模型建立:这里使用的是流水线方法进行两个函数的合并,使用了Pipeline与make_pipeline,然后用GridSearchCV进行参数的调优。由于这里使用的是图片数据,图片由像素点构成,会导致特征过多,所以使用PCA方法进行降维,然后使用GridSearchCV进行降维维度的调参。
- ##模型建立
- from sklearn.pipeline import Pipeline
- from sklearn.svm import SVC
- from sklearn.decomposition import PCA
- pca_svc = Pipeline(steps=[('pca',PCA( whiten=True, random_state=42)),('svc',SVC(class_weight='balanced'))])
- from sklearn.model_selection import GridSearchCV
- param_grid = [{'pca__n_components':[100,150,200,500],'svc__C': [1,5,10],'svc__gamma':[0.0001,0.0005,0.001],
- 'svc__kernel':['linear','poly','rbf','sigmoid']}]
- grid_search =GridSearchCV(pca_svc,param_grid)
- grid_search.fit(X_train,Y_train)
- grid_search.best_params_
- from sklearn.pipeline import make_pipeline
- pca = PCA( whiten=True, random_state=42)
- svc = SVC(class_weight='balanced')
- #先降维然后再SVM
- model = make_pipeline(pca, svc)
- grid_search =GridSearchCV(model,param_grid)
- grid_search.fit(X_train,Y_train)
- grid_search.best_params_
注:此处在Pipeline中一定要对两个方法都有参数的调优,否则会报错。
对训练集进行预测并构造混淆矩阵:
- model=grid_search.best_estimator_
- y_pred = model.predict(X_test)
- from sklearn.metrics import classification_report
- print(classification_report(Y_test,y_pred,target_names=faces.target_names))
- from sklearn.metrics import confusion_matrix
- import seaborn as sns
- import matplotlib.pyplot as plt
- matrix = confusion_matrix(Y_test,y_pred)
- sns.heatmap(matrix.T,square=True,annot=True,fmt='d',cbar=False,
- xticklabels=faces.target_names,
- yticklabels=faces.target_names)
- plt.xlabel('true label')
- plt.ylabel('predicted label')
从中我们可以看出,在中间的对角线中代表数据预测的正确,也可以从上面Classification_report中得出,对George W Bush 的召回率最高也就是正确率最高,在混淆矩阵中也是他的正确数量最多。