1.问题定义
1.1 项目概述
该项目通过对于德国 Rossmann 上千家药妆店三年的销售数据对未来的日销售数据提供预测。该项目需要对多个数据集进行处理,并选取合适的机器模型进行预测,以及选取项目要求的指标对预测错误以及结果水平进行测评。该项目还需要上传到 Kaggle,评分需要符合该项目的要求。
1.2 问题陈述
该问题是一个基于时间序列的预测问题。需要通过数据的清理,特征的筛选,使用机器学习模型针对三年中 1000 个药妆店的超过一百万条数据的日销售数据,对于未来 6 周的销售数据进行预测。
由于计算资源的有限性,无法在预测阶段使用 Cross validation 和 GridSearch, 本问题的解决更加集中在数据的清理、筛选及新特征的创建。
1.3 评价指标
通过已知的 4 万条用来测试的数据,以 RMSPE(ROOT MEAN SQUARE PERCENTAGE
ERROR)作为评价指标评定预测结果。
一方面,由于各日及商店的销售基数极为不同,因此,通过百分比而不是数值可以更加准确的评价预测结果。同时,对误差百分比取平方可以加大对大误差百分比预测的惩罚,进而有效评价模型预测结果的稳定性和后期的模型优化。再开方会使评价指标与真实值在同一值水平,更有利于理解。
2.分析
2.1 数据的探索
2.1.1 数据类型,条目,及数据缺失如下:
2.1.2 非数字特征
StateHoliday, StoreType, Assortment, PromoInterval 为 object,可以按需要使用 one hot encoding 或 label_encoder 进行数字化处理。
2.1.3 数据缺失
根据 info()中体现的信息,Test-Open,Store-CompetitionDistance 有少量数据缺失。
CompetitionOpenSinceMonth, CompetitionOpenSinceYear,
Promo2SinceWeek,Promo2SinceYear,PromoInterval 有大量数据缺失,需要进行 imputation。
2.1.4 训练集与测试集中的特征,测试集中多了 ID, 而少了 Customers。
2.2 探索性可视化
2.2.1 销售数据
销售数据并无缺失的情况下,存在接近于 20%的 0 数据
• 边缘数据展示
2.2.2 特征相关性
在清楚销售额为零的数据条目后,从相关性图标发现是由于店铺关门造成。通过显示删除销售额为零的数据条目后,剩下的数据条目 Open 全部为非零,验证该发现。如下:
因此,Open 特征在清楚销售为 0 的数据之后也可以删除。
2.2.3 数字类特征的数据分布
2.3 算法和技术
XGBoost 作为一个通用的 ensemble learning 的机器学习算法,适用于各种分类及预测场景。他通过将多个弱算法结合,调整若算法的权重,减少了 bias,从而优化了学习正确
率。他复杂度的可调节性,算法运行的并行及速度,相比于 random tree, 及 adaboost 同样具有优势。
相比于 Gradiant Boost 通过忽略错误的权重使用极微小的改变来避免 high Bias,
XGBoost 增加的正则化的部分,通过 lambda 来控制树的复杂度。
并且在 L2 regularization 之外,细化出了叶的部分。
尽管其相比于 lightgbm 和 catboost,运算速度和有潜在 high bias 的风险,在本例中大部分为数字的数据集的情况下,正确率方面仍会有微小优势。
本项目所涉及该算法的相关参数如下:
• eta [default=0.3, alias: learning_rate]通过将其逐渐调小,及调节每一轮特征权重的节奏更精细和保守提高准确率。
• max_depth [default=6] 通过逐渐增大而增加模型的复杂度以平衡和调节准确率和过拟合之间的关系。
• subsample [default=1] 选取随机数据的比例来避免过拟合。
• colsample_bytree 随机选取特征的比例来创建书。
• 本项目通过 feature importance 对特征进行了再挑选,对于结果进行了再优化。
2.4 基准模型
本项目选取 RandomForest 作为基准模型。一方面,random Forest 与 GX boost 都是 Tree base 的算法,并且同样基于 ensemble,有较高的参考性。本项目中的应用细节及结果如下:
3.方法
3.1 数据预处理
3.1.1 对于非数字数据进行处理
3.1.2 将为零的销售数据进行删除
3.1.3 根据之前的分析,删除不相关特征
填充缺失数据
CompetitionDistance 缺失数极少,不影响判断结果,用中位数填充。
CompetitionOpenSinceMonth, CompetitionOpenSinceYear 单纯数据缺失,用零代表没有进行填充。
Promo2SinceWeek,Promo2SinceYear, PromoInterval 的缺失通过相关性 heatmap 看出他们完全由完全 Promo2 的开展与否决定。用零代表没有进行填充。
准备测试数据使测试集与训练集的特征相同。
为训练模型准备训练集
数据分布变换尝试对于 skew 的数据取 log 如下,之后由 log 变换为 log1p 用以避免值为 0 而报错。
目的是将 2.2.3 中的特征的数据分布变为更加正态的分布。
由于变换后预测结果和错误有所增加而舍弃。
3.2 执行过程
3.2.1 对于评价指标的实现
3.2.2 对于 xgboost 特性,如自动处理缺失数据,在项目初期进行了效果的尝试。
尝试使用由缺失数据的训练集训练模型,从而使用 XGBboost 默认自动处理缺失数据的效果,同时尝试比较 linear 和 squarederror 对结果的影响。
3.3 完善
3.3.1 新特征的生成及运用生成并使用了如下新特征
生成并舍弃了如下特征
最终使用的特征汇总如下:
[‘Store’, ‘DayOfWeek’, ‘Promo’, ‘StateHoliday’, ‘SchoolHoliday’, ‘Year’,
‘Month’, ‘Day’, ‘WeekOfYear’, ‘StoreType’, ‘Assortment’, ‘CompetitionDistance’, ‘CompetitionOpenSinceMonth’,
‘CompetitionOpenSinceYear’, ‘Promo2’, ‘Promo2SinceWeek’,
‘Promo2SinceYear’, ‘CompetitionPeriod’, ‘PromoPeriod’, ‘IsPromoMonth’]
3.3.2 销售数据中边缘数据的剔除,尝试通过 Tukey 异常值定义法,将异常阶定义为 1.5 倍的四分位距(interquartile range,IQR)。
• 将指定特征的 25th 分位点的值分配给 Q1 。使用 np.percentile 来完成这个功能。
• 将指定特征的 75th 分位点的值分配给 Q3 。同样的,使用 np.percentile 来完成这个功能。
• 将指定特征的异常阶的计算结果赋值给 step。
• 选择性地通过将索引添加到 outliers 列表中,以移除异常值。
log_data[~((log_data[feature] >= Q1 - step) & (log_data[feature] <= Q3 + step))])
然而,使用此种方法没有异常数据。将系数改为 1.4 倍的四分位距后数据太多,影响了预测效果。因此,在最后使用了手动的方式进行了 outlier 的剔除。
3.3.3 通过 feature importance 不断的筛选替换新特征。
例如, 当删除最后位特征时,观察预测结果,进而确定其去留。
取消最后两项后
data_train_all.drop([‘PromoInterval2’,‘PromoInterval3’],axis=1,inplace=True)
,预测值得到优化。
将 PromoInterval 使用 Onehot coding 划分为了 PromoInterval0,
PromoInterval1,PromoInterval2,PromoInterval3. 在使用了新生成的四项进行预测后,feature_importance 中 PromoInterval2,PromoInterval3 的 f_score 最低,将其删除后,训练预测结果有所优化。
之后结合 train,store 生成新特征’IsPromoMonth’后,由于相关性,移除 PromoInterval0, PromoInterval1 进一步提高了预测正确率。
Competition 和 promo 的值只有开始时间(月,年),Date 并没有直接参与模型训练,将两者结合产生的 Competition period 和 promo period 具有全新的数据分布,且与其它特征无相关性,极大的提高了预测准确率。
3.3.4 参数的优化
对于以下参数进行了重点调试。
Max_depth, subsample, colsample_bytree, min_child_weight
数据集将 Date 直接转化为特征’Year’ ‘Month’, 'Day’参与训练,并未按照时间划分而是通过如下命令直接划分。
X_train, X_valid, y_train, y_valid = train_test_split(data_train_all_x,data_train_all_y,test_size=0.1,random_state=1)
验证集通过 XGBoost 自带的 eval list 参数,将 10%的训练数据进行 DMatrix 数据变换后应用于训练。并且会由验证结果是否改善的结果激活 early_stopping_rounds=250 的运行。
dtest = xgboost.DMatrix(X_valid, np.log(y_valid+1))
evallist = [(dtrain, ‘train’), (dtest, ‘test’)]
xgboost.train(plst, dtrain, num_round, evallist, feval=rmspe_xg, verbose_eval=250, early_stopping_rounds=250)
由于计算资源有限没有使用 gridsearch 而是在上述参数间使用了手动的方式进行调优,下附调优参数时所生成的一部分预测结果。
参数名 调式参数值 最优参数值
max_depth: 6,8,10,11,12,
13 12
min_child_weight:8, 0,4,6,8, 6
Gamma 0,2 0
subsample:0.8 1,0.9,0.8,0.7 0.8
colsample_bytree 1,0.9,0.8,0.7,0.6 0.7
IV. 结果
4.1 模型的评价与验证
该模型在一方面通过 max_depth, min_child_weight 的应用直接控制和增加了复杂度。另一方面通过 subsample and colsample_bytree 增加了随机性,使其对于噪音更加的健全。
得出的最优参数组合如下,尤其在增加了 min_child_weight 之后,明显的改善了预测错误。
模型的健全性通过 subsample and colsample_bytree进行弥补。
在最终阶段将训练数据集按照四六分成进行结果验证,结果并无偏差。
4.2 合理性分析
该模型生成了 1115 个 store,6 周共 41088 条销售预测。其以 RMSPE 为为衡量指标
达到预期并按预期高于基准模型
V. 项目结论
5.1 结果可视化
该结果与使用特征的重要性关系如下,在使用清洁数据的前提下Xgboost优先0.34.
5.2 对项目的思考
• 通过该项目将数据的分析,清理,整合,模型的选择比较有了较深刻的印象。
• 从本项目中体现出了数学算法的基础对于机器学习的重要性,例如实现和使用
RMSPE。希望在本领域深造就需要尽可能的补全相关知识。
• 对于本项目中所需要的特征的整合、新特征的生成的能力,体现了商业的理解和直觉的重要性。数据分析需要有更多的商业经验和项目经验,才能够更好的生成对现存特征有较低的相关性且数据 pattern 新颖的特征。这也是提高模型计算结果的关键。
5.3 需要作出的改进
对于 Gamma,Alpha 参数对于模型的影响和效果在计算资源允许的情况下有待于实验应用。
实现将最后六周数据保留用来检测 RMSPE 指标下错误纠正的权重。来源于:
(from Gert https://www.kaggle.com/c/rossmann-store-sales/discussion/18024)
参考文献
【1】 XGBoost https://xgboost.readthedocs.io/en/latest/tutorials/model.html
【2】 Kaggle Notebook https://www.kaggle.com/elenapetrova/time-series-analysis-and-forecastswith-prophet
【3】 Kaggle Notebook https://www.kaggle.com/omarelgabry/a-journey-through-rossmannstores
【4】 Kaggle Notebook https://www.kaggle.com/danspace/rossmann-store-sales-xgboost
【5】 Kaggle discussion https://www.kaggle.com/c/rossmann-store-sales/discussion/18024 【6】 Kaggle discussion https://www.kaggle.com/c/rossmann-store-sales/discussion/17979