文章链接
模型原理
Xgboost(Extreme Gradient Boost)模型,是一种特殊的梯度提升决策树(GBDT,Gradient Boosting Decision Tree),只不过是力求将速度和效率发挥到了极致,故叫X(Extreme)gradientboost。Xgboost其本质上还是基于树结构并结合集成学习的一种方法,其基础树结构为分类回归树(CART,Classification and Regression Tree)。类似于局部加权线性回归算法,基于树的回归算法也是一类局部的回归算法,通过将数据集切分成多份,在每一份数据上单独建模。但不同的是基于树的回归算法是一种基于参数的学习算法,利用训练数据训练完模型后,参数一旦确定,无需再改变。分类回归树是一种基于决策树的结构,既可以用于解决分类问题也可以用于解决回归问题,是国际权威的学术组织The IEEE International Conference on DataMining (ICDM)早前评选出了数据挖掘领域的十大经典算法之一。
CART算法核心内容包含以下三个方面:
- 二分(Binary Split):在每次判断过程中,都是对观察变量进行二分。CART算法采用一种二分递归分割的技术,算法总是将当前样本集分割为两个子样本集,使得生成的决策树的每个非叶结点都只有两个分枝。因此CART算法生成的决策树是结构简洁的二叉树;
- 单变量分割(Split Based on One Variable):每次最优划分都是针对单个变量;
- 剪枝策略:CART算法的关键点,也是所有基于树(Tree-Based)算法的关键步骤
决策树的生成就是递归地构建二叉决策树的过程,核心思想为在训练数据集所在的输入空间中,递归地将每个区域划分为两个子区域并决定每个子区域上输出值。划分子区域的标准取决于树的种类,对回归树用平方误差最小化准则,对分类树用基尼指数最小化准则。回归树的生成具体步骤如下:
- 选择最优切分变量j与切分点s,求解
遍历变量j,对固定的切分变量j扫描切分点s,选择使上式最小值的对$(j; s)$。其中$R_m$是被划分的输入空间,$c_m$是空间$R_m$对应的输出值
- 用选定的对$(j; s)$划分区域并决定相应的输出值
- 继续对两个子区域递归的调用上述步骤,最终将输入空间划分为M个区域$R_1,R_2,…,R_m$,生成决策树
- 当输入空间划分确定时,可以用平方误差来衡量回归树对于训练数据的拟合程度,用平方误差最小的准则不断地递归划分子树,直到平方误差满足需求
单棵分类回归树精度有限,应用场景受限,故Xgboost在CART的基础上引入了集成学习(boosting方法),并采用并行计算等方式极大的加速了模型计算速度。Boosting的核心思想就是所有弱分类器的结果相加等于预测值,然后下一个弱分类器去拟合误差函数对预测值的梯度/残差(这个梯度/残差就是预测值与真实值之间的误差),从而不断地减小残差,直到满足系统的误差要求(如图所示)
模型安装
pip install xgboost
模型实现
def xgboost_predict(train_data, evaluation_data, forecast_cnt=365, freq="D", importance_fig=False, model_dir=""):
"""
predict time series with XGBoost library which is based on Gradient Boost and CART(classification and regression tree)
:param train_data: data use to train the model
:param evaluation_data: data use to evaluate the model
:param forecast_cnt: how many point needed to be predicted
:param freq: the interval between time index
:param importance_fig: whether plot importance of each feature
:param model_dir: directory of pre-trained model(checkpoint, params)
:return:
"""
def create_features(df, label=None):
"""
Creates time series features from datetime index
"""
df['date'] = df.index
df['hour'] = df['date'].dt.hour
df['dayofweek'] = df['date'].dt.dayofweek
df['quarter'] = df['date'].dt.quarter
df['month'] = df['date'].dt.month
df['year'] = df['date'].dt.year
df['dayofyear'] = df['date'].dt.dayofyear
df['dayofmonth'] = df['date'].dt.day
df['weekofyear'] = df['date'].dt.weekofyear
X = df[['hour', 'dayofweek', 'quarter', 'month', 'year',
'dayofyear', 'dayofmonth', 'weekofyear']]
if label:
y = df[label]
return X, y
return X
model_directory = "./model/XGBoost_%s" % now()
params = {
}
# if there is a pre-trained model, use parameters from it
if model_dir:
model_directory = model_dir
latest_date = evaluation_data["ds"].tolist()[-1]
# set index with datetime
train_data = train_data.set_index(pd.DatetimeIndex(train_data["ds"]))
evaluation_data = evaluation_data.set_index(pd.DatetimeIndex(evaluation_data["ds"]))
forecast_data = pd.DataFrame.from_dict({
"ds": generate_time_series(start_date=latest_date, cnt=forecast_cnt, delta=delta_dict[freq])
})
forecast_data = forecast_data.set_index(pd.DatetimeIndex(forecast_data["ds"]))
x_train, y_train = create_features(train_data, label='y')
x_eval, y_eval = create_features(evaluation_data, label="y")
x_forecast = create_features(forecast_data)
reg = XGBRegressor(n_estimators=1000)
if model_dir:
reg.load_model(model_directory)
else:
reg.fit(x_train, y_train,
eval_set=[(x_train, y_train), (x_eval, y_eval)],
early_stopping_rounds=50,
verbose=False) # Change verbose to True if you want to see it train
reg.save_model(model_directory)
if importance_fig:
plot_importance(reg, height=0.9)
evaluation_data["y"] = reg.predict(x_eval)
forecast_data["y"] = reg.predict(x_forecast)
return evaluation_data, forecast_data
关键参数
- early_stopping_rounds:早期停止次数,用于控制模型的迭代次数。假设为50,则模型会在训练到50次内误差基本不变的时候提前结束训练。
注意这里假设只有一个时间序列,实际情况中,可能存在多个序列与待分析的时间序列相关,可以模仿这里的写法将其拓展为多特征分析与预测(这里仅仅用到了时间这一特征)。