目录
支持向量机算法背景介绍
1995年Cortes和Vapnik首先提出了支持向量机(Support Vector Machine),由于其能够适应小样本的分类,分类速度快等特点,性能不差于人工神经网络,所以在这之后,人们将SVM应用于各个领域。
大量使用SVM模型的论文不断涌现,包括国内和国外支持向量机建立在坚实的统计学理论基础上,是在所有知名的数据挖掘算法中最健壮、最准确的方法之一,具有很好的学习能力和泛化能力。
什么是线性可分?
什么又是超平面?
超平面H是从n维空间到n-1维空间的一个映射子空间,它有一个n维向量和一个实数定义。因为是子空间,所以超平面一定过原点。
怎么正确理解超平面?
超平面是个纯数学概念,不是物理概念。它是直线中的点、平面中的直线、空间中的平面的推广,只有当维度大于3,才能称为“超”平面。
超平面的本质是自由度比空间维度小1,也即最后一个维度可以因其他维度确定而确定。
超平面的两个性质:
1)方程是线性的: 是空间点的各分量的线性组合
2)方程数量为1
说到这里,可能还是比较的懵逼,到底什么是超平面,超平面又该如何选择呢?我们可以看看下面的这个例子!
约束条件合并
对于上图而言:
1)蓝色样本点(y=+1)必定在直线W1TX+b1=+1上方,也即对所有蓝色样本点数据必定满足: W1TX+b1≥+1
2)黑色样本点(y=-1)必定在直线W1TX+b1=-1下面,也即对所有黑色样本点数据必定满足: W1TX+b1≤-1
我们把以上两个不等式合成一个,即:
经过了大量的铺垫工作之后,现在我们对支持向量机进行一个定义
支持向量机(support vector machines)是一种二分类模型,它的目的是寻找一个超平面来对样本进行分割,分割的原则是间隔最大化,最终转化为一个凸二次规划问题来求解:
支持向量机的三种情况
线性可分
线性可分指样本数据集可以找到一个线性函数或者超平面一分为二,这样的支持向量机又叫硬间隔(Hard Margin)支持向量机。求解过程如下:
近线性可分
近线性可分求解过程
线性不可分
如果遇到无穷维的情况,就更加无从计算了。这个时候我们就必须引入Kernel核函数
核函数Kernel是什么?
核函数的本质
1、实际中,我们会经常遇到线性不可分的样例,此时,我们的常用做法是把样例特征映射到高维空间中去(如之前那幅图线性不可分图所示,映射到高维空间后,相关特征便被分开了,也就达到了分类的目的);
2、但进一步,如果凡是遇到线性不可分的样例,一律映射到高维空间,那么这个维度大小是会高到可怕的(如上文中19维乃至无穷维的例子)。那咋办呢?
3、此时,核函数就隆重登场了,核函数的价值在于它虽然也是将特征进行从低维到高维的转换,但核函数绝就绝在它事先在低维上进行计算,而将实质上的分类效果表现在了高维上,也就如上文所说的避免了直接在高维空间中的复杂计算。
说到这里,相信差不多应该可以理解了吧,下面再去举一个例子
讲了这么多,其实就是在介绍支持向量机的本质,如果你认真的看完之后,你会发现,在支持向量机里面,最重要的两个参数就是C和核函数
支持向量机的优势在于:
( 1) 在高维空间中非常高效。
( 2) 即使在数据维度比样本数量大的情况下仍然有效。
( 3) 在决策函数(称为支持向量)中使用训练集的子集,因此它也是高效利用内存的。
支持向量机的缺点包括:
( 1) 如果特征数量比样本数量大得多, 在选择核函数核函数时要避免过拟合。
( 2) 而且正则化项是非常重要的。
( 3) 支持向量机不直接提供概率估计, 这些都是使用昂贵的五次交叉验算计算的。
注意,这里很重要的,根据你选择的核函数,最终对数据是否需要预处理,下面我们就看看一个实例:
代码实例
导入第三方库
#导入所需要的包
from sklearn.metrics import precision_score
from sklearn.model_selection import train_test_split
from sklearn.svm import SVC
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
from sklearn.preprocessing import LabelEncoder
from sklearn.metrics import classification_report
from sklearn.model_selection import GridSearchCV #网格搜索
import matplotlib.pyplot as plt#可视化
import seaborn as sns#绘图包
from sklearn.preprocessing import StandardScaler,MinMaxScaler,MaxAbsScaler
初次加载模型
# 加载模型
model = SVC()
# 训练模型
model.fit(X_train,y_train)
# 预测值
y_pred = model.predict(X_test)
'''
评估指标
'''
# 求出预测和真实一样的数目
true = np.sum(y_pred == y_test )
print('预测对的结果数目为:', true)
print('预测错的的结果数目为:', y_test.shape[0]-true)
# 评估指标
from sklearn.metrics import accuracy_score,precision_score,recall_score,f1_score,cohen_kappa_score
print('预测数据的准确率为: {:.4}%'.format(accuracy_score(y_test,y_pred)*100))
print('预测数据的精确率为:{:.4}%'.format(
precision_score(y_test,y_pred)*100))
print('预测数据的召回率为:{:.4}%'.format(
recall_score(y_test,y_pred)*100))
# print("训练数据的F1值为:", f1score_train)
print('预测数据的F1值为:',
f1_score(y_test,y_pred))
print('预测数据的Cohen’s Kappa系数为:',
cohen_kappa_score(y_test,y_pred))
# 打印分类报告
print('预测数据的分类报告为:','\n',
classification_report(y_test,y_pred))
这里默认使用的是高斯核,但是我们的数据集范围并没有规约到[0,1]之间,由于数据集最先最好了离散化,所以造成的影响也不是很大,因为这里是初次加载
标准化
# 没有作用
# sc = StandardScaler()
# 标准化【0,1】
# 效果不行
sc=MinMaxScaler()
# sc=MaxAbsScaler()
X_train1 = sc.fit_transform(X_train)
X_test1 = sc.transform(X_test)
X_test1
使用标准化后的数据集进行预测
# 加载模型
model = SVC()
# 训练模型
model.fit(X_train1,y_train)
# 预测值
y_pred = model.predict(X_test1)
'''
评估指标
'''
# 求出预测和真实一样的数目
true = np.sum(y_pred == y_test )
print('预测对的结果数目为:', true)
print('预测错的的结果数目为:', y_test.shape[0]-true)
# 评估指标
from sklearn.metrics import accuracy_score,precision_score,recall_score,f1_score,cohen_kappa_score
print('预测数据的准确率为: {:.4}%'.format(accuracy_score(y_test,y_pred)*100))
print('预测数据的精确率为:{:.4}%'.format(
precision_score(y_test,y_pred)*100))
print('预测数据的召回率为:{:.4}%'.format(
recall_score(y_test,y_pred)*100))
# print("训练数据的F1值为:", f1score_train)
print('预测数据的F1值为:',
f1_score(y_test,y_pred))
print('预测数据的Cohen’s Kappa系数为:',
cohen_kappa_score(y_test,y_pred))
# 打印分类报告
print('预测数据的分类报告为:','\n',
classification_report(y_test,y_pred))
并不乐观,继续探索
模型调参
from datetime import time
import datetime
# ploy在该例中跑不出来
Kernel = ["linear", "rbf", "sigmoid","poly"]
for kernel in Kernel:
time0 = time()
clf = SVC(kernel=kernel,
gamma="auto",
cache_size=5000, # 允许使用的内存,单位为MB,默认是200M
).fit(X_train, y_train)
print("The accuracy under kernel %s is %f" % (kernel, clf.score(X_test, y_test)))
# print(datetime.datetime.fromtimestamp(time() - time0).strftime("%M:%S:%f"))
优先考虑多项式和高斯核
gamma调参
RBF调参
# 使用rbf,必须要对其进行数据缩放
# 画学习曲线
score = []
gamma_range = np.logspace(-10, 1, 50) # 返回在对数刻度上均匀间隔的数字
for i in gamma_range:
clf = SVC(kernel="rbf", gamma=i, cache_size=5000).fit(X_train, y_train)
score.append(clf.score(X_test, y_test))
print(max(score), gamma_range[score.index(max(score))])
best_gamma_rbf=gamma_range[score.index(max(score))]
#设置标题
plt. title(f' SVC (rbf) {max(score)}')
#设置x轴标签
plt. xlabel(' gamma')
#设置y轴标签
plt. ylabel(' Score')
#添加图例
# plt. legend()
plt.plot(gamma_range, score)
plt.show()
poly调参
# 画学习曲线,数据不需要处理
score = []
gamma_range = np.logspace(-10, 1, 50) # 返回在对数刻度上均匀间隔的数字
for i in gamma_range:
clf = SVC(kernel="poly", gamma=i, cache_size=5000).fit(X_train, y_train)
score.append(clf.score(X_test, y_test))
print(max(score), gamma_range[score.index(max(score))])
best_gamma_poly=gamma_range[score.index(max(score))]
#设置标题
plt. title(f' SVC (poly) {max(score)}')
#设置x轴标签
plt. xlabel(' gamma')
#设置y轴标签
plt. ylabel(' Score')
#添加图例
# plt. legend()
plt.plot(gamma_range, score)
plt.show()
C值调参
# 调线性核函数
score = []
C_range = np.linspace(0.01, 30, 50)
for i in C_range:
clf = SVC(kernel="linear",C=i,cache_size=5000).fit(X_train,y_train)
score.append(clf.score(X_test, y_test))
print(max(score), C_range[score.index(max(score))])
best_C_linear=C_range[score.index(max(score))]
#设置标题
plt. title(f' SVC (linspace) {max(score)}')
#设置x轴标签
plt. xlabel(' C')
#设置y轴标签
plt. ylabel(' Score')
#添加图例
# plt. legend()
plt.plot(C_range, score)
plt.show()
# 换rbf,并且这里对数据进行了标准化,缩放到0和1之间的
score = []
C_range = np.linspace(0.01, 30, 50)
for i in C_range:
clf = SVC(kernel="rbf", C=i, gamma=0.15998587196060574, cache_size=5000).fit(X_train, y_train)
score.append(clf.score(X_test, y_test))
print(max(score), C_range[score.index(max(score))])
best_C_rbf=C_range[score.index(max(score))]
#设置标题
plt. title(f' SVC (rbf) {max(score)}')
#设置x轴标签
plt. xlabel(' C')
#设置y轴标签
plt. ylabel(' Score')
#添加图例
# plt. legend()
plt.plot(C_range, score)
plt.show()
# 换ploy
score = []
C_range = np.linspace(0.01, 30, 50)
for i in C_range:
clf = SVC(kernel="poly", C=i, gamma=0.0339322177189533, cache_size=5000).fit(X_train, y_train)
score.append(clf.score(X_test, y_test))
print(max(score), C_range[score.index(max(score))])
best_C_poly=C_range[score.index(max(score))]
#设置标题
plt. title(f' SVC (poly) {max(score)}')
#设置x轴标签
plt. xlabel(' C')
#设置y轴标签
plt. ylabel(' Score')
#添加图例
# plt. legend()
plt.plot(C_range, score)
plt.show()
使用Polynomial kernel进行预测
# 加载模型
model_1 = SVC(C=best_C_poly,gamma=best_gamma_poly,kernel='poly',cache_size=5000,degree=3,probability=True)
# 训练模型
model_1.fit(X_train,y_train)
# 预测值
y_pred = model_1.predict(X_test)
'''
评估指标
'''
# 求出预测和真实一样的数目
true = np.sum(y_pred == y_test )
print('预测对的结果数目为:', true)
print('预测错的的结果数目为:', y_test.shape[0]-true)
# 评估指标
from sklearn.metrics import accuracy_score,precision_score,recall_score,f1_score,cohen_kappa_score
print('预测数据的准确率为: {:.4}%'.format(accuracy_score(y_test,y_pred)*100))
print('预测数据的精确率为:{:.4}%'.format(
precision_score(y_test,y_pred)*100))
print('预测数据的召回率为:{:.4}%'.format(
recall_score(y_test,y_pred)*100))
# print("训练数据的F1值为:", f1score_train)
print('预测数据的F1值为:',
f1_score(y_test,y_pred))
print('预测数据的Cohen’s Kappa系数为:',
cohen_kappa_score(y_test,y_pred))
# 打印分类报告
print('预测数据的分类报告为:','\n',
classification_report(y_test,y_pred))
from sklearn.metrics import precision_recall_curve
from sklearn import metrics
# 预测正例的概率
y_pred_prob=model_1.predict_proba(X_test)[:,1]
# y_pred_prob ,返回两列,第一列代表类别0,第二列代表类别1的概率
#https://blog.csdn.net/dream6104/article/details/89218239
fpr, tpr, thresholds = metrics.roc_curve(y_test,y_pred_prob, pos_label=2)
#pos_label,代表真阳性标签,就是说是分类里面的好的标签,这个要看你的特征目标标签是0,1,还是1,2
roc_auc = metrics.auc(fpr, tpr) #auc为Roc曲线下的面积
# print(roc_auc)
plt.figure(figsize=(8,6))
plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--')
plt.plot(fpr, tpr, 'r',label='AUC = %0.2f'% roc_auc)
plt.legend(loc='lower right')
# plt.plot([0, 1], [0, 1], 'r--')
plt.xlim([0, 1.1])
plt.ylim([0, 1.1])
plt.xlabel('False Positive Rate') #横坐标是fpr
plt.ylabel('True Positive Rate') #纵坐标是tpr
plt.title('Receiver operating characteristic example')
plt.show()
评估指标
使用RBF kernel进行预测
# 加载模型
model_2 = SVC(C=best_C_rbf,kernel='rbf',cache_size=5000,probability=True)
# 训练模型
model_2.fit(X_train1,y_train)
# 预测值
y_pred = model_2.predict(X_test1)
'''
评估指标
'''
# 求出预测和真实一样的数目
true = np.sum(y_pred == y_test )
print('预测对的结果数目为:', true)
print('预测错的的结果数目为:', y_test.shape[0]-true)
# 评估指标
from sklearn.metrics import accuracy_score,precision_score,recall_score,f1_score,cohen_kappa_score
print('预测数据的准确率为: {:.4}%'.format(accuracy_score(y_test,y_pred)*100))
print('预测数据的精确率为:{:.4}%'.format(
precision_score(y_test,y_pred)*100))
print('预测数据的召回率为:{:.4}%'.format(
recall_score(y_test,y_pred)*100))
# print("训练数据的F1值为:", f1score_train)
print('预测数据的F1值为:',
f1_score(y_test,y_pred))
print('预测数据的Cohen’s Kappa系数为:',
cohen_kappa_score(y_test,y_pred))
# 打印分类报告
print('预测数据的分类报告为:','\n',
classification_report(y_test,y_pred))
from sklearn.metrics import precision_recall_curve
from sklearn import metrics
# 预测正例的概率
y_pred_prob=model_2.predict_proba(X_test1)[:,1]
# y_pred_prob ,返回两列,第一列代表类别0,第二列代表类别1的概率
#https://blog.csdn.net/dream6104/article/details/89218239
fpr, tpr, thresholds = metrics.roc_curve(y_test,y_pred_prob, pos_label=2)
#pos_label,代表真阳性标签,就是说是分类里面的好的标签,这个要看你的特征目标标签是0,1,还是1,2
roc_auc = metrics.auc(fpr, tpr) #auc为Roc曲线下的面积
# print(roc_auc)
plt.figure(figsize=(8,6))
plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--')
plt.plot(fpr, tpr, 'r',label='AUC = %0.2f'% roc_auc)
plt.legend(loc='lower right')
# plt.plot([0, 1], [0, 1], 'r--')
plt.xlim([0, 1.1])
plt.ylim([0, 1.1])
plt.xlabel('False Positive Rate') #横坐标是fpr
plt.ylabel('True Positive Rate') #纵坐标是tpr
plt.title('Receiver operating characteristic example')
plt.show()
总结
本次使用支持向量机进行模型分类预测,并没有对其进行特征筛选,效果也是不错的,因为支持向量机的本质也会根据根据特征进行划分,这里经过测试之后也确实如此。
核支持向量机是非常强大的模型,在各种数据集上的表现都很好。 SVM 允许决策边界很
复杂,即使数据只有几个特征。它在低维数据和高维数据(即很少特征和很多特征)上的
表现都很好,但对样本个数的缩放表现不好。在有多达 10 000 个样本的数据上运行 SVM
可能表现良好,但如果数据量达到 100 000 甚至更大,在运行时间和内存使用方面可能会
面临挑战。
SVM 的另一个缺点是,预处理数据和调参都需要非常小心。这也是为什么如今很多应用
中用的都是基于树的模型,比如随机森林或梯度提升(需要很少的预处理,甚至不需要预
处理)。此外, SVM 模型很难检查,可能很难理解为什么会这么预测,而且也难以将模型
向非专家进行解释。
不过 SVM 仍然是值得尝试的,特别是所有特征的测量单位相似(比如都是像素密度)而
且范围也差不多时。
核 SVM 的重要参数是正则化参数 C、核的选择以及与核相关的参数。虽然我们主要讲的是
RBF 核,但 scikit-learn 中还有其他选择。 RBF 核只有一个参数 gamma,它是高斯核宽度
的倒数。
gamma 和 C 控制的都是模型复杂度,较大的值都对应更为复杂的模型。因此,这
两个参数的设定通常是强烈相关的,应该同时调节。