sklearn支持向量机SVM
原理概述
说实话以前用支持向量机都是直接套进去的,不过现在看了看菜菜提供数学原理发现其实挺有意思(是超有意思!!)。此处就不详述了,这原理到处都是。反正这SVM和决策树一样,有支持向量分类(SVC)和支持向量回归(SVR).
代码
下面是一个SVC的案例
导入库
1 | from sklearn.datasets import make_blobs |
生成数据集
1 | X,y = make_blobs(n_samples=50, centers=2, random_state=0,cluster_std=0.6) |
核心代码
模型本身并不难,就是要画出相应的图
1 | clf = SVC(kernel = "linear").fit(X,y) |
上述例子预测又对X自己预测了一变。按照核心代码依旧延续sklearn的风格,十分简单。
- 实例化
- fit()
- predict()。
可视化可能优点麻烦,需要用到下面这个函数。这个函数只需输入clf
即可。
1 | def plot_svc_decision_function(model,ax=None): |
函数大概思路就是首先生成一个网格,然后计算网格中各个点到决策边界的距离,最后绘制等高线(算出的距离相等的一条线)。
则可以写作
1 | clf = SVC(kernel = "linear").fit(X,y) |
其中灰色实线就是决策边界,虚线之间的距离就是边际。而SVM就是要找到边际最大的一条决策边界,而上面那三个穿虚线而过的点就是支持向量(最下面那个红色不算),也就是离决策边界最近的3个点。
线性不可分的情况
这就是整个SVM我觉得最关键也最有意思的地方。从上面的图我们能够知道SVM实际上是找到一个超平面将各点分开。如果能找到自然就是线性可分的,那如果找不到呢?
超平面就是比当前空间维度低一个维度的分界,对于二维平面来说是一条线,对于三维空间是一个面。
对于这么一个案例
1 | from sklearn.datasets import make_circles |
我们可以看出,不可能存在一条直线,能将红色和蓝色分开,所以它是线性不可分的,怎么办呢?答案是升维!!!
对于原来每一个点,我们使用一个映射函数,使得从原来的二维点变为原来的三维点。对于原来一个点有(x0,y0),
从(x0,y0)变为(x1,y1,z1),其中z1有
1 | x1 = x0; y1 = y0; z1 = e^{-(x^2+y^2)} |
先别想这个函数是怎么来的,让我们先看看映射后的结果怎么样
这个结果非常的amazing啊,红色的点浮起来了,现在只需要用一个平面就能把红色和蓝色隔开,也就是说,升维之后线性可分了!(当时就把我看湿了
下面是上面这个案例的完整代码
1 | from sklearn.svm import SVC |
核函数
所以说上面这个映射函数到底是怎么来的呢,什么是核函数,又和核函数又什么关系呢?
首先SVM最终的决策函数是
- f(x_test)是最终的分类,定义为{-1,1}
- 其中sign是符号函数,大于0取1,小于0取-1。
- y是标签,二分类有两个标签,取{-1,1}
- x_i是支持向量
- x_test是测试向量
- b是一个常数
很显然我们要计算x_i与x_test之间的内积,当我们升维之后,我们要算的的内积是映射(升维)后的内积。
那么我们定义核函数:
现在我们就只需要直接把两个向量带入核函数,而不用先映射成高维再算内积。这其实省去了很多麻烦,因为计算映射其实挺复杂的。
注意,正是因为再SVM中我们只用到了高维函数的内积,所以只需要计算核函数即可,有了核函数,我们便能再低维计算高维的内积,(这是一次低维生物对高维发起的伟大挑战)显然我们有,只要映射函数不同,核函数就不同。
下面是sklearn中的几个核函数,个人建议用”rbf”,至于每一个核函数对应的映射函数,自己百度吧。
并且有
- 线性核,尤其是多项式核函数在高次项时计算非常缓慢
- rbf和多项式核函数都不擅长处理量纲不统一的数据集
所以需要针对其进行标准化1
2from sklearn.preprocessing import StandardScaler
X = StandardScaler().fit_transform(X)
重要参数C(软间隔和硬间隔)
c:
浮点数,默认1,必须大于等于0,可不填
松弛系数的惩罚项系数。如果C值设定比较大,那SVC可能会选择边际较小的,能够更好地分类所有训练点的决策边界,不过模型的训练时间也会更长。如果C的设定值较小,那SVC会尽量最大化边界,决策功能会更简单,但代价是训练的准确度。换句话说,C在SVM中的影响就像正则化参数对逻辑回归的影响.
混淆矩阵
1是少数类,0是多数类
- 准确率:Accuracy,所有预测正确的样本除以总样本,(11+00)/(11+10+01+00)
- 精确率:Precision,预测的少数类中,真正的少数类的占比。11/(11+01)
- 召回率 :Recall,又被称为敏感度(sensitivity),真正率,查全率,表示所有真实为1的样本中,被我们预测正确的样本所占的比例,11/(11+10).
- 假负率:False Negative Rate,1-Recall。没召回的占少数类的占比。10/(11+10)
- 特异度:Specificity,表示所有真实为0的样本中,被正确预测为0的样本所占的比例,00/(01+00)
- 假正率:False Positive Rate,1 - specificity就是一个模型将多数类判断错误的能力,01/(01+00)
- ROC曲线,横轴为假正率(FPR),纵轴为召回率(Recall),当曲线越往左上角偏说明效果越好,。
- AUC面积,它代表了ROC曲线下方的面积,这个面积越大,代表ROC曲线越接近左上角,模型就越好。
小案例
- 首先生成数据集并带入模型训练
1
2
3
4
5
6
7
8
9
10
11from sklearn.svm import SVC
import matplotlib.pyplot as plt
import numpy as np
from sklearn.datasets import make_blobs
class_1 = 500 #类别1有500个样本
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],centers=centers,cluster_std=clusters_std,random_state=0, shuffle=False)
plt.scatter(X[:, 0], X[:, 1], c=y, cmap="rainbow",s=10)
clf_proba = SVC(kernel="linear",C=1.0,probability=True).fit(X,y)
- 其次把个点所预测得到的概率放入DataFrame里
1
2
3
4
5prob = clf_proba.predict_proba(X)
#将样本和概率放到一个DataFrame中
import pandas as pd
prob = pd.DataFrame(prob)
prob.columns = ["0","1"] - 找出最佳阈值并画出ROC曲线并得到相应的FPR和Recall,以及AUC
1 | from sklearn.metrics import roc_curve |
- 最后进行预测,得出结果
1
2
3
4
5
6
7
8for i in range(prob.shape[0]):
if prob.loc[i,"1"] > thresholds[maxindex]:
prob.loc[i,"pred"] = 1
else:
prob.loc[i,"pred"] = 0
prob["y_true"] = y
prob = prob.sort_values(by="1",ascending=False)
prob
- 得到混淆矩阵来评估
1
2
3from sklearn.metrics import confusion_matrix as CM, precision_score as P, recall_score as R
cm = CM(prob.loc[:,"y_true"],prob.loc[:,"pred"],labels=[1,0])
cm
PS:这个案例用的是核函数是线性核,但又试了一遍还是‘rbf’比较好,
并且其实不一定需要最佳阈值,要结合实际来看。
例如,三星手机发生爆炸,三星想要召回率能达到100%,即宁可把没有问题的手机召回,也不能放过任何一个有问题的手机,阈值也要相应调整。
多分类
多分类其实也很简单,应该是sklearn的多分类很简单,数学原理十分可怕。区别就是输入的Y多了几个分类而已。
1 | clf = SVC(decision_function_shape='ovo') clf.fit(X, Y) |
上面用的是ovo(one vs one),也就是每一个类两两组合来构建,也可以选择’ovr’速度更快,效果不怎么样。
补充
参数class_weight
如果你想追求最高的召回率,宁可错杀不可放过,那么class_weight = "balanced"
1 | clf = SVC(kernel = kernel ,gamma="auto",degree = 1,cache_size = 5000,class_weight = "balanced").fit(Xtrain, Ytrain) |
如果还想再高,那么class_weight = {1:10}
- Post title:python支持向量机SVM (sklearn)
- Post author:newsun-boki
- Create time:2021-11-01 23:04:54
- Post link:https://github.com/newsun-boki2021/11/01/python-svm/
- Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.