1.随机生成一些数据
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
# use seaborn plotting defaults
import seaborn as sns
sns.set()
# 随机生成一些数据
from sklearn.datasets.samples_generator import make_blobs
X,y = make_blobs(n_samples=50,centers=2,random_state=0,cluster_std=0.6)
plt.scatter(X[:,0],X[:,1],c=y,s=50,cmap='autumn')
xfit = np.linspace(-1,3,5)
plt.scatter(X[:,0],X[:,1],c=y,s=50,cmap='autumn')
# 画出异常点
plt.plot([0.6],[2.1],'x',color='red',markeredgewidth=2, markersize=10)
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)
xfit = np.linspace(-1,3.5)
plt.scatter(X[:,0],X[:,1],c=y,s=50,cmap='autumn')
for m, b, d in [(1, 0.65, 0.33), (0.5, 1.6, 0.55), (-0.2, 2.9, 0.2)]:
yfit = m * xfit + b
plt.plot(xfit,yfit,'-k')
plt.fill_between(xfit,yfit - d,yfit + d,edgecolor='none',color='#AAAAAA',alpha=0.4)
plt.xlim(-1,3.5)
from sklearn.svm import SVC
# 线性分类
model = SVC(kernel='linear')
model.fit(X,y)
注:decision_function_shape共有两个可选值,一个是ovr,一个是ovo。
ovr(one ve rest)的思想很简单,无论你是多少元分类,我们都可以看做二元分类。具体做法是,对于第K类的分类决策,我们把所有第K类的样本作为正例,除了第K类样本以外的所有样本都作为负例,然后在上面做二元分类,得到第K类的分类模型。其他类的分类模型获得以此类推。
ovo(one-vs-one)则是每次在所有的T类样本里面选择两类样本出来,不妨记为T1类和T2类,把所有的输出为T1和T2的样本放在一起,把T1作为正例,T2作为负例,进行二元分类,得到模型参数。我们一共需要T(T-1)/2次分类。
从上面的描述可以看出ovr相对简单,但分类效果相对略差(这里指大多数样本分布情况,某些样本分布下ovr可能更好)。而ovo分类相对精确,但是分类速度没有ovr快。一般建议使用ovo以达到较好的分类效果。
5.绘制决策边界线以及超平面,并输出支持向量
def plot_svc_decision_function(model,ax=None,plot_support=True):
"""Plot the decision function for a 2D SVC"""
if ax is None:
ax = plt.gca()
#get the range of x
xlim = ax.get_xlim()
#get the range of y
ylim = ax.get_ylim()
# create grid to evaluate model
x = np.linspace(xlim[0], xlim[1],30)
y = np.linspace(ylim[0], ylim[1],30)
Y,X = np.meshgrid(y,x)
#xy列
xy = np.vstack([X.ravel(), Y.ravel()]).T
P = model.decision_function(xy).reshape(X.shape)
# plot decision boundary and margins
ax.contour(X, Y, P, colors='k',
levels=[-1, 0, 1], alpha=0.5,
linestyles=['--', '-', '--'])
# plot support vectors
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)
从图中可以看出,中间的实线为超平面,两边的虚线为决策边界,决策边界上有三个点,这三个点为支持向量。在sklearn中,它们存在model.support_vectors_这个属性中。
注:model.decision_function函数:实例到各个类所代表的超平面的距离。
model.support_vectors_
6.观察可以发现,只需要支持向量我们就可以把模型构造出来,接下来我们尝试用不同多的数据点,来观察效果会不会发生改变。这里我们分别使用60个和120个数据点。
def plot_svm(N=10,ax=None):
# 聚类数据生成器
X,y = make_blobs(n_samples=200,centers=2,random_state=0,cluster_std=0.6)
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))
只要支持向量没变,其他的数据怎么加都不会影响到SVM的分类结果。
7.引入核函数的SVM
(1)首先先用线性的核来看一下在下面这样比较难的数据集上还能分了吗?
from sklearn.datasets.samples_generator import make_circles
# 生成一个圈
X,y = make_circles(100,factor=.1,noise=.1)
clf = SVC(kernel='linear').fit(X,y)
plt.scatter(X[:,0],X[:,1],c=y,s=50,cmap='autumn')
plot_svc_decision_function(clf,plot_support=False)
# 加入新的维度r
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', C=1e6,gamma='auto')
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')
使用这种核支持向量机,我们学习了一个合适的非线性决策边界。这种核变换策略在机器学习中经常被使用!
8. 调节SVM参数: Soft Margin问题,调节C参数,当C趋近于无穷大时:意味着分类严格不能有错误;当C趋近于很小的时:意味着可以有更大的错误容忍。
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)
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)
9.例子(As an example of support vector machines in action, let’s take a look at the facial recognition problem.We will use the Labeled Faces in the Wild dataset, which consists of several thousand collated photos of various public figures.A fetcher for the dataset is built into Scikit-Learn:)
(1)获取数据集
from sklearn.datasets import fetch_lfw_people
faces = fetch_lfw_people(min_faces_per_person=60)
print(faces.target_names)
print(faces.images.shape)
fig,ax = plt.subplots(3,5)
for i,axi in enumerate(ax.flat):
# i表示图的位置,axi表示该图的轴
axi.imshow(faces.images[i],cmap='bone')
axi.set(xticks=[],yticks=[],xlabel=faces.target_names[faces.target[i]])
(3)每个图的大小是 [62×47],每一个像素点是一个特征,特征太多,用PCA进行降维操作。
from sklearn.svm import SVC
from sklearn.decomposition import PCA
from sklearn.pipeline import make_pipeline
pca = PCA(n_components=150,whiten=True,random_state=42)
svc = SVC(kernel='linear',class_weight='balanced')
model = make_pipeline(pca,svc)
注:①串行化,通过pipeline类实现。make_pipeline函数是pipeline类的简单实现,只需传入每个step的类实例即可,不需自己命名,自动将类的小写设为该step的名。(串行实例说明:假如有数据集4个特征,step1:标准化,step2:log,数据集先进行标准化,标准化之后的数据进行log处理,最后共有4个特征)
②并行化,通过FeatureUnion实现。FeatureUnion同样通过(key,value)对来设置,通过set_params设置参数。不同的是,每一个step分开计算,FeatureUnion最后将它们计算得到的结果合并到一块,返回的是一个数组,不具备最后一个estimator的方法。有些数据需要标准化,或者取对数,或onehot编码最后形成多个特征项,再选择重要特征,这时候FeatureUnion非常管用。(并行实例说明:假如有数据集4个特征,step1:标准化,step2:log,经过并行之后,数据集前4列为标准化之后的特征,后4列为log后的特征,最后共有8个特征)
(4)将数据集分成训练集合测试集
from sklearn.model_selection import train_test_split
Xtrain, Xtest, ytrain, ytest = train_test_split(faces.data,faces.target,random_state=40)
(5)使用grid search和cross-validation来选择我们的参数。
from sklearn.model_selection import GridSearchCV
param_grid = {'svc__C': [1, 5, 10],
'svc__gamma': [0.0001, 0.0005, 0.001]}
grid = GridSearchCV(model,param_grid)
%time grid.fit(Xtrain, ytrain)
print(grid.best_params_)
(6)测试一下,看看效果
model = grid.best_estimator_
yfit = model.predict(Xtest)
fig,ax = plt.subplots(4,6)
for i,axi in enumerate(ax.flat):
# i表示图的位置,axi表示该图的轴
axi.imshow(Xtest[i].reshape(62, 47), cmap=‘bone’)
axi.set(xticks=[], yticks=[])
axi.set_ylabel(faces.target_names[yfit[i]].split()[-1],
color=‘black’ if yfit[i] == ytest[i] else ‘red’)
fig.suptitle(‘Predicted Names; Incorrect Labels in Red’, size=14)
(7)sklearn中的classification_report函数用于显示主要分类指标的文本报告.在报告中显示每个类的精确度,召回率,F1值等信息。
from sklearn.metrics import classification_report
print(classification_report(ytest,yfit,target_names=faces.target_names))
精度(precision) = 正确预测的个数(TP)/被预测正确的个数(TP+FP)
召回率(recall)=正确预测的个数(TP)/预测个数(TP+FN)
F1 = 2x精度x召回率/(精度+召回率)
(8)画出混淆矩阵(我前面的博客有对混淆矩阵的介绍)
from sklearn.metrics import confusion_matrix
mat = confusion_matrix(ytest,yfit)
sns.heatmap(mat.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')
10.总结
SVM模型有两个非常重要的参数C与gamma。其中 C是惩罚系数,即对误差的宽容度。c越高,说明越不能容忍出现误差,容易过拟合。C越小,容易欠拟合。C过大或过小,泛化能力变差
gamma是选择核函数作为kernel后,该函数自带的一个参数。隐含地决定了数据映射到新的特征空间后的分布,gamma越大,支持向量越少,gamma值越小,支持向量越多。支持向量的个数影响训练与预测的速度。sklearn中的SVC中gamma可取两个值:①auto: 1 / n_features②scale:1 / (n_features * X.std())。
一些小的结论:①参数的选择(特征值)都是通过交叉验证进行选择的,经验值都是不靠谱的②无论是线性还是非线性函数都可以设置C值来控制软间隔。但是只有非线性高斯函数(径向基函数)才能通过设置gama值来控制软间隔。从而提高模型的泛化能力;③样本数目少于特征维度并不一定会导致过拟合;④RBF核应该可以得到与线性核相近的效果(按照理论,RBF核可以模拟线性核),可能好于线性核,也可能差,但是不应该相差太多。
参考文档:
https://www.cnblogs.com/zhengxingpeng/p/6670443.html
https://blog.csdn.net/wateryouyo/article/details/53909636
https://blog.csdn.net/lujiandong1/article/details/46386201