SVM

#%% md

支持向量机算法在心脏病预测中的应用

#%% md

一、实验简介

(一)问题描述

心脏病是人类健康的头号杀手,全世界1/3的人口死亡是心脏病引起的。

如何快速、有效地对心脏病进行诊断始终是生命科学领域研究的重点问题之一。如果可以通过采集人体相关的体测指标数据,利用机器学习来分析这些数据的各个特征对于心脏病的影响,帮助医生做出有效的决策,将对心脏病的治疗起到至关重要的作用。

本实验借助美国某区域的心脏病检查患者的体测数据,利用Sklearn机器学习库中的支持向量机算法对该地区的体测数据进行学习,再对新的体测数据进行预测,判断其是否患有心脏病。

#%% md

(二)问题需求

本实验中的数据集中总共有303个样本数据,每个样本包含14个属性,其中属性target代表分类标签(0=no;1=yes)。

数据中其它属性的具体描述如下:

特征名 特征含义
age 年龄
sex 性别 1=male,0=female
cp 胸痛类型(4种) 值1:典型心绞痛;值2:非典型心绞痛;值3:非心绞痛;值4:无症状
trestbps 静息血压
chol 血清胆固醇
fbs 空腹血糖 >120mg/dl,1=true;0=false
restecg 静息心电图(值0,1,2)
thalach 达到的最大心率
exang 运动诱发的心绞痛(1=yes;0=no)
oldpeak 相对于休息的运动引起的ST值(ST值与心电图上的位置有关)
slope 运动高峰ST段的坡度, Value 1: upsloping向上倾斜; Value 2: flat持平; Value 3: downsloping向下倾斜
ca 透视检查看到的血管数(0-3)
thal 缺陷种类(3 =正常;6 =固定缺陷;7 =可逆转缺陷)

实验中将整个数据集以8:2的比例拆分为训练集与测试集。

数据文件所在的位置“支持向量机\data\hearts.csv”。

本实验将使用心脏病数据训练集来训练SVM模型,并使用测试集在训练得到的模型上进行验证,得到每个类别的精确度、召回率、F1值等信息。

#%% md

二、实验任务

本实验利用Sklearn库中的支持向量机模块,通过将303个样本中的80%作为训练集对支持向量机模型进行训练,使用训练后的模型对其余20%的样本进行分类预测。

具体的实验任务如下:

1.观察数据集:加载数据集,并观察数据集的整体情况,查看数据的行、列数以及各个数据的数据类型,对数据进行描述性统计,大致了解本实验的数据集。

2.数据可视化:对age这列特征进行分析,并绘出直方图,观察不同年龄的统计情况。

3.数据处理:对age这列特征划分等级,构造成新的类别型特征;再对此数据集的特征进行独热编码;为减少计算量,需要对特征数据进行标准化。

4.数据集的划分:实验中需要将数据集按8:2的比例划分成训练集和测试集。

5.模型训练:需要实现SVM模型,并使用训练集对模型进行训练。

6.模型的评估:需要用测试集评估模型分类结果的准确度。

#%% md

三、实验步骤

#%% md

(一)加载相关的包

1.加载Numpy、Pandas等包做数据处理。

2.加载Matplotlib包做数据可视化。

3.从Sklearn.svm加载SVC算法函数。

4.从Sklearn.preprocessing中加载StandardScaler函数。

5.从Sklearn.model_selection中加载train_test_split训练集-测试集划分的函数。

6.从Sklearn.metrics中加载classification_report评估函数。

#%%

import numpy as np # 数据处理包
import pandas as pd # 文件读取包
import seaborn as sns # 数据可视化包
import matplotlib.pyplot as plt # 数据可视化包
from sklearn.svm import SVC # 支持向量机包
from sklearn.preprocessing import StandardScaler # 数据标准化
from sklearn.model_selection import train_test_split # 数据划分函数
from sklearn.metrics import classification_report # 评估函数

#%% md

(二)加载数据集

利用Pandas中的read_csv()方法来读取csv格式的数据文件。

#%%

train = pd.read_csv(‘data/heart.csv’) # 加载数据文件,注意加载数据文件的方式

#%% md

为了更直观的了解数据集的内容,观察数据的整体特征,可以使用Pandas中的head(15)函数和tail(15)函数来查看整个dataframe中的前后15行数据。

#%%

train.head(15) # 观察数据集的前15行

#%%

train.tail(15) # 观察数据集的后15行

#%% md

(三)数据处理

读取完数据集后,需要观察数据集的信息,对数据进行预处理,解决掉干扰因素,才能更加精准地分析结果。一般而言该阶段包括:观察数据统计信息、缺失数据处理、冗余数据处理、标准化等过程。 本实验数据处理的主要工作如下:

