上一章:机器篇——集成学习(六) 细说 GBDT 算法
下一章:机器篇——集成学习(八) 细说 ball49_pred 项目(彩票预测)
本小节,细说 XGBoost 算法,下一小节细说 ball49_pred 项目
论文参考以下链接: XGBoost: A Scalable Tree Boosting System
二. 具体算法
7. XGBoost 算法(Extreme Gradient Boostin, XGBoost)
XGBoost 是 GB 算法的一种高效实现。XGBoost 中的基学习器除了可以是 CART(gbtree),也可以是线性分类器(gblinear)。
(1). XGBoost 是一种监督学习算法,它实现了一个称为增强的过程,以产生准确的模型。
监督学习的常用目标函数,通常包含两个部分:训练误差 + 正则化
:为损失函数,度量模型预测值和真实值的误差。
平方损失函数:
交叉熵损失函数:
为分类数,特别地,当 时,为 二分类的交叉熵损失函数:
logistic 损失函数:
:为正则化项,度量模型的复杂度,避免过拟合。常用的正则化有 正则化。
正则:
正则:
(2). 模型融合
一棵 CART 往往过于简单,而无法有效地进行预测,因此更加高效的是使用多个 CART 进行融合,使用集成的方法提升预测效果。
①. 假设有两棵回归树,进行融合预测结果,公式为:
:为树的棵数
:为第 棵树对于输入 输出的得分
:为相应函数
:为相应函数空间
②. 目标函数:
:为损失函数。
(3). 模型训练
①. 假设每次迭代生成一棵树,则训练目标函数可以写成:
:为第 步迭代的预测值。
②. 迭代关系:
③. 训练目标函数可以写成:
对于第 步来说, 是已知的,则:
④. 如果 使用平方损失函数,则
其中,对于第 步来说, 也是常数。
则目标函数可化为:
:为残差。
⑤. 泰勒展开
a. 泰勒展开公式:
在 处有 阶导数
b. 目标函数:
令 , 则
c. 其中 和 为常数项。
由于目标是让这个目标函数最小化,所以常数项并没有什么用,去掉常数项后,得到的目标函数:
⑥. 对于平方损失函数
(4). 模型正则化项
①. 如何衡量一棵树的正则化项,为此,首先对 CART 树作另一番定义:
②. 定义的解释
一棵树有 个叶子节点,这 个叶子节点的值组成了一个 维向量 , 是一个映射,用来将样本映射成 1 到 的某个值。也就是把它分到某个叶子节点, 其实就代表了 CART 树的结构。 自然就是这棵树对样本 的预测值了。
③. XGBoost 的正则化项
a. 和 为 XGBoost 中的两个超参
b. 越大,表示越希望获得结构简单的树,因为此时对较多叶子节点的树惩罚越大。
c. 越大,也是希望获得结构越简单的树,因为此时对 惩罚越大。
(5). 简化目标函数
①. 令
②. 对于第 棵 CART 树的某一个确定的结构(可用 表示),所有的 和 都是正确的。而且 中各叶子节点的值 之间是相互独立的。因此,可求出各个叶子节点的最佳值以及此时目标函数的值:
:最佳树结构的最佳叶子权重
:为学习率
:为反向梯度
的最佳值就是负的梯度乘以一个权重系数,该系数类似于随机梯度下降中的学习率。当 越大时,这个系数越小,也就是学习率越小; 越大,代表在该点附近梯度变化非常剧烈。
:表示这棵树的结构有多好。值越小,代表的结构越好。也就是说,它是衡量第 棵 CART 树的结构好坏的标准。该值仅仅是用来衡量结构的好坏,与叶子节点的值可是无关的。 和 和 和 有关,而它们又和树的结构 有关,与叶子节点的值没有任何关系。
(6). 最优目标函数
在实践中,贪婪地种植这棵树
①. 从树深度为 0
②. 对每棵树的叶子节点尝试添加分裂。目标是添加拆分后的变化。
③. 对于每个节点,枚举所有特性。
④. 时间复杂度增加树的深度
:为左子树节点的分数
:为右子树节点的分数
:为不分割时树节点的分数
:为加入新的叶子节点引入的复杂度代价。
Gain 值为分裂后的值减去分裂前的值。
(7). XGBoost 的小结
①. XGBoost 是对 GBDT 进行的优化。GBDT 在优化中只用到了一阶导数信息,而 XGBoost 则对代价函数进行了二阶泰勒展开,并同时用到了一阶和二阶导数,所以 XGBoost 的效率相较于 GBDT 大大的提高,并且 XGBoost 由于可以并行计算一阶和二阶导数,所以 XGBoost 可以并行建树。
②. XGBoost 的代价函数里加入了正则项,用于控制模型的复杂度。
③. 对于缺失值的处理。对于特征的值有缺失的样本,XGBoost 可以自动学习出它的分裂方向。XGBoost 对于缺失值能预先学习一个默认的分裂方向。
(8). 代码演示
代码的操作和之前的 随机森林,GBDT 一样的,利用网格搜索,寻找出较优的参数,来进行模型训练,预测。
#!/usr/bin/env python
# _*_ coding:utf-8 _*_
# ============================================
# @Time : 2020/01/14 21:50
# @Author : WanDaoYi
# @FileName : xgboost_demo.py
# ============================================
import os
import numpy as np
import pandas as pd
from sklearn import metrics
from sklearn.model_selection import GridSearchCV, train_test_split
from xgboost.sklearn import XGBClassifier
import matplotlib.pyplot as plt
import matplotlib
# 用于解决画图中文乱码
font = {"family": "SimHei"}
matplotlib.rc("font", **font)
class XGBoostDemo(object):
def __init__(self):
self.base_path = os.getcwd()
self.data_path_last = "data_info/car.csv"
self.data_path = os.path.join(self.base_path, self.data_path_last)
self.x_train, self.x_val, self.y_train, self.y_val = self.read_data()
pass
# 获取数据集
def read_data(self):
# 读取数据文件
data_info = pd.read_csv(self.data_path, encoding="gbk")
print(data_info.head())
print("data_info_shape: {}".format(data_info.shape))
# 划分数据为训练集和验证集
x = data_info.values[:, : -1]
y = data_info.values[:, -1]
x_train, x_val, y_train, y_val = train_test_split(x, y, test_size=0.3)
print("x_train_shape: {}".format(x_train.shape))
return x_train, x_val, y_train, y_val
pass
def best_estimators_depth(self):
# np.arange 可以生成 float 类型,range 只能生成 int 类型
best_param = {'n_estimators': range(10, 201, 5),
'max_depth': range(1, 20, 1)
}
best_gsearch = GridSearchCV(estimator=XGBClassifier(learning_rate=0.1,
gamma=0,
subsample=0.8,
colsample_bytree=0.8,
objective='binary:logistic',
nthread=4,
min_child_weight=5,
seed=27
),
param_grid=best_param, scoring='roc_auc', iid=False, cv=10)
best_gsearch.fit(self.x_train, self.y_train)
print("best_param:{0}".format(best_gsearch.best_params_))
print("best_score:{0}".format(best_gsearch.best_score_))
# best_param: {'max_depth': 7, 'n_estimators': 45}
# best_score: 0.9627551020408163
return best_gsearch.best_params_
pass
def best_lr_gamma(self):
# np.arange 可以生成 float 类型,range 只能生成 int 类型
best_param = {'learning_rate': np.arange(0.1, 1.1, 0.1),
'gamma': np.arange(0.1, 5.1, 0.2)
}
best_gsearch = GridSearchCV(estimator=XGBClassifier(n_estimators=45,
max_depth=7,
# learning_rate=0.1,
# gamma=0,
subsample=0.8,
colsample_bytree=0.8,
objective='binary:logistic',
nthread=4,
min_child_weight=5,
seed=27
),
param_grid=best_param, scoring='roc_auc', iid=False, cv=10)
best_gsearch.fit(self.x_train, self.y_train)
print("best_param:{0}".format(best_gsearch.best_params_))
print("best_score:{0}".format(best_gsearch.best_score_))
# best_param: {'gamma': 4.500000000000001, 'learning_rate': 0.6}
# best_score: 0.9671996144784749
return best_gsearch.best_params_
pass
def best_subsmaple_bytree(self):
# np.arange 可以生成 float 类型,range 只能生成 int 类型
# 调整subsample(行),colsample_bytree(列)
best_param = {'subsample': np.arange(0.1, 1.1, 0.1),
'colsample_bytree': np.arange(0.1, 1.1, 0.1)
}
best_gsearch = GridSearchCV(estimator=XGBClassifier(n_estimators=45,
max_depth=7,
learning_rate=0.6,
gamma=4.5,
# subsample=1.0,
# colsample_bytree=0.8,
objective='binary:logistic',
nthread=4,
min_child_weight=5,
seed=27
),
param_grid=best_param, scoring='roc_auc', iid=False, cv=10)
best_gsearch.fit(self.x_train, self.y_train)
print("best_param:{0}".format(best_gsearch.best_params_))
print("best_score:{0}".format(best_gsearch.best_score_))
# best_param: {'colsample_bytree': 0.8, 'subsample': 0.9}
# best_score: 0.9680867346938775
return best_gsearch.best_params_
pass
def best_nthread_weight(self):
# np.arange 可以生成 float 类型,range 只能生成 int 类型
best_param = {'nthread': range(1, 20, 1),
'min_child_weight': range(1, 20, 1)
}
best_gsearch = GridSearchCV(estimator=XGBClassifier(n_estimators=45,
max_depth=7,
learning_rate=0.1,
gamma=4.3,
subsample=0.9,
colsample_bytree=0.8,
objective='binary:logistic',
# nthread=4,
# min_child_weight=5,
seed=27
),
param_grid=best_param, scoring='roc_auc', iid=False, cv=10)
best_gsearch.fit(self.x_train, self.y_train)
print("best_param:{0}".format(best_gsearch.best_params_))
print("best_score:{0}".format(best_gsearch.best_score_))
# best_param: {'min_child_weight': 2, 'nthread': 1}
# best_score: 0.9648386521025202
return best_gsearch.best_params_
pass
def best_seek(self):
# np.arange 可以生成 float 类型,range 只能生成 int 类型
best_param = {'seed': range(1, 500, 1)}
best_gsearch = GridSearchCV(estimator=XGBClassifier(n_estimators=45,
max_depth=7,
learning_rate=0.1,
gamma=4.3,
subsample=0.9,
colsample_bytree=0.8,
nthread=1,
min_child_weight=2,
# seed=27,
objective='binary:logistic'
),
param_grid=best_param, scoring='roc_auc', iid=False, cv=10)
best_gsearch.fit(self.x_train, self.y_train)
print("best_param:{0}".format(best_gsearch.best_params_))
print("best_score:{0}".format(best_gsearch.best_score_))
# best_param: {'seed': 134}
# best_score: 0.9773406154065825
return best_gsearch.best_params_
pass
# 较好的 模型参数 进行训练
def best_param_xgboost(self):
best_model = XGBClassifier(n_estimators=45,
max_depth=7,
learning_rate=0.1,
gamma=4.3,
subsample=0.9,
colsample_bytree=0.8,
nthread=1,
min_child_weight=2,
seed=134,
objective='binary:logistic'
)
best_model.fit(self.x_train, self.y_train)
y_pred = best_model.predict(self.x_val)
acc_score = metrics.accuracy_score(self.y_val, y_pred)
print("acc_score: {}".format(acc_score))
print("score: {}".format(best_model.score(self.x_val, self.y_val)))
print("AUC Score: {}".format(metrics.roc_auc_score(self.y_val, y_pred)))
y_proba = best_model.predict_proba(self.x_val)
# 预测为 0 的概率
y_zero = y_proba[:, 0]
# 预测为 1 的概率
y_one = y_proba[:, 1]
print("AUC Score2: {}".format(metrics.roc_auc_score(self.y_val, y_one)))
# 得到误判率、命中率、门限
fpr, tpr, thresholds = metrics.roc_curve(self.y_val, y_one)
# 计算auc
roc_auc = metrics.auc(fpr, tpr)
# 对ROC曲线图正常显示做的参数设定
# 用来正常显示中文标签, 上面设置过
# plt.rcParams['font.sans-serif'] = ['SimHei']
# 用来正常显示负号
plt.rcParams['axes.unicode_minus'] = False
plt.plot(fpr, tpr, label='{0}_AUC = {1:.5f}'.format("xgboost", roc_auc))
plt.title('ROC曲线')
plt.xlim([-0.05, 1.05])
plt.ylim([-0.05, 1.05])
plt.legend(loc='lower right')
plt.plot([0, 1], [0, 1], 'r--')
plt.ylabel('命中率: TPR')
plt.xlabel('误判率: FPR')
plt.show()
pass
if __name__ == "__main__":
demo = XGBoostDemo()
# demo.best_estimators_depth()
# demo.best_lr_gamma()
# demo.best_subsmaple_bytree()
# demo.best_nthread_weight()
# demo.best_seek()
demo.best_param_xgboost()
pass
在代码中,我们不难发现,xgboost 利用 二阶求导,迭代次数明显下降了很多,而且,精度也提高了。xgboost,在目前的这集成学习里面,算是比较顶级的方法。所以,在生产当中,如果需要用到机器学习中的集成学习方法,我们一般优先选择 xgboost 算法来对业务进行实现。
在接下来下一章,我将用实例,讲解现实生产中,用xgboost 进行 训练和预测。
上一章:机器篇——集成学习(六) 细说 GBDT 算法
下一章:机器篇——集成学习(八) 细说 ball49_pred 项目(彩票预测)