1. SVM算法
支持向量机(Support Vector Machine, SVM)是一类按监督学习(supervised learning)方式对数据进行二元分类的广义线性分类器(generalized linear classifier),其决策边界是对学习样本求解的最大边距超平面(maximum-margin hyperplane)。
SVM使用铰链损失函数(hinge loss)计算经验风险(empirical risk)并在求解系统中加入了正则化项以优化结构风险(structural risk),是一个具有稀疏性和稳健性的分类器 。SVM可以通过核方法(kernel method)进行非线性分类,是常见的核学习(kernel learning)方法之一 。
SVM被提出于1964年,在二十世纪90年代后得到快速发展并衍生出一系列改进和扩展算法,在人像识别、文本分类等模式识别(pattern recognition)问题中有得到应用。
2. 实验数据
直接邮寄给公司的潜在客户-给许多人“垃圾邮件”,可能是推销产品或服务的非常有效的方式。 但是,众所周知,对于大多数接收该垃圾邮件的人来说,这些垃圾邮件确实不感兴趣。 大部分废物最终被扔掉了,不仅浪费了公司花在它上面的钱,而且还填满了垃圾填埋场或需要回收利用。
如果公司对潜在客户是谁有更好的了解,他们会更准确地知道将其发送给谁,因此可以减少一些浪费和费用。 因此,在1999年成功举办CoIL竞赛之后,CoIL在2000年面临新的竞赛挑战:您能预测谁会对购买旅行车保险单感兴趣并给出解释吗?
数据集用来训练和检验预测模型,建立了5822个客户的记录。每个记录由86个属性组成,包含社会人口数据和产品的所有关系。社会人口数据是有派生邮政编码派生而来,生活在相同邮政编码地区的客户具有相同的社会人口属性。第86个属性:“大篷车:家庭移动政策”,为目标变量。
预测的数据集为预测最后一个属性值。预测数据共有4003条。预测数据为85个属性。实验就是对后一个属性进行预测为二分类问题。即对第86个属性进行预测。可以在分类前对数据进行清洗。
具体内容可以见网站:http://liacs.leidenuniv.nl/~puttenpwhvander/library/cc2000/
3. 实验展示
在这一部分,我们将逐步探索改进SVM
分类器的性能。我们首先不对数据和SVM
分类器做任何设置,快速完成我们的目的——对目标变量进行预测,而后再仔细分析问题所在,并针对问题进行定向优化,以此来提升我们模型的性能。
3.1 实验环境
本次实验我们将用到一下几个库(函数):
import numpy as np
import pandas as pd
from sklearn import preprocessing
from sklearn.svm import SVC
from sklearn.metrics import precision_score, recall_score, roc_auc_score, average_precision_score, f1_score
3.2 读入数据
我们首先读入整个数据集,分为训练集和验证集两个部分。其中,每个部分包含数据属性和标签两个子部分:
trainingSet = np.loadtxt('Tic2000/ticdata2000.txt', delimiter='\t')
trainingData, trainingLabel = trainSet[:, :-1], trainSet[:, -1]
validationData = np.loadtxt('Tic2000/ticeval2000.txt', delimiter='\t')
validationLabel = np.loadtxt('Tic2000/tictgts2000.txt')
3.3 建立模型
我们在数据集上直接建立默认参数的SVM
模型:
model = SVC(probability=True)
注意:这里我们将
SVM
模型的probability
参数设置为True
。这样做是为了在模型预测时可以输出样本的概率。
我们将模型在训练数据上进行拟合:
model.fit(trainingData, trainingLabel)
3.4 模型的预测与评估
这里,我们分别在训练集和验证集上,对模型评估结果进行打分。在训练集上评估模型的原因是评估模型的拟合效果;在验证集上评估模型的原因是评估模型的泛化能力。
我们首先调用predict_proba(), predict()
两个函数分别返回待预测数据的打分值。
注意:
predict_proba()
函数返回两类的概率,这里我们只取预测为正样本的概率。
traingingProbablity = model.predict_proba(trainingLabel)[:, 1]
traingPredictLabel = model.predict(trainingLabel)
validationProbablity = model.predict_proba(validationData)[:, 1]
validationPredictLabel = model.predict(validationData)
之后,我们采用精准率(precision)
,召回率(recall)
,受试者操作特征曲线下的面积(the area under the receiver operating characteristic curve)
,平均精度(average precision)
(也就是PR曲线下方的面积AUPR
)进行模型评估:
trainingPrecision = precision_score(trainingLabel, traingPredictLabel)
trainingRecall = recall_score(trainingLabel, traingPredictLabel)
traingAuc = roc_auc_score(trainingLabel, traingingProbablity)
traingAupr = average_precision_score(trainingLabel, traingingProbablity)
traingF1 = f1_score(trainingLabel, traingPredictLabel)
print('trainging Precision:', trainingPrecision)
print('traing Recall:', trainingRecall)
print('trainging auc:', traingAuc)
print('traing aupr:', traingAupr)
print('traing f1:', traingF1)
validationPrecision = precision_score(validationLabel, validationPredictLabel)
validationRecall = recall_score(validationLabel, validationPredictLabel)
validationAuc = roc_auc_score(validationLabel, validationProbablity)
validationAupr = average_precision_score(validationLabel, validationProbablity)
validationF1 = f1_score(validationLabel, validationPredictLabel)
print('validation Precision:', validationPrecision)
print('validation Recall:', validationRecall)
print('validation auc:', validationAuc)
print('validation aupr:', validationAupr)
print('validation f1:', validationF1)
得到实验打分结果:
同时编译器给出了以下警告:
我们仔细分析预测的标签值,发现所有的样本都被分类为了负样本:
造成这样的原因是因为数据的类别不均衡:
下面,我们尝试给出解决方案。
3.5 均衡采样
一个解决样本不均衡问题的重要方法就是采样,采样包括上采样和下采样。这里我们主要使用上采样技术。我们调用imblearn
模块中的SMOTE()
函数进行采样:
from imblearn.over_sampling import SMOTE
smo = SMOTE(random_state=0)
trainingDataSMOTE, trainingLabelSMOTE = smo.fit_resample(trainingData, trainingLabel)
print(Counter(trainingLabelSMOTE))
采样结果为:
我们保持之前的默认SVM
分类器不变,再次对其进行训练评估,得到结果:
这次,我们看到模型在训练集上的拟合效果相较于采样之前有了一个较大的提升。不过模型在验证集上的效果泛化性能依然不理想。仔细观察我们发现:验证集上主要影响模型性能的指标是精度。这里模型是明显过拟合了,不过这个问题我们稍后再进行处理。下面,将给出解决样本不均衡问题的另一个方法。
3.6 设置类别权重
SVM
分类器中,可以设置类别权重,通过给予出现次数类别较少的样本更高的权重,使得模型可以更加“重视”类别次数较少的样本:
我们示例化SVM
分类器如下:
model = SVC(probability=True, class_weight='balanced')
我们再次运行实验,得到实验结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kzf1ooii-1621690143713)(C:\Users\16011\AppData\Roaming\Typora\typora-user-images\image-20210517214820853.png)]
我们可以看到,使用类别权重方法,也能达到与过采样相似的效果。为了简便期间,我们以下的改进,都建立在设置离别权重的基础之上。
3.7 归一化处理
我们观察特征向量每一个维度的值:
array([[33., 1., 3., ..., 0., 0., 0.],
[37., 1., 2., ..., 0., 0., 0.],
[37., 1., 2., ..., 0., 0., 0.],
...,
[33., 1., 3., ..., 0., 0., 0.],
[34., 1., 3., ..., 0., 0., 0.],
[33., 1., 3., ..., 0., 0., 0.]])
可以发现,每个维度值的大小很不均匀。这样会影响模型的训练效果,我们下面对其进行归一化处理,这里我们以最小最大归一化为例,其代码为:
trainingSet = np.loadtxt('Tic2000/ticdata2000.txt', delimiter='\t')
trainingData, trainingLabel = trainingSet[:, :-1], trainingSet[:, -1]
validationData = np.loadtxt('Tic2000/ticeval2000.txt', delimiter='\t')
validationLabel = np.loadtxt('Tic2000/tictgts2000.txt')
minMaxScaler = preprocessing.MinMaxScaler()
trainingData = minMaxScaler.fit_transform(trainingData)
validationData = minMaxScaler.transform(validationData)
经过归一化之后,向量的每一维的取值都被映射到 [ 0 , 1 ] [0,1] [0,1] 之间:
array([[0.8 , 0. , 0.5 , ..., 0. , 0. , 0. ],
[0.9 , 0. , 0.25 , ..., 0. , 0. , 0. ],
[0.9 , 0. , 0.25 , ..., 0. , 0. , 0. ],
...,
[0.8 , 0. , 0.5 , ..., 0. , 0. , 0. ],
[0.825, 0. , 0.5 , ..., 0. , 0. , 0. ],
[0.8 , 0. , 0.5 , ..., 0. , 0. , 0. ]])
之后,我们再次运行实验,得到实验结果:
3.8 扩展
-
初次之外,我们还可以利用各种方法,如:L2规范化,标准化等对数据进行预处理,通过不同的变换使得特征展现出不同的分布。
-
我们可以利用特征选择的方法,如:卡方检验,基于相关系数互信息的度量等。在训练前,预先选择特征。或者利用
PCA
技术选取重要的特征。 -
或者,我们可以更换
SVM
的核函数,来尝试不同参数下,分类器的性能