0%

XGBoost——贰

本文介绍如何使用XGboost预测是否患有心脏病,涉及的数据有数值型和类别型。最后,使用了sklearn的自动超参数优化方法,寻找最优的超参数。

1 数据集导入与预处理

本次测试使用的是Kaggle上最近很火的预测心脏病的数据集,地址如下:

https://www.kaggle.com/fedesoriano/heart-failure-prediction/code?datasetId=1582403&sortBy=dateRun&tab=profile

使用pandas导入输入集并查看数据格式:

1
2
3
import pandas as pd
features = pd.read_csv("data/heart.csv")
features.head()
Age Sex ChestPainType RestingBP Cholesterol FastingBS RestingECG MaxHR ExerciseAngina Oldpeak ST_Slope HeartDisease
0 40 M ATA 140 289 0 Normal 172 N 0.0 Up 0
1 49 F NAP 160 180 0 Normal 156 N 1.0 Flat 1
2 37 M ATA 130 283 0 ST 98 N 0.0 Up 0
3 48 F ASY 138 214 0 Normal 108 Y 1.5 Flat 1
4 54 M NAP 150 195 0 Normal 122 N 0.0 Up 0

1.1 认识数据集

1.1.1 认识特征

  1. 数据的整体
1
2
features.shape
# (918, 12)

可以看到,训练数据集有918个样本,11个特征,最后一列为标签值,标签为1代表患有心脏病,标签为0表示没有患有心脏病。