1.观察数据集:观察数据的整体信息,查看数据描述统计信息,便于后续的数据预处理。

2.缺失值判断:如果含有缺失值,需要进行相应的填充,保证数据的完整性。

3.数据可视化:对数据的特征进行可视化,分析数据,并对特征进行相应的处理。

5.数据标准化:对数据进行标准化。

6.数据集的划分:将数据划分成训练集和测试集。

(1)观察数据集

利用Pandas中的info()函数来查看数据集的大小、行列规模以及数据类型。

#%%

train.info() # 观察数据的整体信息

#%% md

利用pandas中的describe()函数来查看数据集的描述性统计信息,包括计数、均值、std、各个分位数等,数据集里的NaN值不参与计算。

#%%

train.describe() # 数据集的统计数据

#%% md

(2)缺失值判断

由于模型训练时需要一个无缺失值的数据,在数据处理的过程中需要对缺失值进行检测,可以利用pandas.isnull().sum()查看数据集是否存在缺失值,如果含有缺失值,需要进行缺失值处理。

isnull()函数返回的是布尔型的矩阵,某处如为缺失值,值是True;某处不为缺失值,值是False。再通过sum()函数对缺失值数量进行统计。

#%%

train.isnull().sum() # 可以观察到数据集没有缺失值,后面无需做缺失值处理

#%% md

(3)数据可视化

为了减小算法的时间和空间开销,可以对数据进行离散化。离散化特征易于模型的快速迭代,也对异常数据有很强的鲁棒性。比如一个特征是年龄,年龄>30是1,否则0。如果特征没有离散化,一个异常数据“年龄300岁”会给模型造成很大的干扰。在进行数据的离散化时,可以对数据集的年龄这列特征画出其直方图来观察年龄的分布情况。

绘制直方图时,首先利用pd.crosstab(index,columns)函数按照指定的行和列统计分组频数,然后利用Pandas下的自带的plot(kind=”bar”,figsize=(20,8))函数绘出年龄的直方图,以便更直观的观察年龄这列特征,最后利用plt.xlabel()和plt.ylabel()设置坐标轴的lable。

plt.crosstab()函数所使用的参数解释:

index: 用于行分组的值。
columns:用于列分组的值。

plot()函数所使用的参数解释:

kind:选择图表类型,默认为折线图。可选参数为‘line’(折线图)、‘bar’(柱状图,竖直方向)、‘barh’(柱状图,水平方向)、‘hist’(直方图)、‘box’(箱线图)等。
figsize:设置画布的大小,其中20代表画布的长度,8代表画布的高度。

#%%

plt.rcParams[‘font.family’] = ‘SimHei’ # 解决Matplotlib绘图显示中文的问题
pd.crosstab(train.age,train.target).plot(kind=”bar”,figsize=(20,8))
plt.xlabel(‘年龄’)# x轴
plt.ylabel(‘年龄个数’)# y轴
plt.show()

#%% md

通过观察年龄分布情况,划分年龄阶段的最好标准是将年龄分为老年人、中年人、年轻人三个阶段。

首先利用min(),max(),dataframe.mean()分别求最小、最大年龄和年龄均值。

#%%

minAge=min(train.age)# 求年龄最小值
maxAge=max(train.age)# 求年龄最大值
meanAge=train.age.mean()# 求年龄均值
print(‘Min Age :’,minAge)
print(‘Max Age :’,maxAge)
print(‘Mean Age :’,meanAge)

#%% md

根据均值、最小值等对年龄这列特征设定合适的范围,以条件的形式将三范围里的年龄进行提取,再次对年龄进行统计和可视化。

#%%

young_ages=train[(train.age>=29)&(train.age<40)] #对大于29并且小于40的数据进行提取
middle_ages=train[(train.age>=40)&(train.age<55)]#对大于40并且小于55的数据进行提取
elderly_ages=train[(train.age>55)]#对大于55的数据进行提取
print(‘Young Ages :’,len(young_ages))
print(‘Middle Ages :’,len(middle_ages))
print(‘Elderly Ages :’,len(elderly_ages))

#%%

plt.figure(figsize=(12,6))#创建画布,并设置画布的长度和宽度的参数
#绘画出直方图,可以用来观察分层后年龄计数
sns.barplot(x=[‘年轻人’,’中年人’,’老年人’],y=[len(young_ages),len(middle_ages),len(elderly_ages)])
plt.xlabel(‘年龄间隔’)
plt.ylabel(‘年龄统计’)
plt.show()

