目录
1.1 基本概念
支持向量机(Support vector machines, SVM)是一种二分类模型,它将实例的特征向量映射为空间中的一些点,SVM 的目的就是想要画出一条线,以 “最好地” 区分这两类点,以至如果以后有了新的点,这条线也能做出很好的分类。
1.2 关键词
线性可分:我们把实例的特征向量映射到n维空间中的样本点,如果以二维平面为例,利用一条直线可以将样本点分为两类,我们称这个样本线性可分。
决策分界线:将两类样本点分开的线。
超平面:把二维平面推广到n维空间,此时样本空间的划分就不是一条线了,决策分界线成为超平面。
支持向量:映射到n维空间距离超平面最近样本点的向量。
核函数:以二维平面为例,当我们将其推广到n维欧式空间时,需要利用函数将二维样本点映射到n维空间,这个函数称之为核函数(Kernel Function)。
线性核函数:kernel=‘linear’
多项式核函数:kernel=‘poly’
径向基核函数:kernel=‘rbf’
sigmod核函数:kernel=‘sigmod’
支持向量回归:Support Vector Regression, SVR
支持向量分类:Support Vector Classification,SVC
1.3 公式推导
链接:【机器学习】支持向量机 SVM(非常详细) - 知乎 (zhihu.com)
1.4 算法优点与缺点
优点:1. 可以利用内积核函数处理高维空间的非线性映射;
2. 对于小样本案例具有高效的解决方法,从训练样本到预报样本,不同于传统的从归纳到演绎;
3. 少数的支持向量决定了最后的结果,可以帮助我们抓住关键样本,剔除冗余样本,从而使得模型具有较好的鲁棒性:
1) 增、删非支持向量样本对模型没有影响;
2) 支持向量样本集具有一定的鲁棒性;
3) 有些成功的应用中,SVM 方法对核的选取不敏感;
缺点:1.处理海量数据时,SVM运算量巨大,效率低下;
2. 在处理某些非线性问题时,核函数难以寻找;
3. 经典的SVM一般用于解决二分类问题,对于多分类问题难以下手。我们面对这种多分类问题一般构造多个分类器或利用SVM决策树进行处理,主要为了克服经典SVM客观存在的缺点,结合其他算法的优势与经典SVM进行互补,提高模型分类精度。
1.5 案例分析
import numpy as np # linear algebra
import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)
import matplotlib.pyplot as plt # for data visualization
import seaborn as sns # for statistical data visualization
#忽略警告信息
import warnings
warnings.filterwarnings('ignore')
#导入数据
data = 'pulsar_stars.csv'
df = pd.read_csv(data)
df.columns = df.columns.str.strip() #删除列空格
col_names = df.columns
df.columns = ['IP Mean', 'IP Sd', 'IP Kurtosis', 'IP Skewness',
'DM-SNR Mean', 'DM-SNR Sd', 'DM-SNR Kurtosis',
'DM-SNR Skewness', 'target_class'] #简化列名称
df['target_class'].value_counts() #检索目标列不同数值的个数
df['target_class'].value_counts()/np.float(len(df)) #检索目标列数值占比
#分析数据 数量 平均值 方差 中位数...
round(df.describe(),2)
#提取目标函数以及特征向量
X = df.drop(['target_class'], axis=1) #删除target_class这列 axis=1时为删除列 默认删除行
y = df['target_class']
#将数据分割为单独的训练集和测试集
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 0)
#数据预处理 特征向量归一化
cols = X_train.columns #寄存列标签
print(cols)
#标准化数据 归一化数据
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
print("处理前")
print(X_train)
X_train = scaler.fit_transform(X_train)
print("处理后")
print(X_train)
X_test = scaler.transform(X_test)
#整理归一化后的训练集和测试集数据
X_train = pd.DataFrame(X_train, columns=[cols])
X_test = pd.DataFrame(X_test, columns=[cols])
X_train.describe() #分析数据 数量 平均值 方差 中位数...
#利用SVM训练数据
# 默认惩罚程度C=1.0
# 核函数使用径向基核函数(RBF)kernel=‘rbf
# RBF的Gamma参数控制单个训练点的影响距离
from sklearn.svm import SVC #利用SVC求解分类超平面
from sklearn.metrics import accuracy_score #准确性评价
# 实例化分类器
svc=SVC()
# 训练数据拟合模型
svc.fit(X_train,y_train)
# 对测试集预测
y_pred=svc.predict(X_test)
# 打印精度
print('Model accuracy score with default
hyperparameters: {0:0.4f}'.
format(accuracy_score(y_test, y_pred)))
1.5.1 模型调参
我们在前期分析数据时可以观察到,这个数据集存在较多的异常点,如图3所示:
在SVC中参数C为惩罚系数,用于调整损失函数,因为异常点较多,我们把C调高,运行结果如下:
尝试使用其他核函数进行模型拟合:
1.5.2 检查过拟合
现在,我们比较训练集和测试集的准确性,以检查过拟合。利用Y text和Y pre的Accuracy Score与Y Pre train和Y train的Accuracy Score,如图8所示:
在这里,我们以C=1000,kernel="linear"为例进行验证:
#过拟合验证
linear_svc=SVC(kernel='linear',C=1000)
linear_svc.fit(X_train, y_train)
y_pred=linear_svc.predict(X_test)
y_pred_train = linear_svc.predict(X_train)
print('Training set score: {:.4f}'.format(linear_svc.score(X_train, y_train)))
print('Test set score: {:.4f}'.format(linear_svc.score(X_test, y_test))
训练集准确度得分为0.9783,测试集准确度得分为0.9830。这两个值相近。所以,不存在过拟合的问题。
1.5.3 null accuracy
即使这样,我们还不能得出这个模型很好的结论,我们必须再观察一下null accuracy,null accurracy是测试集中最多那一类的占比。我们测试集的预测结果:
print(y_test.value_counts())
null_accuracy = (3306/(3306+274))
print('Null accuracy score: {0:0.4f}'. format(null_accuracy))
1.5.4 小结
经过以上几次参数调整的比较,我们得出采用RBF和C=100.0的参数可以得到较高精度,accuracy为0.9832。基于以上分析我们可以得出,我们的模型预测精度非常高,预测类标签方面做的很好。
但实际上我们的精度并不是真的高,我们的数据集是不平衡的,即两类数据量相差较大。在数据不平衡的情况下,准确度不足以量化模型的预测性能。
因此,我们必须探索在选择模型时提供更好指导的替代量。评估不平衡数据模型的一个很好的方法是混淆矩阵。
1.5.5 混淆矩阵
混淆矩阵也称误差矩阵,是表示精度评价的一种标准格式,用n行n列的矩阵形式来表示。
例如:我们利用训练好模型进行苹果的图像识别,建立混淆矩阵对模型进行评估如下:
混淆矩阵 | 样本是苹果 | 样本不是苹果 |
预测结果是苹果 | TP | FN |
预测结果不是苹果 | FP | TN |
精度:也称准确率,
查全率:也称召回率,
查准率:也称精准率,
F1度量:在一些应用中,对查准率和查全率的重视程度有所不同。F1度量能表达出对查准率/查全率的不同偏好,
我们利用混淆矩阵对我们训练好的模型进行评估:
#利用混淆矩阵验证模型
y_pre_test=linear_svc.predict(y_test)
from sklearn.metrics import confusion_matrix
cm = confusion_matrix(y_test, y_pre_test)
print('Confusion matrix\n\n', cm)
print('\nTrue Positives(TP) = ', cm[0,0])
print('\nTrue Negatives(TN) = ', cm[1,1])
print('\nFalse Positives(FP) = ', cm[0,1])
print('\nFalse Negatives(FN) = ', cm[1,0])
cm_matrix = pd.DataFrame(data=cm,
columns=['Actual Positive:1', 'Actual Negative:0'],
index=['Predict Positive:1', 'Predict Negative:0'])
sns.heatmap(cm_matrix, annot=True, fmt='d', cmap='YlGnBu')
1.5.6 分类报告
分类报告是评估模型的另一种方法,它可以将模型的精度、召回率、f1和支持度分数等表示出来。
下面我们将模型准确度(精度)、错误率、精准率(查准率)、召回率(查全率)、真阳率、
假阳率、特异度进行计算。
#分类报告
from sklearn.metrics import classification_report
print(classification_report(y_test, y_pre_test))
TP = cm[0,0]
TN = cm[1,1]
FP = cm[0,1]
FN = cm[1,0]
#准确率
classification_accuracy = (TP + TN) / float(TP + TN + FP + FN)
print('Classification accuracy : {0:0.4f}'.format(classification_accuracy))
#错误率
classification_error = (FP + FN) / float(TP + TN + FP + FN)
print('Classification error : {0:0.4f}'.format(classification_error))
#精准率(查准率)
precision = TP / float(TP + FP)
print('Precision : {0:0.4f}'.format(precision))
#召回率(查全率)
recall = TP / float(TP + FN)
print('Recall or Sensitivity : {0:0.4f}'.format(recall))
#真阳率
true_positive_rate = TP / float(TP + FN)
print('True Positive Rate : {0:0.4f}'.format(true_positive_rate))
#假阳率
false_positive_rate = FP / float(FP + TN)
print('False Positive Rate : {0:0.4f}'.format(false_positive_rate))
#特异度
specificity = TN / (TN + FP)
print('Specificity : {0:0.4f}'.format(specificity))
1.5.7 ROC-AUC
ROC是评估分类器效果的一个指标。在介绍ROC前,我们先对真正例率(True Positive Rate, TPR)和假正例率(Flase Positive Rate, FPR)进行理解。在混淆矩阵中,我们定义:
分类器对一组数据集进行分类,得到其预测值,然后对其进行混淆矩阵的建立,获取这组数据集的TPR和FPR。建立以FPR为横轴、TPR为纵轴的直角坐标系。这组数据集预测后的结果在坐标系上就表示为一个点(FPR,TPR)。
这个点还对应着一个变量,概率输出(threshold),即表示分类器认为某个样本具有多大的概率属于正样本。
我们在现实任务中利用有限个测试样例进行ROC图的绘制,这仅能获得有限个坐标对,当测试样例不断增多时图像趋向于光滑,如图13所示。
我们利用roc_curve()对ROC曲线进行绘制,对该函数输入两个变量:测试集真实值y_test和预测值y_pred_test,绘制出ROC曲线上的一个点。
#绘制ROC曲线
from sklearn.metrics import roc_curve
fpr, tpr, thresholds = roc_curve(y_test, y_pred_test)
plt.figure(figsize=(6,4))
plt.plot(fpr, tpr, linewidth=2)
plt.plot([0,1], [0,1], 'k--' )
plt.rcParams['font.size'] = 12
plt.title('ROC curve for Predicting a Pulsar Star classifier')
plt.xlabel('False Positive Rate (1 - Specificity)')
plt.ylabel('True Positive Rate (Sensitivity)')
plt.show()
AUC为ROC曲线与横轴围成的面积,通常情况下一个模型的AUC越大,则该模型的分类性能越理想。
#计算AUC
from sklearn.metrics import roc_auc_score
ROC_AUC = roc_auc_score(y_test, y_pre_test)
print('ROC AUC : {:.4f}'.format(ROC_AUC))
1.5.8 小结
我们模型的AUC接近于1。 因此,我们可以得出结论,我们的分类器在对pulsar_stars进行分类方面做得很好。
1.5.9 k折交叉验证
初始采样分割成K个子样本,一个单独的子样本被保留作为验证模型的数据,其他K-1个样本用来训练。交叉验证重复K次,每个子样本验证一次,平均K次的结果或者使用其它结合方式,最终得到一个单一估测。
#K折交叉验证
from sklearn.model_selection import KFold
from sklearn.model_selection import cross_val_score
kfold=KFold(n_splits=5, shuffle=True, random_state=0)
#利用线性核函数验证
linear_svc=SVC(kernel='linear')
linear_scores = cross_val_score(linear_svc, X, y, cv=kfold)
print('Stratified Cross-validation scores with rbf kernel:\n\n{}'.format(linear_scores))
print('Average stratified cross-validation score with rbf kernel:{:.4f}'.format(linear_scores.mean()))
rbf_svc=SVC(kernel='rbf')
rbf_scores = cross_val_score(rbf_svc, X, y, cv=kfold)
print('Stratified Cross-validation scores with rbf kernel:\n\n{}'.format(rbf_scores))
print('Average stratified cross-validation score with rbf kernel:{:.4f}'.format(rbf_scores.mean()))
1.5.10 基于GridSearch CV的超参数优化
Grid Search:一种调参手段,在所有候选的参数选择中,通过循环遍历,尝试每一种可能性,表现最好的参数就是最终的结果。其原理就像是在数组里找最大值。
#利用GridSearch CV进行超参数优化
from sklearn.model_selection import GridSearchCV
# import SVC classifier
from sklearn.svm import SVC
# instantiate classifier with default hyperparameters with kernel=rbf, C=1.0 and gamma=auto
svc=SVC()
# declare parameters for hyperparameter tuning
parameters = [ {'C':[1, 10, 100, 1000], 'kernel':['linear']},
{'C':[1, 10, 100, 1000], 'kernel':['rbf'], 'gamma':[0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]},
{'C':[1, 10, 100, 1000], 'kernel':['poly'], 'degree': [2,3,4] ,'gamma':[0.01,0.02,0.03,0.04,0.05]}
]
grid_search = GridSearchCV(estimator = svc,
param_grid = parameters,
scoring = 'accuracy',
cv = 5,
verbose=0)
grid_search.fit(X_train, y_train)
# best score achieved during the GridSearchCV
print('GridSearch CV best score : {:.4f}\n\n'.format(grid_search.best_score_))
# print parameters that give the best results
print('Parameters that give the best results :','\n\n', (grid_search.best_params_))
# print estimator that was chosen by the GridSearch
print('\n\nEstimator that was chosen by the search :','\n\n', (grid_search.best_estimator_))
# calculate GridSearch CV score on test set
print('GridSearch CV score on test set: {0:0.4f}'.format(grid_search.score(X_test, y_test)))
1.5.11 小结
我们的原始模型测试精度为0.9832,而GridSearch CV在测试集上的得分为0.9835。 因此,GridSearch CV有助于模型参数优化。
1.6 附录(数据集及源代码)
1.7 参考资料
01 (7条消息) 机器学习算法(一)SVM_yaoyz105-CSDN博客_svm
02 SVM核方法--这是我见过最好的一个视频。。核技巧核函数_哔哩哔哩_bilibili
03 【机器学习】支持向量机 SVM(非常详细) - 知乎 (zhihu.com)
04 (8条消息) 支持向量机(SVM)的优缺点_浆果吖的博客-CSDN博客_svm的优缺点