工具:天池NoteBook 训练数据10.9MB
训练数据下载链接:https://tianchi-media.oss-cn-beijing.aliyuncs.com/DSW/7XGBoost/train.csv
本文主要内容来源于天池龙珠计划寒假训练营教材,部分内容有修改。
原文地址:
添加链接描述
简单易用。相对其他机器学习库,用户可以轻松使用XGBoost并获得相当不错的效果。
高效可扩展。在处理大规模数据集时速度快效果好,对内存等硬件资源要求不高。
鲁棒性强。相对于深度学习模型不需要精细调参便能取得接近的效果。
XGBoost内部实现提升树模型,可以自动处理缺失值。
相对于深度学习模型无法对时空位置建模,不能很好地捕获图像、语音、文本等高维数据。
在拥有海量训练数据,并能找到合适的深度学习模型时,深度学习的精度可以遥遥领先XGBoost。
1.eta[默认0.3]
通过为每一颗树增加权重,提高模型的鲁棒性。
典型值为0.01-0.2。
2.min_child_weight[默认1]
决定最小叶子节点样本权重和。
这个参数可以避免过拟合。当它的值较大时,可以避免模型学习到局部的特殊样本。
但是如果这个值过高,则会导致模型拟合不充分。
3.max_depth[默认6]
这个值也是用来避免过拟合的。max_depth越大,模型会学到更具体更局部的样本。
典型值:3-10
4.max_leaf_nodes
树上最大的节点或叶子的数量。
可以替代max_depth的作用。
这个参数的定义会导致忽略max_depth参数。
5.gamma[默认0]
在节点分裂时,只有分裂后损失函数的值下降了,才会分裂这个节点。Gamma指定了节点分裂所需的最小损失函数下降值。 这个参数的值越大,算法越保守。这个参数的值和损失函数息息相关。
6.max_delta_step[默认0]
这参数限制每棵树权重改变的最大步长。如果这个参数的值为0,那就意味着没有约束。如果它被赋予了某个正值,那么它会让这个算法更加保守。
但是当各类别的样本十分不平衡时,它对分类问题是很有帮助的。
7.subsample[默认1]
这个参数控制对于每棵树,随机采样的比例。
减小这个参数的值,算法会更加保守,避免过拟合。但是,如果这个值设置得过小,它可能会导致欠拟合。
典型值:0.5-1
8.colsample_bytree[默认1]
用来控制每棵随机采样的列数的占比(每一列是一个特征)。
典型值:0.5-1
9.colsample_bylevel[默认1]
用来控制树的每一级的每一次分裂,对列数的采样的占比。
subsample参数和colsample_bytree参数可以起到相同的作用,一般用不到。
10.lambda[默认1]
权重的L2正则化项。(和Ridge regression类似)。
这个参数是用来控制XGBoost的正则化部分的。虽然大部分数据科学家很少用到这个参数,但是这个参数在减少过拟合上还是可以挖掘出更多用处的。
11.alpha[默认1]
权重的L1正则化项。(和Lasso regression类似)。
可以应用在很高维度的情况下,使得算法的速度更快。
12.scale_pos_weight[默认1]
在各类别样本十分不平衡时,把这个参数设定为一个正值,可以使算法更快收敛。
2.4.2 XGBoost原理粗略讲解
XGBoost底层实现了GBDT算法,并对GBDT算法做了一系列优化:
对目标函数进行了泰勒展示的二阶展开,可以更加高效拟合误差。
提出了一种估计分裂点的算法加速CART树的构建过程,同时可以处理稀疏数据。
提出了一种树的并行策略加速迭代。
为模型的分布式算法进行了底层优化。
XGBoost是基于CART树的集成模型,它的思想是串联多个决策树模型共同进行决策。
image.png
那么如何串联呢?XGBoost采用迭代预测误差的方法串联。举个通俗的例子,我们现在需要预测一辆车价值3000元。我们构建决策树1训练后预测为2600元,我们发现有400元的误差,那么决策树2的训练目标为400元,但决策树2的预测结果为350元,还存在50元的误差就交给第三棵树……以此类推,每一颗树用来估计之前所有树的误差,最后所有树预测结果的求和就是最终预测结果!
XGBoost的基模型是CART回归树,它有两个特点:(1)CART树,是一颗二叉树。(2)回归树,最后拟合结果是连续值。
XGBoost模型可以表示为以下形式,我们约定 f t ( x ) f_t(x) ft(x)表示前 t t t颗树的和, h t ( x ) h_t(x) ht(x)表示第 t t t颗决策树,模型定义如下:
f t ( x ) = ∑ t = 1 T h t ( x ) f_{t}(x)=\sum_{t=1}^{T} h_{t}(x) ft(x)=∑t=1Tht(x)
由于模型递归生成,第 t t t步的模型由第 t − 1 t-1 t−1步的模型形成,可以写成:
f t ( x ) = f t − 1 ( x ) + h t ( x ) f_{t}(x)=f_{t-1}(x)+h_{t}(x) ft(x)=ft−1(x)+ht(x)
每次需要加上的树 h t ( x ) h_t(x) ht(x)是之前树求和的误差:
r t , i = y i − f m − 1 ( x i ) r_{t, i}=y_{i}-f_{m-1}\left(x_{i}\right) rt,i=yi−fm−1(xi)
我们每一步只要拟合一颗输出为 r t , i r_{t,i} rt,i的CART树加到 f t − 1 ( x ) f_{t-1}(x) ft−1(x)就可以了。
案例简介:
基于天池公开的北京市某一时段的各监测点的数据,用来预测明天是否下雨。本次我们选择天气数据集进行方法的尝试训练,现在有一些由气象站提供的每日降雨数据,我们需要根据历史降雨数据来预测明天会下雨的概率。样例涉及到的测试集数据test.csv与train.csv的格式完全相同,但其RainTomorrow未给出,为预测变量。
#导入需要用到的数据集
!wget https://tianchi-media.oss-cn-beijing.aliyuncs.com/DSW/7XGBoost/train.csv
## 基础函数库
import numpy as np
import pandas as pd
## 绘图函数库
import matplotlib.pyplot as plt
import seaborn as sns
数据的各个特征描述如下:
数据的各个特征描述如下:
特征名称 意义 取值范围
Date 日期 字符串
Location 气象站的地址 字符串
MinTemp 最低温度 实数
MaxTemp 最高温度 实数
Rainfall 降雨量 实数
Evaporation 蒸发量 实数
Sunshine 光照时间 实数
WindGustDir 最强的风的方向 字符串
WindGustSpeed 最强的风的速度 实数
WindDir9am 早上9点的风向 字符串
WindDir3pm 下午3点的风向 字符串
WindSpeed9am 早上9点的风速 实数
WindSpeed3pm 下午3点的风速 实数
Humidity9am 早上9点的湿度 实数
Humidity3pm 下午3点的湿度 实数
Pressure9am 早上9点的大气压 实数
Pressure3pm 早上3点的大气压 实数
Cloud9am 早上9点的云指数 实数
Cloud3pm 早上3点的云指数 实数
Temp9am 早上9点的温度 实数
Temp3pm 早上3点的温度 实数
RainToday 今天是否下雨 No,Yes
RainTomorrow 明天是否下雨 No,Yes
## 我们利用Pandas自带的read_csv函数读取并转化为DataFrame格式
data = pd.read_csv('train.csv')
data.info() # 查看数据的整体信息
data.head() # 查看数据表的前5行
data.describe() # 查看数据的统计学信息
## 利用value_counts函数查看训练集标签的数量
pd.Series(data['RainTomorrow']).value_counts()
缺失值的处理,通过isna 和isnull 方法和sum()可以快速过滤出对应的缺失值并进行统计汇总
例如:data.isnull().sum()
常见缺失值处理方法:
a)、 缺失数据行数如果占总体数据量的比例很小,则可以考虑直接删除;
b)、对缺失值进行填充,比如:中位数、平均值、众数等
c)、对于可以进行拟合的数值型数据,可以考虑进行拟合,比如线性回归、随机森林等
填充缺失值的方法.fillna(要填充的数据)
删除缺失值的方法.drop()
# 提取特征变量中的数值型特征:
numerical_features = [x for x in data.columns if data[x].dtype == np.float]
# 提取特征变量中的类别特征,注意不包含目标变量RainTomorrow.
category_features = [x for x in data.columns if data[x].dtype != np.float and x != 'RainTomorrow']
## 选取三个特征与标签组合的散点可视化
sns.pairplot(data=data[['Rainfall',
'Evaporation',
'Sunshine'] + ['RainTomorrow']], diag_kind='hist', hue= 'RainTomorrow')
plt.show()
data[['Rainfall','Evaporation','Sunshine']+['RainTomorrow']].head()
# 为了更好的理解为什么会画出这样的图片,首先要查看下画图用到的数据究竟是啥样子的。三个变量两两交叉故有3x3 = 9 张图
data[['Rainfall','Evaporation','Sunshine']+['RainTomorrow']].head()
for col in data[numerical_features].columns:
if col != 'RainTomorrow':
sns.boxplot(x='RainTomorrow', y=col, saturation=0.5, palette='pastel', data=data)
plt.title(col)
plt.show()
其余图表省略
利用箱型图我们也可以得到不同类别在不同特征上的分布差异情况。我们可以发现Sunshine,Humidity3pm,Cloud9am,Cloud3pm的区分能力较强
故对Sunshine,Humidity3pm,Cloud9am,Cloud3pm这4个变量进一步深入研究:
tlog = {}
for i in category_features:
tlog[i] = data[data['RainTomorrow'] == 'Yes'][i].value_counts()
flog = {}
for i in category_features:
flog[i] = data[data['RainTomorrow'] == 'No'][i].value_counts()
data1 = pd.DataFrame(tlog['RainToday'])
data1[:2].sort_index()['RainToday']
plt.figure(figsize=(10,10))
plt.subplot(1,2,1) # 表示1行两列的画布中第一个子图表
plt.title('RainTomorrow')
sns.barplot(x = pd.DataFrame(tlog['Location']).sort_index()['Location'], y = pd.DataFrame(tlog['Location']).sort_index().index, color = "red")
plt.subplot(1,2,2)
plt.title('Not RainTomorrow')
sns.barplot(x = pd.DataFrame(flog['Location']).sort_index()['Location'], y = pd.DataFrame(flog['Location']).sort_index().index, color = "blue")
plt.show()
从上图可以发现不同地区降雨情况差别很大,有些地方明显更容易降雨
plt.figure(figsize=(10,2))
plt.subplot(1,2,1)
plt.title('RainTomorrow')
sns.barplot(x = pd.DataFrame(tlog['RainToday'][:2]).sort_index()['RainToday'], y = pd.DataFrame(tlog['RainToday'][:2]).sort_index().index, color = "red")
plt.subplot(1,2,2)
plt.title('Not RainTomorrow')
sns.barplot(x = pd.DataFrame(flog['RainToday'][:2]).sort_index()['RainToday'], y = pd.DataFrame(flog['RainToday'][:2]).sort_index().index, color = "blue")
plt.show()
由于XGBoost无法处理字符串类型的数据,我们需要一些方法讲字符串数据转化为数据。一种最简单的方法是把所有的相同类别的特征编码成同一个值,例如女=0,男=1,狗狗=2,所以最后编码的特征值是在 [0,特征数量−1] 之间的整数。除此之外,还有独热编码、求和编码、留一法编码等等方法可以获得更好的效果。
- 主要目的:就是将非数值型特征变量就行转化成数值型变量,方便模型对数据进行处理;
## 把所有的相同类别的特征编码为同一个值
def get_mapfunction(x):
mapp = dict(zip(x.unique().tolist(),
range(len(x.unique().tolist()))))
def mapfunction(y):
if y in mapp:
return mapp[y]
else:
return -1
return mapfunction
for i in category_features:
data[i] = data[i].apply(get_mapfunction(data[i]))
## 编码后的字符串特征变成了数字
data['Location'].unique()
输出结果如下:
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48])
# 探查特征变量之间的相关性,决定是否考虑交叉项或者对高度相关的变量之间进行处理。
pearson_mat = data_features_part.corr(method='pearson') # 计算皮尔逊相关系数矩阵
plt.figure(figsize=(15,15)) #建立图像
a = sns.heatmap(pearson_mat,vmin=-1,vmax=1, square =True, annot=True,cmap='YlGnBu')# 用热度图表示相关系数矩阵
plt.show() # 展示热度图
# 为了正确评估模型性能,将数据划分为训练集和测试集,并在训练集上训练模型,在测试集上验证模型性能。
from sklearn.model_selection import train_test_split
## 选择其类别为0和1的样本 (不包括类别为2的样本)
data_target_part = data['RainTomorrow']
data_features_part = data[[x for x in data.columns if x != 'RainTomorrow']]
## 测试集大小为20%, 80%/20%分
x_train, x_test, y_train, y_test = train_test_split(data_features_part, data_target_part, test_size = 0.2, random_state = 2020)
XGBClassifier(base_score=0.5, booster='gbtree', colsample_bylevel=1,
colsample_bynode=1, colsample_bytree=1, gamma=0, learning_rate=0.1,
max_delta_step=0, max_depth=3, min_child_weight=1, missing=None,
n_estimators=100, n_jobs=1, nthread=None,
objective='binary:logistic', random_state=0, reg_alpha=0,
reg_lambda=1, scale_pos_weight=1, seed=None, silent=None,
subsample=1, verbosity=1)
## 导入XGBoost模型
from xgboost.sklearn import XGBClassifier
## 定义 XGBoost模型
clf = XGBClassifier()
# 在训练集上训练XGBoost模型
clf.fit(x_train, y_train)
## 在训练集和测试集上分布利用训练好的模型进行预测
train_predict = clf.predict(x_train)
test_predict = clf.predict(x_test)
from sklearn import metrics
## 利用accuracy(准确度)【预测正确的样本数目占总预测样本数目的比例】评估模型效果
print('The accuracy of the Logistic Regression is:',metrics.accuracy_score(y_train,train_predict))
print('The accuracy of the Logistic Regression is:',metrics.accuracy_score(y_test,test_predict))
## 查看混淆矩阵 (预测值和真实值的各类情况统计矩阵)
confusion_matrix_result = metrics.confusion_matrix(test_predict,y_test)
print('The confusion matrix result:\n',confusion_matrix_result)
# 利用热力图对于结果进行可视化
plt.figure(figsize=(8, 6))
sns.heatmap(confusion_matrix_result, annot=True, cmap='Blues')
plt.xlabel('Predicted labels')
plt.ylabel('True labels')
plt.show()
结果:
The accuracy of the Logistic Regression is: 0.8517142354802789
The accuracy of the Logistic Regression is: 0.8469689155609733
The confusion matrix result:
[[15759 2470]
[ 794 2306]]
通过上述的初步探索,我们可以发现已经有0.85 左右的准确度【预测正确的样本数目占总预测样本数目的比例】。但是,我们想要提高模型的准确定,一方面可以对特征进行选择剔除显著或者影响较小的变量;另一面,我们也可以对XGBoost 的参数进行调参。
XGBoost的特征选择属于特征选择中的嵌入式方法,在XGboost中可以用属性feature_importances_去查看特征的重要度。
sns.barplot(y=data_features_part.columns, x=clf.feature_importances_)
从图中我们可以发现下午3点的湿度与今天是否下雨是决定第二天是否下雨最重要的因素
weight:是以特征用到的次数来评价
gain:当利用特征做划分的时候的评价基尼指数
cover:利用一个覆盖样本的指标二阶导数(具体原理不清楚有待探究)平均值来划分。
total_gain:总基尼指数
total_cover:总覆盖
from sklearn.metrics import accuracy_score
from xgboost import plot_importance
def estimate(model,data):
#sns.barplot(data.columns,model.feature_importances_)
ax1=plot_importance(model,importance_type="gain")
ax1.set_title('gain')
ax2=plot_importance(model, importance_type="weight")
ax2.set_title('weight')
ax3 = plot_importance(model, importance_type="cover")
ax3.set_title('cover')
plt.show()
def classes(data,label,test):
model=XGBClassifier()
model.fit(data,label)
ans=model.predict(test)
estimate(model, data)
return ans
ans=classes(x_train,y_train,x_test)
pre=accuracy_score(y_test, ans)
print('acc=',accuracy_score(y_test,ans))
通过上述分析就可以剔除一些影响比较小的特征变量,保留对应的特征变量:
比如:选取特征重要性数值>0.05的那些特征,进行重新拟合;
XGBoost中包括但不限于下列对模型影响较大的参数:
learning_rate: 有时也叫作eta,系统默认值为0.3。每一步迭代的步长,很重要。太大了运行准确率不高,太小了运行速度慢。
subsample:系统默认为1。这个参数控制对于每棵树,随机采样的比例。减小这个参数的值,算法会更加保守,避免过拟合, 取值范围零到一。
colsample_bytree:系统默认值为1。我们一般设置成0.8左右。用来控制每棵随机采样的列数的占比(每一列是一个特征)。
max_depth: 系统默认值为6,我们常用3-10之间的数字。这个值为树的最大深度。这个值是用来控制过拟合的。max_depth越大,模型学习的更加具体。
调节模型参数的方法有贪心算法、网格调参、贝叶斯调参等。这里我们采用网格调参,它的基本思想是穷举搜索:在所有候选的参数选择中,通过循环遍历,尝试每一种可能性,表现最好的参数就是最终的结果;
## 从sklearn库中导入网格调参函数
from sklearn.model_selection import GridSearchCV
## 定义参数取值范围
learning_rate = [0.1, 0.3, 0.6]
subsample = [0.8, 0.9]
colsample_bytree = [0.6, 0.8]
max_depth = [3,5,8]
parameters = { 'learning_rate': learning_rate,
'subsample': subsample,
'colsample_bytree':colsample_bytree,
'max_depth': max_depth}
model = XGBClassifier(n_estimators = 50)
## 进行网格搜索
clf = GridSearchCV(model, parameters, cv=3, scoring='accuracy',verbose=1,n_jobs=-1)
clf = clf.fit(x_train, y_train)
clf.best_params_
## 在训练集和测试集上分布利用最好的模型参数进行预测
## 定义带参数的 XGBoost模型
clf = XGBClassifier(colsample_bytree = 0.6, learning_rate = 0.3, max_depth= 8, subsample = 0.9)
# 在训练集上训练XGBoost模型
clf.fit(x_train, y_train)
train_predict = clf.predict(x_train)
test_predict = clf.predict(x_test)
## 利用accuracy(准确度)【预测正确的样本数目占总预测样本数目的比例】评估模型效果
print('The accuracy of the Regression is:',metrics.accuracy_score(y_train,train_predict))
print('The accuracy of the Regression is:',metrics.accuracy_score(y_test,test_predict))
## 查看混淆矩阵 (预测值和真实值的各类情况统计矩阵)
confusion_matrix_result = metrics.confusion_matrix(test_predict,y_test)
print('The confusion matrix result:\n',confusion_matrix_result)
# 利用热力图对于结果进行可视化
plt.figure(figsize=(8, 6))
sns.heatmap(confusion_matrix_result, annot=True, cmap='Blues')
plt.xlabel('Predicted labels')
plt.ylabel('True labels')
plt.show()