#%% md

通过观察上面的直方图,可以获取区间,再将年龄这列连续变量进行离散化。

在此将需要利用Pandas中以区间和索引的方式对年龄进行分层,分别获取三个年龄阶段的下标。

#%%

train[‘AgeRange’]=0 # 在数据train中新增AgeRange这一列

分别获取分层后各个年龄层的下标

youngAge_index=train[(train.age>=29)&(train.age<40)].index
middleAge_index=train[(train.age>=40)&(train.age<55)].index
elderlyAge_index=train[(train.age>55)].index

#%% md

最后以索引的方式设置新建特征AgeRange里的值。

#%%

for index in elderlyAge_index:
train.loc[index,’AgeRange’]=2# 设置老年人值为2

for index in middleAge_index:
train.loc[index,’AgeRange’]=1# 设置中年人值为1

for index in youngAge_index:
train.loc[index,’AgeRange’]=0# 设置年轻人值为0

#%% md

由于age这列特征已经构建成一列新特征AgeRange,需要采用pandas下的drop()函数对age这列特征进行删除。

drop()函数所使用的参数解释:

labels:需要删除的列名。
axis:设置删除的维度。
inplace:是否替换原来的内容。

#%%

train.drop(labels=”age”,axis=1,inplace=True)# 删除特征age

#%% md

通过观察上面显示的数据,可以发现数据集含有很多离散特征,需要将离散特征值的个数大于等于三的特征的进行独热编码。独热编码解决了分类器不好处理属性数据的问题,在一定程度上也起到了扩充特征的作用。在这里选取cp、restecg、slope、ca、thal这几个离散特征进行独热编码。

为了实现独热编码,可以调用Pandas下的get_dummies(data, prefix=None)函数将变量进行独热编码。

get_dummies()函数所使用的参数解释:

data:数据的格式为array-like、Series或者DataFrame,代表输入的数据。
prefix:string、含字符串的list、含字符串的dict,默认为None。指定需要实现类别转换的列名。

#%%

对特征进行独热编码

dummies_cp = pd.get_dummies(train[‘cp’], prefix=’cp’)
dummies_restecg = pd.get_dummies(train[‘restecg’], prefix=’restecg’)
dummies_slope = pd.get_dummies(train[‘slope’], prefix=’slope’)
dummies_ca = pd.get_dummies(train[‘ca’], prefix=’ca’)
dummies_thal = pd.get_dummies(train[‘thal’], prefix=’thal’)

用concat函数将这些新的属性连接到dataframe中

train = pd.concat([train, dummies_cp, dummies_restecg,
dummies_slope, dummies_ca, dummies_thal], axis=1)

再通过drop函数将原先的属性从dataframe中去掉

train.drop([‘cp’, ‘restecg’, ‘slope’, ‘ca’, ‘thal’], axis=1, inplace=True)

通过columns查看编码都的特征名

train.columns

#%% md

本实验中,由于target是标签,所以把它单独取出来,采用Pandas中取某一列的方法取出标签列数据。

#%%

y = train[“target”]# 提取target

#%% md

取得了标签数据后,需要利用pandas下的drop(列名,axis=1指定为列,inplace=是否替换原来的内容)函数删除标签列。

#%%

X = train.drop(‘target’, axis=1)#删除target

#%% md

(4)数据标准化
因为数据标准化可以降低计算量,同时不会影响SVM模型预测的准确率。实验中利用Sklearn中StandardScaler().fit_transform(X)函数来对未进行处理的连续特征进行标准化。

#%%

#标准化特征数据
std = StandardScaler()
X[‘trestbps’] = std.fit_transform(X[‘trestbps’].values.reshape(-1, 1))
X[‘chol’] = std.fit_transform(X[‘chol’].values.reshape(-1, 1))
X[‘thalach’] = std.fit_transform(X[‘thalach’].values.reshape(-1, 1))
X[‘oldpeak’] = std.fit_transform(X[‘oldpeak’].values.reshape(-1, 1))

#%%

X.head() # 查看处理完后数据前五行

#%% md

(5)数据集的划分
由于本实验只提供一个数据集,因此需要将数据集划分为训练集与测试集,可以采用sklearn.preprocess模块下的train_test_split(X,y,test_size=0.2)函数来划分数据集。使用训练集训练模型,用测试集测试模型的正确率,以验证模型的有效性。

train_test_split()函数所使用的参数解释:

train_data:所要划分的样本特征集。
train_target:所要划分的样本标签。
test_size:样本占比,如果是整数的话就是样本的数量。

#%%

