SVC
本文作为SVM的进阶说明,在上一篇文章的基础上进行提高和探索。
SVC 参数
先说明一下SVC中一些参数的作用,软间隔体现在这些参数当中
SVC参数列表
C: float参数 默认值为1.0
错误项的惩罚系数。C越大,即对分错样本的惩罚程度越大,因此在训练样本中准确率越高,但是泛化能力降低,也就是对测试数据的分类准确率降低。相反,减小C的话,容许训练样本中有一些误分类错误样本,泛化能力强。对于训练样本带有噪声的情况,一般采用后者,把训练样本集中错误分类的样本作为噪声。
参考文章 Sklearn —SVC 参数与实例
Sample 1 Soft margin
引入一些包
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
from sklearn import svm
from sklearn.datasets import make_circles, make_moons, make_blobs,make_classification
利用plt.subplots()
来查看四种不同的数据的分布
n_samples = 100
datasets = [
make_moons(n_samples=n_samples, noise=0.2, random_state=0),
make_circles(n_samples=n_samples, noise=0.2, factor=0.5, random_state=1),
make_blobs(n_samples=n_samples, centers=2, random_state=5),
make_classification(n_samples=n_samples,n_features = 2,n_informative=2,n_redundant=0, random_state=5)
]
Kernel = ['linear']
fig1,ax1 = plt.subplots(nrows=2, ncols=2,figsize=(10,9))
axes1 = ax1.flatten()
for id,(X,Y) in enumerate(datasets):
axes1[id].scatter(X[:,0],X[:,1],c=Y,s = 50,cmap="rainbow")
# print(list(enumerate(datasets))[0])
使用线性分类器进行训练,观察训练结果
# len(datasets)
nrows = len(datasets)
ncols = len(Kernel) + 1
fig2,axes2 = plt.subplots(nrows,ncols,figsize=(10,16))
for id_datasets,(X,Y) in enumerate(datasets):
# 去掉每一个子图的坐标轴
axes2[id_datasets,0].set_xticks(())
axes2[id_datasets,0].set_yticks(())
axes2[id_datasets,1].set_xticks(())
axes2[id_datasets,1].set_yticks(())
ax_d = axes2[id_datasets,0]
if id_datasets == 0:
ax_d.set_title("Input data")
ax_d.scatter(X[:,0],X[:,1],c=Y,zorder=10, cmap=plt.cm.Paired,edgecolors='k')
print(ax)
for id_kernel,kernel in enumerate(Kernel):
ax_k = axes2[id_datasets,1]
if id_datasets == 0:
ax_k.set_title(kernel)
#建模
clf = svm.SVC(kernel = kernel,C=1).fit(X,Y)
score = clf.score(X,Y)
ax_k.scatter(X[:,0],X[:,1],c= Y,zorder = 10,cmap = plt.cm.Paired,edgecolors='k')
ax_k.scatter(clf.support_vectors_[:,0],clf.support_vectors_[:,1],
s=100,facecolors = 'none',zorder=10,edgecolors = 'red')
#绘制决策边界,这里加减0.5的是要扩充外边界,防止有白边
x_min, x_max = X[:, 0].min() - .5, X[:, 0].max() + .5
y_min, y_max = X[:, 1].min() - .5, X[:, 1].max() + .5
XX,YY = np.mgrid[x_min:x_max:200j, y_min:y_max:200j]
#np.c_,类似于np.vstack的功能
Z = clf.decision_function(np.c_[XX.ravel(), YY.ravel()]).reshape(XX.shape)
#填充等高线不同区域的颜色
ax_k.pcolormesh(XX, YY, Z > 0, cmap=plt.cm.Paired)
#绘制等高线
ax_k.contour(XX, YY, Z, colors=['k', 'k', 'k'], linestyles=['--', '-', '--'],
levels=[-1, 0, 1])
#为每张图添加分类的分数
ax_k.text(0.95, 0.06, ('%.2f' % score).lstrip('0')
, size=15
, bbox=dict(boxstyle='round', alpha=0.8, facecolor='white')
#为分数添加一个白色的格子作为底色
, transform=ax_k.transAxes #确定文字所对应的坐标轴,就是ax子图的坐标轴本身
, horizontalalignment='right' #位于坐标轴的什么方向
)
plt.tight_layout()
plt.show()
Sample 2 样本不均衡
对于分类问题,永远都逃不过的一个痛点就是样本不均衡问题。样本不均衡是指在一组数据集中,标签的一类天生占有很大的比例,但我们有着捕捉出某种特定的分类的需求的状况。比如,我们现在要对潜在犯罪者和普通人进行 分类,潜在犯罪者占总人口的比例是相当低的,也许只有2%左右,98%的人都是普通人,而我们的目标是要捕获 出潜在犯罪者。这样的标签分布会带来许多问题。
首先,分类模型天生会倾向于多数的类,让多数类更容易被判断正确,少数类被牺牲掉。因为对于模型而言,样本量越大的标签可以学习的信息越多,算法就会更加依赖于从多数类中学到的信息来进行判断。如果我们希望捕获少数类,模型就会失败。其次,模型评估指标会失去意义。这种分类状况下,即便模型什么也不做,全把所有人都当 成不会犯罪的人,准确率也能非常高,这使得模型评估指标accuracy变得毫无意义,根本无法达到我们的“要识别 出会犯罪的人”的建模目的。
所以现在,我们首先要让算法意识到数据的标签是不均衡的,通过施加一些惩罚或者改变样本本身,来让模型向着
捕获少数类的方向建模。然后,我们要改进我们的模型评估指标,使用更加针对于少数类的指标来优化模型。
要解决第一个问题,我们在逻辑回归中已经介绍了一些基本方法,比如上采样下采样。但这些采样方法会增加样本的总数,对于支持向量机这个样本总是对计算速度影响巨大的算法来说,我们完全不想轻易地增加样本数量。况 且,支持向量机中地决策仅仅决策边界的影响,而决策边界又仅仅受到参数C和支持向量的影响,单纯地增加样本 数量不仅会增加计算时间,可能还会增加无数对决策边界无影响的样本点。因此在支持向量机中,我们要大力依赖 我们调节样本均衡的参数:SVC类中的class_weight和接口fit中可以设定的sample_weight。
在逻辑回归中,参数class_weight默认None,此模式表示假设数据集中的所有标签是均衡的,即自动认为标签的 比例是1:1。所以当样本不均衡的时候,我们可以使用形如{“标签的值1”:权重1,“标签的值2”:权重2}的字典来 输入真实的样本标签比例,来让算法意识到样本是不平衡的。或者使用”balanced“模式,直接使用 n_samples/(n_classes * np.bincount(y))作为权重,可以比较好地修正我们的样本不均衡情况。
SVC的参数:class_weight
可输入字典或者"balanced
”,可不填,默认None
对SVC,将类i的参数C设置为class_weight [i] * C
。如果没有给出 具体的class_weight
,则所有类都被假设为占有相同的权重1,模型会根据数据原本的状况去训练。如果希望改善 样本不均衡状况,请输入形如{“标签的值1”:权重1,“标签的值2”:权重2}的字典,则参数C将会自动被设为: 标签的值1的C:权重1 * C,标签的值2的C:权重2*C 或者,可以使用“balanced
”模式,这个模式使用y的值自动调整与输入数据中的类频率成反比的权重为 n_samples/(n_classes * np.bincount(y))
SVC的接口fit的参数:sample_weight
数组,结构为 (n_samples, )
,必须对应输入fit中的特征矩阵的每个样本
每个样本在fit时的权重,让权重 * 每个样本对应的C值来迫使分类器强调设定的权重更大的样本。通常,较大的权 重加在少数类的样本上,以迫使模型向着少数类的方向建模
通常来说,这两个参数我们只选取一个来设置。如果我们同时设置了两个参数,则C会同时受到两个参数的影响, 即 class_weight
中设定的权重 * sample_weight
中设定的权重 * C
我们接下来就来看看如何使用这个参数。
首先,我们来自建一组样本不平衡的数据集。我们在这组数据集上建两个SVC模型,一个设置有class_weight
参 数,一个不设置class_weight
参数。我们对两个模型分别进行评估并画出他们的决策边界,以此来观察 class_weight
带来的效果。
首先导入一些包
import numpy as np
import matplotlib.pyplot as plt
from sklearn import svm
from sklearn.datasets import make_blobs
构造两个样本不均衡数据,centers
表示两个数据的中心点,clusters_std
表示两个类别的方差,一般方差越大样本越分散
class_1 = 500
class_2 = 50
centers = [[0.0,0.0],[2.0,2.0]]
clusters_std = [1.5,0.5] # 两个类别的方差
X, y = make_blobs(n_samples=[class_1, class_2],
centers=centers,
cluster_std=clusters_std,
random_state=0, shuffle=False)
print(X.shape) #(550,2)
样本可视化
plt.scatter(X[:,0],X[:,1],c = y,s = 10 ,cmap = "rainbow")
使用线性分类器,惩罚系数为1,不设定class_weight
#不设定class_weight
clf = svm.SVC(kernel = 'linear',C=1.0)
clf.fit(X,y)
设定class_weight
#设定class_weight
wclf = svm.SVC(kernel='linear', class_weight={1: 10})
wclf.fit(X, y)
给两个模型分别打分看看,这个分数是accuracy准确度,做样本均衡之后,我们的准确率下降了,没有样本均衡的准确率更高
print(clf.score(X,y)) #0.9418181818181818
print(wclf.score(X,y)) #0.9127272727272727
画出图像
plt.figure(figsize=(13,10))
# plt.scatter(X[:,0],X[:,1],c=y,cmap = "rainbow",s = 10)
red = plt.scatter(X[:,0][y == 1],X[:,1][y == 1],c='red',s = 10,label = 'xx')
darkviolet = plt.scatter(X[:,0][y == 0],X[:,1][y == 0],c='darkviolet',s = 10,label = 'bb')
ax = plt.gca()
xlim = ax.get_xlim()
ylim = ax.get_ylim()
xx = np.linspace(xlim[0],xlim[1],30)
yy = np.linspace(ylim[0],ylim[1],30)
YY,XX = np.meshgrid(yy,xx)
xy = np.vstack([XX.ravel(),YY.ravel()]).T
print(xy)
Z_clf = clf.decision_function(xy).reshape(XX.shape)
a = ax.contour(XX,YY,Z_clf,colors = 'black',levels=[0], alpha=0.5, linestyles=['-'])
Z_wclf = wclf.decision_function(xy).reshape(XX.shape)
b = ax.contour(XX, YY, Z_wclf, colors='red', levels=[0], alpha=0.5, linestyles=['-'])
#第三步:画图例
# legend() 只能有一个,下面那个会覆盖上面那个legend
plt.legend([a.collections[0], b.collections[0],red,darkviolet],
["non weighted", "weighted","small","more"],
loc="upper right")
plt.show()
如果都使用高斯径向基核函数