机器学习:12. SVM支持向量机(下)
二分类SVC的进阶
1. 二分类SVC中的样本不均衡问题:重要参数class_weight
样本不均衡是指在一组数据集中,标签的一类天生占有很大的比例,但我们有着捕捉出某种特定的分类的需求的状况。
但是,分类模型天生会倾向于多数的类,让多数类更容易被判断正确,少数类被牺牲掉。因此,这样得到的模型评估指标将失去意义。此在支持向量机中,我们要大力依赖我们调节样本均衡的参数:SVC类中的class_weight和接口fit中可以设定的sample_weight。
SVC的参数:class_weight
可输入字典或者"balanced”,可不填,默认None 对SVC,将类i的参数C设置为class_weight [i] * C。
SVC的接口fit的参数:sample_weight
数组,结构为 (n_samples, ),必须对应输入fit中的特征矩阵的每个样本在fit时的权重。较大的权重加在少数类的样本上,以迫使模型向着少数类的方向建模。
参数的使用
1 导入库
import numpy as np
import matplotlib.pyplot as plt
from sklearn import svm
from sklearn.datasets import make_blobs
2 创建样本不均衡数据集
class_1 = 500 #类别1有500个样本 10:1
class_2 = 50 #类别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], #n_samples=[标签1样本数, 标签2样本数]
centers=centers,
cluster_std=clusters_std,
random_state=0, shuffle=False)
X.shape
#看看数据集长什么样
plt.scatter(X[:, 0], X[:, 1], c=y, cmap="rainbow",s=10)
#其中红色点是少数类,紫色点是多数类
image.png
3. 在数据集上分别建模
#不设定class_weight
clf = svm.SVC(kernel='linear', C=1.0)
clf.fit(X, y)
#设定class_weight
wclf = svm.SVC(kernel='linear', class_weight={1: 10}) #少数类1占的权重是10
wclf.fit(X, y)
#给两个模型分别打分看看,这个分数是accuracy准确度
#可以看到做样本均衡后,准确率下降了
clf.score(X,y)
wclf.score(X,y)
输出结果
4. 绘制两个模型下数据的决策边界
#首先要有数据分布
plt.figure(figsize=(6,5))
plt.scatter(X[:, 0], X[:, 1], c=y, cmap="rainbow",s=10)
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
#第二步:找出我们的样本点到决策边界的距离
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=['-'])
#第三步:画图例
plt.legend([a.collections[0], b.collections[0]], ["non weighted", "weighted"],
loc="upper right")
plt.show()
两个模型
可以看到做了样本平衡后,少数类几乎全部都被分类正确了,但是多数类有许多被分错了。没有做样本均衡时,多数类分布正确,但是少数类分布错误的多,所以没做均衡的准确率反而高,就可以理解了。做均衡是为了能够更加准确得评判少数类。
图例这一步是怎么做到的
a.collections #调用这个等高线对象中画的所有线,返回一个惰性对象
#用[*]把它打开试试看
[*a.collections] #返回了一个linecollection对象,其实就是我们等高线里所有的线的列表
#现在我们只有一条线,所以我们可以使用索引0来锁定这个对象
a.collections[0]
#plt.legend([对象列表],[图例列表],loc)
#只要对象列表和图例列表相对应,就可以显示出图例
image.png
2 SVC的模型评估指标
在现实中,我们往往都在追求捕捉少数类,因为在很多情况下,将少数类判断错的代价是巨大的。所以在现实中,我们往往在寻找捕获少数类的能力和将多数类判错后需要付出的成本的平衡。为了评估这样的能力,我们将引入新的模型评估指标:混淆矩阵和ROC曲线来帮助我们。
2.1 混淆矩阵
混淆矩阵是二分类问题的多维衡量指标体系,在样本不平衡时极其有用。在混淆矩阵中,我们将少数类认为是正例,多数类认为是负例。在决策树,随机森林这些普通的分类算法里,即是说少数类是1,多数类是0。在SVM里,就是说少数类是1,多数类是-1。普通的混淆矩阵,一般使用{0,1}来表示。
混淆矩阵中,永远是真实值在前,预测值在后
2.1.1 模型整体效果:准确率
准确率Accuracy就是所有预测正确的所有样本除以总样本,通常来说越接近1越好。
2.1.2 捕捉少数类的艺术:精确度,召回率和F1 score
精确度精确度Precision,又叫查准率,表示所有被我们预测为是少数类的样本中,真正的少数类所占的比例
image.png
在支持向量机中,精确度可以被形象地表示为决策边界上方的所有点中,红色点所占的比例。精确度越高,代表我们捕捉正确的红色点越多,对少数类的预测越精确。当每一次将多数类判断错误的成本非常高昂的时候(比如大众召回车辆的例子),我们会追求高精确度。 召回率
召回率Recall,又被称为敏感度(sensitivity),表示所有真实为1的样本中,被我们预测正确的样本所占的比例。召回率越高,代表我们尽量捕捉出了越多的少数类。如果我们希望不计一切代价,找出少数类(比如找出潜在犯罪者的例子),那我们就会追求高召回率。
而召回率和精确度是此消彼长的,两者之间的平衡代表了捕捉少数类的需求和尽量不要误伤多数类的需求的平衡。究竟要偏向于哪一方,取决于我们的业务需求:究竟是误伤多数类的成本更高,还是无法捕捉少数类的代价更高。
2.1.3 判错多数类的考量:特异度与假正率
特异度(Specificity)表示所有真实为0的样本中,被正确预测为0的样本所占的比例。
特异度衡量了一个模型将多数类判断正确的能力,而1 - specificity就是一个模型将多数类判断错误的能力,叫做假正率(False Positive Rate):
2.1.4 sklearn中的混淆矩阵
image.png2.2 ROC曲线和相关问题
ROC曲线,全称The Receiver Operating Characteristic Curve,译为受试者操作特性曲线。这是一条以不同阈值下的假正率FPR为横坐标,不同阈值下的召回率Recall为纵坐标的曲线。横坐标是FPR,代表着模型将多数类判断错误的能力,纵坐标Recall,代表着模型捕捉少数类的能力,所以ROC曲线代表着,随着Recall的不断增加,FPR如何增加。我们建立ROC曲线的根本目的是找寻Recall和FPR之间的平衡,让我们能够衡量模型在尽量捕捉少数类的时候,误伤多数类的情况会如何变化。
2.2.1 重要接口decision_function
我们在画等高线,也就是决策边界的时候曾经使用SVC的接口decision_function,它返回我们输入的特征矩阵中每个样本到划分数据集的超平面的距离。所以,到超平面的距离一定程度上反应了样本归属于某个标签类的可能性。接口decision_function返回的值也因此被我们认为是SVM
中的置信度(confidence)。
2.2.2 sklearn中的ROC曲线和AUC面积
在sklearn中,我们有帮助我们计算ROC曲线的横坐标假正率FPR,纵坐标Recall和对应的阈值的类sklearn.metrics.roc_curve。同时,我们还有帮助我们计算AUC面积的类sklearn.metrics.roc_auc_score.
sklearn.metrics.roc_curve
sklearn中的ROC曲线和AUC面积
from sklearn.metrics import roc_curve
#drop_intermediate=True默认是TRUE,舍弃一些ROC上不显示的阈值点,这里就计算了45个
FPR, recall, thresholds = roc_curve(y,clf_proba.decision_function(X), pos_label=1)
FPR.shape
recall.shape
thresholds #此时的threshold就不是一个概率值,而是距离值中的阈值了,所以它可以大于1,也可以为负
输出结果
sklearn.metrics.roc_auc_score (y_true, y_score, average=’macro’, sample_weight=None, max_fpr=None)
AUC面积的分数使用以上类来进行计算,输入的参数也比较简单,就是真实标签,和与roc_curve中一致的置信度分数或者概率值。
计算AUC
from sklearn.metrics import roc_auc_score as AUC
area = AUC(y,clf_proba.decision_function(X))
area
0.9696400000000001
画图
plt.figure()
plt.plot(FPR, recall, color='red',
label='ROC curve (area = %0.2f)' % area)
plt.plot([0, 1], [0, 1], color='black', linestyle='--')
plt.xlim([-0.05, 1.05])
plt.ylim([-0.05, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('Recall')
plt.title('Receiver operating characteristic example')
plt.legend(loc="lower right")
plt.show()
ROC曲线
2.2.3 利用ROC曲线找出最佳阈值
在ROC曲线中,随着recall的变大,FPR的大小越小越好。所以我们希望找到的最有点,其实是Recall和FPR差距最大的点。这个点,又叫做约登指数。
#找出max(recall - FPR)对应的索引
maxindex = (recall - FPR).tolist().index(max(recall - FPR))
#decision_function生成的置信度来说是-1.08,用概率返回的就是概率
thresholds[maxindex] #该索引对应的就是thresholds就是最佳阈值
-1.0860191749391461
看看这个点在图片上哪里
plt.figure()
plt.plot(FPR, recall, color='red',
label='ROC curve (area = %0.2f)' % area)
plt.plot([0, 1], [0, 1], color='black', linestyle='--')
#我们可以在图像上来看看这个点在哪里
plt.scatter(FPR[maxindex],recall[maxindex],c="black",s=30)
plt.xlim([-0.05, 1.05])
plt.ylim([-0.05, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('Recall')
plt.title('Receiver operating characteristic example')
plt.legend(loc="lower right")
plt.show()
image.png
最佳阈值就这样选取出来了,由于现在我们是使用decision_function来画ROC曲线,所以我们选择出来的最佳阈值其实是最佳距离。如果我们使用的是概率,我们选取的最佳阈值就会使一个概率值了。