xgboost是Boosting算法的其中一种,Boosting算法的思想是许多弱分类器集成在一起,形成一个强分类器。以为xgboost是一种提升树模型,所以他是将许多树模型集成在一起,形成一个很强的分类器。而所用到的树模型则是cart回归树模型。
xgboost算法可以给预测模型带来能力的提升。当我们对其表现有很多了解的时候,我们会发现他们有如下优势:
实际上,xgboost是以“正则化提升技术”而闻名。xgboost在代价函数里加入了正则化项,用于控制模型的复杂度。
正则化项里包含了:
xgboost工具支持并行。众所周知,Boosting算法是串行结构的处理方式,也就是说按照这种顺序的处理是没有办法实现并行处理的。这里要格外的注意xgboost的并行处理,并不是并行的建树。xgboost也是一次迭代完才能进行下一次迭代的(第t次迭代的代价函数里包含)。xgboost的并行是在特征粒度,也就是说每一棵树的构造依然是依赖于前一棵树
决策树的整个建树过程,最耗时耗资源的一个步骤就是对特征值得排序(因为要确定最佳分割点),xgboost的并行处理过程如下:
xgboost支持用户自定义目标函数和评估函数,只要目标函数二阶可导就行了。它对模型增加了一个全新的维度,所以我们的处理不会受到任何限制。
在工程实际问题的优化设计中,所列的目标函数往往很复杂,为了使问题简化,常常将目标函数在某点邻域展开成泰勒多项式来逼近原函数。
实际上,xgboost使用泰勒二阶展开的目的是,为了自定义Loss,如果按照最小二乘法的损失函数直接推导,同样能够得到陈大佬最终的推导式子:
二阶泰勒展开实际上不是最小二乘法,平方损失函数的二阶泰勒展开 = 最小二乘,但是陈大佬之所以使用泰勒展开,就是为了xgboost库的可扩展性,因为任何Loss函数只要二阶可导,就可以重复使用,关于最小二乘法的任何推导,而且泰勒展开的本质就是尽量去模仿一个函数,而二阶泰勒展开已经足够去近似大量的Loss函数了,最经典还有基于分类的对数似然Loss函数,这样的话,同样的一套代码就可以完成分类或回归了,而不至于每次都要重新推导一遍。
对于特征值有缺失的样本,xgboost可以自动学习出他的分裂方向。xgboost内置处理缺失值的规则。用户需要提供一个和其他样本不同的值,然后把它作为一个参数传入,以此来作为缺失值的取值。xgboost在不同节点遇到缺失值时采用不同的处理方式,并且会学习未来遇到缺失值时的处理方法。
xgboost先从顶到底建立所有可以建立的子树,再从底到顶的反向机芯剪枝,比起lightGBM,这样不容易陷入局部最优解。【后剪枝技术:去掉某个子树或用叶子节点代替某个子树时,对模型整体的能力影响很小】
xgboost允许在每一轮Boosting迭代中使用交叉验证,因此可以方便的获得最优Boosting迭代次数,而GBDT使用网格搜索,只能检测有限个值。
安装有两种方式:
pip install xgboost-0.81-cp37-cp37m-win_amd64.whl
注意:多线程版本安装比较复杂,可以参考一些专业教程进行安装
xgboost可以加载多种数据格式的训练数据
名称 | 说明 |
---|---|
libsvm | 格式的文本数据 |
Numpy | 二维数组 |
xgboost | 算法自带的二进制的缓存文件。剪枝的数据存储在对象DMatrix中。 |
dtrain1 = xgb.DMatrix('train.svm.txt')
dtrain2 = xgb.DMatrix('train.svm.buffer')
data = np.random.rand(5,10) # 5行10列数据集
label = np.random.randint(2,size=5) # 二分类目标值
dtrain = xgb.DMatrix(data,label=label) # 组成训练集
csr = scipy.sparse.csr_matrix((dat,(row,col)))
dtrain = xgb.DMatrix( csr )
dtrain = xgb.DMatrix('train.svm.txt')
dtrain.save_binary("train.buffer")
dtrain = xgb.DMatrix( data, label=label, missing = -999.0)
w = np.random.rand(5,1)
dtrain = xgb.DMatrix( data, label=label, missing = -999.0, weight=w)
xgboost使用
# xgboost模型
params = {
'booster':'gbtree',
'objective':'multi:softmax', # 多分类问题
'num_class':10, # 类别数,与multi softmax并用
'gamma':0.1, # 用于控制是否后剪枝的参数,越大越保守,一般0.1 0.2的样子
'max_depth':12, # 构建树的深度,越大越容易过拟合
'lambda':2, # 控制模型复杂度的权重值的L2 正则化项参数,参数越大,模型越不容易过拟合
'subsample':0.7, # 随机采样训练样本
'colsample_bytree':3,# 这个参数默认为1,是每个叶子里面h的和至少是多少
# 对于正负样本不均衡时的0-1分类而言,假设h在0.01附近,min_child_weight为1
#意味着叶子节点中最少需要包含100个样本。这个参数非常影响结果,
# 控制叶子节点中二阶导的和的最小值,该参数值越小,越容易过拟合
'silent':0, # 设置成1 则没有运行信息输入,最好是设置成0
'scale_pos_weight':1 # 正负样本比例
'eta':0.007, # 如同学习率
'seed':1000,
'nthread':7, #CPU线程数
#'eval_metric':'auc'
}
general parameters
、booster parameters
和task parameters
:这个参数是用来控制理想的优化目标和每一步结果的度量方法
xgboost.train(params,
dtrain,
num_boost_round=10,
evals(),
obj=None,
feval=None,
maximize=False,
early_stopping_rounds=None,
evals_result=None,
verbose_eval=True,
learning_rates=None,
xgb_model=None)
参数解析:
params = {'booster':'gbtree','eta':0.1}
evals = [(dtrain,'train'),(dval:'val')]
或者是evals = [(dtrain,'train')]
,对于第一种情况,它使得我们可以在训练过程中观察验证集的效果。bst.best_score
,bst.best_iteration
和bst.best_ntree_limit
有了参数列表和数据就可以训练模型了
num_round = 10
bst = xgb.train( plst, dtrain, num_round, evallist )
模型已经训练好了,可以利用已经训练好的模型对测试集进行预测
# X_test类型可以是二维List,也可以是numpy的数组
dtest = DMatrix(X_test)
y_pred = model.predict(dtest)
模型训练好了,一直在内存中毕竟是不安全的,持久化到磁盘才是王道。并且我们也很好奇模型到底长什么样子
bst.save_model('test.model')
导出模型和特征映射(Map),之后就可以查看.txt文件,来满足自己的好奇心了(查看模型结构和意义)
# 导出模型到文件
bst.dump_model('dump.raw.txt')
# 导出模型和特征映射
bst.dump_model('dump.raw.txt','featmap.txt')
保存模型是为了后面可以方便使用,所以加载模型会为我们提供便利
bst = xgb.Booster({'nthread':4}) # init model
bst.load_model("model.bin") # load data
注意:在加载模型的时候需要先初始化一个“空壳”这个类似TensorFlow中的操作
xgboost有俩大类接口:xgboost原生接口和sklearn接口,並且xgboost能夠實現分類回歸兩種任務,下面對4中情況作出解析
数据使用的sklearn中自带的iris数据集
注意:如果开启多线程,使用MacOS系统,要配置环境变量os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE"
,不然系统会不支持调用多线程,在Windows系统使用什么情况不太清楚,有不同的意见可留言讨论!
from sklearn.datasets import load_iris
import xgboost as xgb
from xgboost import plot_importance
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score # 准确率
import os
os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE"
# 记载样本数据集
iris = load_iris()
X,y = iris.data,iris.target
# 数据集分割
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.2,random_state=22)
# 算法参数
params = {
'booster':'gbtree',
'objective':'multi:softmax',
'num_class':3,
'gamma':0.1,
'silent': 1, # 关闭掉运行时打印信息
'max_depth':6,
'lambda':2,
'subsample':0.7,
'colsample_bytree':0.7,
'min_child_weight':3,
'slient':1,
'eta':0.1,
'seed':1000,
'nthread':4,
}
# 构造参数
plst = params.items()
# 生成数据集格式,xgboost的数据需要转换一下
dtrain = xgb.DMatrix(X_train,y_train)
num_rounds = 500
# xgboost模型训练
model = xgb.train(plst,dtrain,num_rounds,verbose_eval=100)
# 对测试集进行预测
dtest = xgb.DMatrix(X_test)
y_pred = model.predict(dtest)
# 计算准确率
accuracy = accuracy_score(y_test,y_pred)
print('accuarcy:%.2f%%'%(accuracy*100))
# 显示重要特征
plot_importance(model)
plt.show()
经过500次迭代,测试集计算的accuracy = 93.34%,结果算是很高的了
数据集使用的sklearn中的Boston数据集
import xgboost as xgb
from xgboost import plot_importance
from matplotlib import pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_boston
from sklearn.metrics import mean_squared_error
import os
os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE"
# 加载数据集,此数据集时做回归的
boston = load_boston()
X,y = boston.data,boston.target
# Xgboost训练过程
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.2,random_state=22)
# 算法参数
params = {
'booster':'gbtree',
'objective':'reg:gamma',
'gamma':0.01,
'max_depth':6,
'silent': 1,
'lambda':3,
'subsample':0.8,
'colsample_bytree':0.8,
'min_child_weight':3,
'slient':1,
'eta':0.1,
'seed':1000,
'nthread':4,
}
dtrain = xgb.DMatrix(X_train,y_train)
num_rounds = 800
plst = params.items()
model = xgb.train(plst,dtrain,num_rounds)
# 对测试集进行预测
dtest = xgb.DMatrix(X_test)
y_pred = model.predict(dtest)
# 计算mse
mse= mean_squared_error(y_true=y_test,y_pred=y_pred)
print('mse:',mse)
# 显示重要特征
plot_importance(model)
plt.show()
先熟悉一下sklearn接口中模型初始化参数都有哪些
from xgboost.sklearn import XGBClassifier
clf = XGBClassifier(
silent=0, # 设置成1则没有运行信息输出,最好是设置为0,是否在运行升级时打印消息
nthread = 4 # CPU 线程数 默认最大
learning_rate=0.3 , # 如同学习率
min_child_weight = 1,
# 这个参数默认为1,是每个叶子里面h的和至少是多少,对正负样本不均衡时的0-1分类而言
# 假设h在0.01附近,min_child_weight为1 意味着叶子节点中最少需要包含100个样本
# 这个参数非常影响结果,控制叶子节点中二阶导的和的最小值,该参数值越小,越容易过拟合
max_depth=6, # 构建树的深度,越大越容易过拟合
gamma = 0,# 树的叶子节点上做进一步分区所需的最小损失减少,越大越保守,一般0.1 0.2这样子
subsample=1, # 随机采样训练样本,训练实例的子采样比
max_delta_step=0, # 最大增量步长,我们允许每个树的权重估计
colsample_bytree=1, # 生成树时进行的列采样
reg_lambda=1, #控制模型复杂度的权重值的L2正则化项参数,参数越大,模型越不容易过拟合
# reg_alpha=0, # L1正则项参数
# scale_pos_weight =1 # 如果取值大于0的话,在类别样本不平衡的情况下有助于快速收敛,平衡正负权重
# objective = 'multi:softmax', # 多分类问题,指定学习任务和响应的学习目标
# num_class = 10, # 类别数,多分类与multisoftmax并用
n_estimators=100, # 树的个数
seed = 1000, # 随机种子
# eval_metric ='auc'
)
下面使用sklearn接口下的xgboost帮助我们处理分类问题
iris数据集算是多分类,注意一下使用的目标函数objective='multi:softmax’
from sklearn.datasets import load_iris
import xgboost as xgb
from xgboost import plot_importance
from matplotlib import pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
# 加载样本数据集
iris = load_iris()
X,y = iris.data,iris.target
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.2,random_state=12343)
# 训练模型
model = xgb.XGBClassifier(max_depth=5,learning_rate=0.1,n_estimators=160,silent=True,objective='multi:softmax')
model.fit(X_train,y_train)
# 对测试集进行预测
y_pred = model.predict(X_test)
#计算准确率
accuracy = accuracy_score(y_test,y_pred)
print('accuracy:%2.f%%'%(accuracy*100))
# 显示重要特征
plot_importance(model)
plt.show()
依然使用Boston数据集进行回归预测
认真的同学可能发现了,使用sklearn的接口用起来方便一些,目前可以看到的至少不用单独构建数据了,接口已经辅助构造了
import xgboost as xgb
from xgboost import plot_importance
from matplotlib import pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_boston
from sklearn.metrics import mean_squared_error
import os
os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE"
# 导入数据集
boston = load_boston()
X ,y = boston.data,boston.target
# Xgboost训练过程
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.2,random_state=0)
model = xgb.XGBRegressor(max_depth=5,learning_rate=0.1,n_estimators=160,silent=True,objective='reg:gamma')
model.fit(X_train,y_train)
# 对测试集进行预测
y_pred = model.predict(X_test)
# 计算mse
mse= mean_squared_error(y_true=y_test,y_pred=y_pred)
print('mse:',mse)
# 显示重要特征
plot_importance(model)
plt.show()
为了确定Boosting参数,我们要先给其他参数一个初始值。咱们先按照如下方法取值:
我们先对这两个参数调优,是因为他们对最终结果有很大的影响。首先。我们先大范围的粗略设置参数,然后在小范围的调整
注意:在这一个环节我会进行高负荷的网格搜索调参,这个过程会很久,也行十几分钟、几十分钟、甚至更久,具体的情况取决于你的系统性能。
在已经调整好的其他参数的基础上,我们可以进行gamma参数的调优了。Gamma参数取值范围很大,这里简单的设置为5,当然也可取得更加精细些,比如:
# 数据包
from sklearn.model_selection import GridSearchCV
# 设置待测参数
param_test3 = {
'gamma':[i/10.0 for i in range(0,5)]
}
# 初始化CV模型
gsearch = GridSearchCV(estimator = XGBClassifier( learning_rate =0.1,
n_estimators=140,
max_depth=4,
min_child_weight=6,
gamma=0,
subsample=0.8,
colsample_bytree=0.8,
objective= 'binary:logistic',
nthread=4,
scale_pos_weight=1,
seed=22
),
param_grid = param_test3,
scoring='roc_auc',
n_jobs=4,
iid=False,
cv=5)
# 开启CV
gsearch.fit(train[predictors],train[target])
# 找到每个CV分数,最好的参数,最好的分数
gsearch.grid_scores_,
gsearch.best_params_,
gsearch.best_score_
尝试不同的subsample和colsample_bytree参数。可以分成两个阶段进行这个步骤。这两个步骤都取0.6、0.7、0.8、0.9作为起始值。
由于gamma函数提供了一种更加有效的降低过拟合的方法,大部分人很少会用到这个参数,但是我们可以尝试调整一下这个参数。
最后,我使用较低的学习速率,以及使用更多的决策树,我们可以用xgboost中CV函数来进行这一步工作。
**要想模型的表现有大幅度的提升,调整每个参数带来的影响也必须清楚,仅仅靠着参数的调整和模型的小幅度优化,想要让模型的表现有个大幅度提升是不可能的。想要模型的表现有质的飞跃,需要依靠其他的手段。没错的,是【数据】。特征工程、模型融合、模型堆叠也许效果更明显。
feature_importances_
得到。如下可以展示在控制台:print(model.feature_importances_)
# plot
pyplot.bar(range(len(model.feature_importances_)), model.feature_importances_)
pyplot.show()
# plot feature importance manually
from numpy import loadtxt
from xgboost import XGBClassifier
from matplotlib import pyplot
from sklearn.datasets import load_iris
# load data
dataset = load_iris()
# split data into X and y
X = dataset.data
y = dataset.target
# fit model no training data
model = XGBClassifier()
model.fit(X, y)
# feature importance
print(model.feature_importances_)
# plot
pyplot.bar(range(len(model.feature_importances_)), model.feature_importances_)
pyplot.show()
"""
[0.17941953 0.11345647 0.41556728 0.29155672]
"""
这种绘图的方式并不是很完美,只是显示了特征重要性而没有排序,可以在进行绘图之前对特征重要性进行排序
下面就通过内置的绘图函数,进行特征重要性得分排序后的绘制,这个函数就是plot_importance(),如下所示:
# plot feature importance manually
from numpy import loadtxt
from xgboost import XGBClassifier
from matplotlib import pyplot
from sklearn.datasets import load_iris
from xgboost import plot_importance
# load data
dataset = load_iris()
# split data into X and y
X = dataset.data
y = dataset.target
# fit model no training data
model = XGBClassifier()
model.fit(X, y)
# feature importance
print(model.feature_importances_)
# plot feature importance
plot_importance(model)
pyplot.show()
"""
[0.17941953 0.11345647 0.41556728 0.29155672]
"""
根据特征在输入数组的索引,特征被自动命名为f0 - f3,在问题描述中手动的将这些索引映射到名称,我们可以看到,f2具有最高的重要性,f1具有最低的重要性。
# 使用阈值进行选择特征
selection = SelectFromModel(model, threshold=thresh, prefit=True)
select_X_train = selection.transform(X_train)
# 训练模型
selection_model = XGBClassifier()
selection_model.fit(select_X_train, y_train)
# 评估模型
select_X_test = selection.transform(X_test)
y_pred = selection_model.predict(select_X_test)
我们可以通过测试多个阈值,来从特征重要性中选择特征。具体而言,每个输入变量的特征重要度,本质上允许我们通过重要性来测试每个特征子集。
下面是完整代码:
# 绘制特征重要性得分
import numpy as np
from xgboost import XGBClassifier
from matplotlib import pyplot
from sklearn.datasets import load_iris
from xgboost import plot_importance
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.feature_selection import SelectFromModel
# 加载数据
dataset = load_iris()
# 切分数据,样本&标签
X = dataset.data
y = dataset.target
# 切分训练集测试集
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.33,random_state=7)
# 训练一个xgboost分类器
model = XGBClassifier()
model.fit(X_train, y_train)
# 获得特征重要性
print(model.feature_importances_)
# 对测试数据做出预测,并进行评估
y_pred = model.predict(X_test)
predictions = [round(value) for value in y_pred]
accuracy = accuracy_score(y_test,predictions)
print("Accuracy:%.2f%%"%(accuracy*100.0))
# 训练模型,使用每个特征重要性作为阈值
# 特征重要性得分排序
thresholds = np.sort(model.feature_importances_)
for thresh in thresholds:
# 遍历所有的特征重要性得分,把每个得分作为阈值进行特征选择
selection = SelectFromModel(model,threshold=thresh,prefit=True )
# 构建新的训练集
select_X_train = selection.transform(X_train)
# 使用选择出的特征作为训练集,重新训练一个模型出来
selection_model = XGBClassifier()
selection_model.fit(select_X_train, y_train)
# 对测试集进行transform()操作
select_X_test = selection.transform(X_test)
# 预测,评估
y_pred = selection_model.predict(select_X_test)
predictions = [round(value) for value in y_pred]
accuracy = accuracy_score(y_test,predictions)
print("Thresh=%.3f, n=%d, Accuracy: %.2f%%" % (thresh, select_X_train.shape[1], accuracy * 100.0))
"""
[0.20993228 0.09029345 0.54176074 0.15801354]
Accuracy:92.00%
Thresh=0.090, n=4, Accuracy: 92.00%
Thresh=0.158, n=3, Accuracy: 92.00%
Thresh=0.210, n=2, Accuracy: 86.00%
Thresh=0.542, n=1, Accuracy: 90.00%
"""
我们可以看到,模型的性能通常随着所选择的特征的数量减少,在这个问题上,可以对测试集准确率和模型复杂度做一个权衡。例如,选择3个特征,接受准确率为92%,这样可能是对这样一个小数据集的清洗,但是对于更大的数据集,使用交叉验证作为模型评估方案可能是更有用的策略。