1
features.info()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 918 entries, 0 to 917
Data columns (total 12 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Age 918 non-null int64
1 Sex 918 non-null object
2 ChestPainType 918 non-null object
3 RestingBP 918 non-null int64
4 Cholesterol 918 non-null int64
5 FastingBS 918 non-null int64
6 RestingECG 918 non-null object
7 MaxHR 918 non-null int64
8 ExerciseAngina 918 non-null object
9 Oldpeak 918 non-null float64
10 ST_Slope 918 non-null object
11 HeartDisease 918 non-null int64
dtypes: float64(1), int64(6), object(5)
memory usage: 86.2+ KB
  1. 查看缺失数据情况
1
features.isnull().sum()
1
2
3
4
5
6
7
8
9
10
11
12
13
Age               0
Sex 0
ChestPainType 0
RestingBP 0
Cholesterol 0
FastingBS 0
RestingECG 0
MaxHR 0
ExerciseAngina 0
Oldpeak 0
ST_Slope 0
HeartDisease 0
dtype: int64

没有缺失数据!

  1. 类别数据查看

特征值取值上,有类别数据和连续值,我们通过unique()查看类别数据的个数:

1
2
3
4
features["Sex"].unique()
# array(['M', 'F'], dtype=object)
features["ChestPainType"].unique()
# array(['ATA', 'NAP', 'ASY', 'TA'], dtype=object)
  1. 数值型数据查看

    1
    features.describe()

得到的结果为:

Age RestingBP Cholesterol FastingBS MaxHR Oldpeak HeartDisease
count 918.000000 918.000000 918.000000 918.000000 918.000000 918.000000 918.000000
mean 53.510893 132.396514 198.799564 0.233115 136.809368 0.887364 0.553377
std 9.432617 18.514154 109.384145 0.423046 25.460334 1.066570 0.497414
min 28.000000 0.000000 0.000000 0.000000 60.000000 -2.600000 0.000000
25% 47.000000 120.000000 173.250000 0.000000 120.000000 0.000000 0.000000
50% 54.000000 130.000000 223.000000 0.000000 138.000000 0.600000 1.000000
75% 60.000000 140.000000 267.000000 0.000000 156.000000 1.500000 1.000000
max 77.000000 200.000000 603.000000 1.000000 202.000000 6.200000 1.000000

对于数值型数据,还可以查看他们之间的线性关联程度,相关系数的取值范围为[-1,1],属于0.8-1:极强相关;属于0.6-0.8:强相关;属于0.4-0.6:中等程度相关;属于0.2-0.4:弱相关;0-0.2:极弱相关或无相关。注意这是线性关联程度,只能作为参考。

1
features.corr()
Age RestingBP Cholesterol FastingBS MaxHR Oldpeak HeartDisease
Age 1.000000 0.254399 -0.095282 0.198039 -0.382045 0.258612 0.282039
RestingBP 0.254399 1.000000 0.100893 0.070193 -0.112135 0.164803 0.107589
Cholesterol -0.095282 0.100893 1.000000 -0.260974 0.235792 0.050148 -0.232741
FastingBS 0.198039 0.070193 -0.260974 1.000000 -0.131438 0.052698 0.267291
MaxHR -0.382045 -0.112135 0.235792 -0.131438 1.000000 -0.160691 -0.400421
Oldpeak 0.258612 0.164803 0.050148 0.052698 -0.160691 1.000000 0.403951
HeartDisease 0.282039 0.107589 -0.232741 0.267291 -0.400421 0.403951 1.000000

查看数据偏斜程度,用来描述数据分布形态的统计量,其描述的是某总体取值分布的对称性,简单来说就是数据的不对称程度。
(1)Skewness = 0 ,分布形态与正态分布偏度相同。
(2)Skewness > 0 ,正偏差数值较大,为正偏或右偏。长尾巴拖在右边,数据右端有较多的极端值。
(3)Skewness < 0 ,负偏差数值较大,为负偏或左偏。长尾巴拖在左边,数据左端有较多的极端值。
(4)数值的绝对值越大,表明数据分布越不对称,偏斜程度大。

1
features.skew()
1
2
3
4
5
6
7
8
Age            -0.195933
RestingBP 0.179839
Cholesterol -0.610086
FastingBS 1.264484
MaxHR -0.144359
Oldpeak 1.022872
HeartDisease -0.215086
dtype: float64

可以看到,FastingBS和Oldpeak两类数据的偏斜较大。

1.1.2 判断样本分布是否平衡

因为类别数据是0/1,所以通过如下式子计算样本分布是否均衡:

1
2
sum(y) / len(y)
# 0.5533769063180828

样本是均衡的!

1.1.3 切分数据

倒数第1列label,其他列为特征:

1
2
X = features.drop("HeartDisease",axis=1).copy()
y = features["HeartDisease"].copy()

1.1.4 对类别特征进行one-hot编码

因为我们后续准备对数据采用xgboost的方式建模,所以在这里我们对类别数据进行ont-hot建模。首先需要说明一下,为什么1-N建模不可取,因为如果采用1-N建模,那么编码相近的类别会被认为联系更大,然而实际上这没有任何关系。需要说明的是,ont-hot编码只只适用于树模型,logsitc回归等模型不可以。代码如下:

1
2
X_encoded = pd.get_dummies(X,columns=["ChestPainType","Sex","RestingECG","ST_Slope","ExerciseAngina"])
X_encoded.head()

结果如下:

Age RestingBP Cholesterol FastingBS MaxHR Oldpeak ChestPainType_ASY ChestPainType_ATA ChestPainType_NAP ChestPainType_TA Sex_F Sex_M RestingECG_LVH RestingECG_Normal RestingECG_ST ST_Slope_Down ST_Slope_Flat ST_Slope_Up ExerciseAngina_N ExerciseAngina_Y
0 40 140 289 0 172 0.0 0 1 0 0 0 1 0 1 0 0 0 1 1 0
1 49 160 180 0 156 1.0 0 0 1 0 1 0 0 1 0 0 1 0 1 0
2 37 130 283 0 98 0.0 0 1 0 0 0 1 0 0 1 0 0 1 1 0
3 48 138 214 0 108 1.5 1 0 0 0 1 0 0 1 0 0 1 0 0 1
4 54 150 195 0 122 0.0 0 0 1 0 0 1 0 1 0 0 0 1 1 0

主要关注原先是类别数据那几列,即”ChestPainType”,”Sex”,”RestingECG”,”ST_Slope”,”ExerciseAngina”。可以看到,对类别数据进行了ont-hot编码。

1.1.5 划分训练集和测试集

最后,我们划分一下训练集合和测试集合,为后续上模型预测做准备:

1
2
3
from sklearn.model_selection import train_test_split

X_train,X_test,y_train,y_test = train_test_split(X_encoded,y,random_state=42,stratify=y)

查看一下划分的效果:

1
2
3
4
sum(y_train) / len(y_train)
# 0.5537790697674418
sum(y_test) / len(y_test)
# 0.5521739130434783

嗯,分的不错!

2 XGboost的安装

xgboost和其他机器学习模型不一样,不能在sklearn中调用,而是有自己的包:

1
pip install xgboost

3 XGboost的简单使用

因为我们前面已经处理好了数据,因此直接套模型:

1
2
3
4
5
6
7
8
9
import xgboost as xgb

clf_xgb = xgb.XGBClassifier(objective="binary:logistic",miss=None,seed=66)
clf_xgb.fit(X_train,
y_train,
verbose=True,
early_stopping_rounds=10,
eval_metric="aucpr",
eval_set=[(X_test,y_test)])

这就训练结束了,不出意外的话,可以达到0.93890的准确度。查看一下模型的混淆矩阵:

1
2
3
4
5
6
7
from sklearn.metrics import plot_confusion_matrix

plot_confusion_matrix(clf_xgb,
X_test,
y_test,
values_format="d",
display_labels=["HeartDisease","Healthy"])

可以看到,xgboost确实非常强大,随便训练一下,就可以得到很不错的结果。XGBClassifier中的各个参数意义参加:

https://www.cnblogs.com/wanglei5205/p/8579244.html

我们这里目标函数选择为二分类,返回概率,其他都是默认值。

4 XGboost的自动调参

上面使用的基本都是默认参数,那么如果想继续提升模型的预测精度,就需要进行手动调参了。

这里,我们使用了sklearn的自动调参工具GridSearchCV,听名字就知道这是采用网格搜索的方法进行调参的。这里,我们只需要在上面的基础上,把xgb.XGBClassifier类换成GridSearchCV类,其他都不变:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from sklearn.model_selection import GridSearchCV

optimal_params = GridSearchCV(
estimator=xgb.XGBClassifier(objective="binary:logistic",
seed=42,
subsample=0.9,
colsample_bytree=1),
param_grid=param_grid,
scoring="roc_auc", # 不均衡样本
verbose=0,
n_jobs=10,
cv=3
)

optimal_params.fit(X_train,
y_train,
early_stopping_rounds=10,
eval_metric="auc",
eval_set=[(X_test,y_test)],
verbose=False)

当然,这里的param_grid参数值就是我们需要搜索的范围了,定义如下:

1
2
3
4
5
6
7
param_grid = {
"max_depth": [3,4,5],
"learning_rate":[0.1,0.01,0.05],
"gamma":[0,0.25,1.00],
"reg_lambda":[0,1,5],
"scale_pos_weight":[1,3,5]
}

按照和上述相同的方式就可以自动寻找最合适的超参数了。

完事以后,我们打印一下结果:

1
print(optimal_params.best_params_)