#划分数据集
train_X,test_X,train_y,test_y=train_test_split(X,y,test_size=0.2)
train_X.shape,train_y.shape,test_X.shape,test_y.shape

#%% md

(四)算法的介绍与应用

#%% md

支持向量机(SVM)是一种二分类模型,其基本模型定义为特征空间上的间隔最大的线性分类器,其学习策略是使间隔最大化,最终可转化为一个凸二次规划问题的求解。SVM不仅适用于线性问题还合适用于非线性问题(用核函数),但二次规划问题求解将涉及m阶矩阵的计算(m为样本的个数),因此SVM不适用于超大数据集,同时SVM不加修改仅适用于处理二分类问题。

调用SVM算法的具体实现步骤如下:

1.实例化sklearn.svm里的SVC()算法模块。

2.利用SVC().fit(train_X,train_y)函数对训练集数据进行拟合。

3.利用model.predict(test_X)函数对测试集数据进行预测。

4.利用sklearn.metrics里的accuracy_score()进行评分。

SVC()函数所使用的参数解释:

C: 错误项的惩罚系数,默认值为1.0。C越大,即对分错样本的惩罚程度越大,因此在训练样本中准确率越高,但是泛化能力降低;相反,减小C的话,容许训练样本中有一些误分类错误样本,泛化能力强。

kernel: 核函数,默认是'rbf',可以是‘linear’(线性核),‘poly’(多项式核), ‘rbf’(径向基核), ‘sigmoid’(sigmod核函数), ‘precomputed’(核矩阵)。

degree: 多项式poly函数的维度,默认是3,选择其他核函数时会被忽略。

gamma: 核函数系数,只对‘rbf’,‘poly’,‘sigmod’有效。默认是’auto’,代表其值为样本特征数的倒数,即1/n_features。

classification_report()函数所使用的参数解释:

y_true: 测试样本标签。
y_pred: 预测结果。

本实验需要使用三个不同核函数的SVM算法对训练集数据进行训练,最后利用classification_report(test_y, pred_y)函数对这三个模型的精确度、召回率、F1值等信息进行评分。

#%%

实例化算法

clf_linear=SVC(C=1.0, kernel=’linear’)# 线性核
clf_poly = SVC(C=10, kernel=’poly’, degree=1,gamma=0.6)# 多项式核,degree表示多项式核函数的最高次项次数
clf_rbf = SVC(C=1.0, kernel=’rbf’, gamma=0.6)# 径向基核
#训练模型
clf_linear.fit(train_X,train_y)
clf_poly.fit(train_X,train_y)
clf_rbf.fit(train_X,train_y)

#%%

#用测试集测试得到预测标签
linear_pred=clf_linear.predict(test_X)
poly_pred=clf_poly.predict(test_X)
rbf_pred=clf_rbf.predict(test_X)

#%%

#显示各类指标
print(‘线性核的各类指标报告;\n’,classification_report(test_y, linear_pred))
print(‘多项式核的各类指标报告;\n’,classification_report(test_y, poly_pred))
print(‘径向基核的各类指标报告;\n’,classification_report(test_y, rbf_pred))

#%% md

五、结果分析

本实验利用了美国某区域的心脏病检查患者的体测数据。在实验过程中完成了以下内容:缺失值判断、数据可视化、数据处理的过程、数据的编码、数据集的划分以及算法的调用,训练得到的三个模型的准确率稳定在85%左右。

在本实验中,数据集的特征比较少,样本数量一般,比较适合的核函数是径向基核(rbf),但如果数据集特征比较多,样本数量也多的情况下,在SVM算法中使用线性核(linear)是一个比较好的选择。在实际应用中,通常使用交叉验证的方法,来试用不同的核函数,误差最小的即为效果最好的核函数,或者也可以将多个核函数结合起来,形成混合核函数。

#%%

from sklearn.svm import SVC,SVR

#%%

from sklearn.datasets import load_iris

#%%

data = load_iris()

#%%

data_x = data.data

#%%

data_x.shape

#%%

data_y = data.target

#%%

data_y.shape

#%%

model1 = SVC(C=1.0, kernel=’linear’)# 线性核
model2 = SVC(C=10, kernel=’poly’, degree=1,gamma=0.6)# 多项式核,degree表示多项式核函数的最高次项次数
model3 = SVC(C=1.0, kernel=’rbf’, gamma=0.6)# 径向基核 高斯核

#%%

model1.fit(data_x,data_y)
model2.fit(data_x,data_y)
model3.fit(data_x,data_y)

#%%

model1.predict(data_x),data.target

#%%

model2.predict(data_x),data.target

#%%

model3.predict(data_x),data.target

#%%