Professional Documents
Culture Documents
6-机器学习实战 - 综合项目 - 电商销量预估进阶方案
6-机器学习实战 - 综合项目 - 电商销量预估进阶方案
引言
1.项目概况介绍
1.1 背景介绍
Rossmann 成立于1972年,是德国最大的日化用品超市,在 7 个欧洲国家有 3000 多家商店。商店不定时会举办
短期的促销活动以及连续的促销活动以此来提高销售额。除此之外,商店的销售还受到许多因素的影响,包括促
销、竞争、学校和国家假日、季节性和周期性。
可靠的销售预测使商店经理能够创建有效的员工时间表,从而提高生产力和动力,比如更好的调整供应链和合理
的促销策略与竞争策略,具有重要的实用价值与战略意义。如果可以帮助 Rossmann 创建一个强大的预测模
型,将帮助仓库管理人员专注于对他们最重要的内容:客户和团队。
1.2 数据介绍
我们本次用到的数据集是 🏆 Kaggle 机器学习比赛 Rossmann Store Sales,数据以 家 Rossmann 连锁
商店为研究对象,从2013年1月1日到2015年7月共计录 条销售数据( 个特征)。大家可以通过
ShowMeAI 的百度网盘地址下载。
🏆 实战数据集下载(百度网盘):公众号『ShowMeAI研究中心』回复『实战』,或者点击 这里 获取本
牛博带你学AI 搜集整理
文 [33]电商销量预估 Rossmann Store Sales | Kaggle 『Rossmann Store Sales数据集』
⭐ ShowMeAI官方GitHub:https://github.com/ShowMeAI-Hub
数据集一共涵盖了四个文件:
train.csv :含有销量的历史数据。
test.csv :未含销量的历史数据。
sample_submission.csv :以正确格式提交的示例文件。
store.csv :关于每个商店的一些补充信息。
store :为对应店铺的 id 序号
DayOfWeek :代表着每周开店的天数
Data :是对应销售额 Sales 产生的日期
Sales :就是销售额的历史数据
Customers :为进店的客人数量
Open :则表示这个店铺是否开门与否
Promo :表示商店是否在当天有促销活动
StateHoliday :与 SchoolHoliday 分别表示了是否是国定假日或是学校假日
(1) 训练集
(2) 测试集
数据分布和部分示例数据如下:
牛博带你学AI 搜集整理
(3) 结果文件
(4) 商店信息
store.csv 的数据分布情况,可以注意到这里有很多离散的类别标签。
数据分布和部分示例数据如下:
其中:
Store :对应表示了店铺的编号。
StoreType :店铺的种类,一共有 a、b、c、d 四种不同种类的店铺。大家可以把它想象成快闪店,普通营
业店,旗舰店,或mini店这样我们生活中的类型。
Assortment :用 a、b、c 三种分类描述店铺内售卖产品的组合级别。例如旗舰店和mini店中组合的产品肯
定是有很大不同的。
Competition Distance 、 Competition Open Since Year 、 Competition Open Since Month :分别表示最
近的竞争对手的店铺距离,开店时间(以年计算),开店时间(以月计算)。
Promo2 :描述该店铺是否有长期的促销活动。
Promo2 Since Year 于 Promo2 Since Week :分别表示商店开始参与促销的年份和日历周。
Promo Interval :描述 promo2 开始的连续间隔,以促销重新开始的月份命名。
1.3 项目目标
在了解了这些数据后我们就需要明确一下我们的项目目的,在 Rossmanns 销售预测中,我们需要利用历史数
牛博带你学AI 搜集整理
在了解了这些数据后我们就需要明确 下我们的项目目的,在 Rossmanns 销售预测中,我们需要利用历史数
据,也就是 train.csv 中的数据进行监督学习。训练出的模型利用通test.csv中的数据进行模型推断(预测),将
预测出的数据以 sample_submission.csv 的格式提交至Kaggle进行评分。在这过程中还可以结合 store.csv 中
的补充信息加强我们模型获得数据的能力。
1.4 评估准则
模型所采纳的评估指标为 Kaggle 在竞赛中所推荐的 Root Mean Square Percentage Error (RMSPE)指标。
其中:
代表门店当天的真实销售额。
代表相对应的预测销售额。
代表样本的数量。
如果有任何一天的销售额为 ,那么将会被忽略。最后计算得到的这个RMSPE值越小代表误差就越小,相应就
会获得更高的评分。
1.5 解决方案核心板块
本篇我们的解决方案,分成以下几个板块。
Step 1: 加载数据
Step 2: 探索性数据分析
Step 3: 数据预处理(缺失值)
Step 4: 特征工程
Step 5: 基准模型与评估
Step 6: XGBoost建模
1. ## 载入必要的库
2. import pandas as pd
3. import numpy as np
4. import xgboost as xgb
5.
6. import missingno as msno
7. import seaborn as sns
8. import matplotlib as mpl
9. import matplotlib.pyplot as plt
10. %matplotlib inline
1.6 加载数据
Rossmann 场景建模数据包含很多信息维度,比如客户数量、假期等。又根据其任务目标可以判定为监督学习中
典型的回归类建模问题。我们先对加载数据再做后续分析挖掘建模。
牛博带你学AI 搜集整理
1. ## 载入数据
2. train = pd.read_csv('./rossmann-store-sales/train.csv')
3. test = pd.read_csv('./rossmann-store-sales/test.csv')
4. store = pd.read_csv('./rossmann-store-sales/store.csv')
通过 DataFrame.info() 操作可以查看DataFrame的数据基本信息(数值分布、缺失值情况等)。详细的pandas操
作也欢迎大家查看ShowMeAI的 数据分析系列教程 和 数据科学工具速查 | Pandas使用指南。
1. <class 'pandas.core.frame.DataFrame'>
2. RangeIndex: 1017209 entries, 0 to 1017208
3. Data columns (total 9 columns):
4. Store 1017209 non-null int64
5. DayOfWeek 1017209 non-null int64
6. Date 1017209 non-null object
7. Sales 1017209 non-null int64
8. Customers 1017209 non-null int64
9. Open 1017209 non-null int64
10. Promo 1017209 non-null int64
11. StateHoliday 1017209 non-null object
12. SchoolHoliday 1017209 non-null int64
13. dtypes: int64(7), object(2)
14. memory usage: 69.8+ MB
15.
16. <class 'pandas.core.frame.DataFrame'>
17. RangeIndex: 41088 entries, 0 to 41087
18. Data columns (total 8 columns):
19. Id 41088 non-null int64
20. Store 41088 non-null int64
21. DayOfWeek 41088 non-null int64
22. Date 41088 non-null object
23. Open 41077 non-null float64
24. Promo 41088 non-null int64
25. StateHoliday 41088 non-null object
26. SchoolHoliday 41088 non-null int64
27. dtypes: float64(1), int64(5), object(2)
28. memory usage: 2.5+ MB
29.
30. <class 'pandas.core.frame.DataFrame'>
31. RangeIndex: 1115 entries, 0 to 1114
32. Data columns (total 10 columns):
33. Store 1115 non-null int64
34. StoreType 1115 non-null object
35. Assortment 1115 non-null object
36. CompetitionDistance 1112 non-null float64
37. CompetitionOpenSinceMonth 761 non-null float64
38. CompetitionOpenSinceYear 761 non-null float64
39. Promo2 1115 non-null int64
40. Promo2SinceWeek 571 non-null float64
41. Promo2SinceYear 571 non-null float64
42 PromoInterval 571 non null object
牛博带你学AI 搜集整理
42. PromoInterval 571 non-null object
43. dtypes: float64(5), int64(2), object(3)
44. memory usage: 87.2+ KB
2. 探索性数据分析
我们先对目标结果sales做一点分析,先对其进行分布绘图
1. train.loc[train.Open==0].Sales.hist(align='left')
发现:当店铺关闭时,日销量必然为 。
1. fig = plt.figure(figsize=(16,6))
2.
3. ax1 = fig.add_subplot(121)
4. ax1.set_xlabel('Sales')
5. ax1.set_ylabel('Count')
6. ax1.set_title('Sales of Closed Stores')
7. plt.xlim(-1,1)
8. train.loc[train.Open==0].Sales.hist(align='left')
9.
10. ax2 = fig.add_subplot(122)
11. ax2.set_xlabel('Sales')
12. ax2.set_ylabel('PDF')
13. ax2.set_title('Sales of Open Stores')
14. sns.distplot(train.loc[train.Open!=0].Sales)
15.
16. print('The skewness of Sales is {}'.format(train.loc[train.Open!=0].Sales.skew()))
牛博带你学AI 搜集整理
去掉店铺关闭时的数据之后,重新绘制店铺开启时的日销量分布图。可以发现日销量表现为明显的有偏分布,其
偏度约为 ,远大于 ,我们会考虑对数据分布做预处理调整。
1. train = train.loc[train.Open != 0]
2. train = train.loc[train.Sales > 0].reset_index(drop=True)
3. train.shape
1. (844338, 9)
3. 缺失值处理
1. ## 训练集的缺失信息:无缺失
2. train[train.isnull().values==True]
1. ## 测试集的缺失信息
2. test[test.isnull().values==True]
下面我们看一下store的缺失情况
1. ## store 的缺失信息
2. msno.matrix(store)
牛博带你学AI 搜集整理
test.csv 与 store.csv 中都有缺失值,我们会对其进行处理,并对特征进行合并:
1. ## 默认test中的店铺全部正常营业
2. test.fillna(1,inplace=True)
3.
4. ## 对CompetitionDistance中的缺失值采用中位数进行填补
5. store.CompetitionDistance =
store.CompetitionDistance.fillna(store.CompetitionDistance.median())
6.
7. ## 对其它缺失值全部补0
8. store.fillna(0,inplace=True)
我们知道,缺失值的一些处理方法包括:
删除字段(去除包含缺失值的 columns)。
填补缺失值(填入平均值、中位数或者拟合填充等)。
标记缺失值,把缺失值标记为特殊值(比如 -999)或者新加一列标注某字段是否是缺失。
1. ## 特征合并
2. train = pd.merge(train, store, on='Store')
3. test = pd.merge(test, store, on='Store')
1. train.head(10)
牛博带你学AI 搜集整理
4. 特征工程
4.1 特征抽取函数
特征抽
牛博带你学AI 搜集整理
4.2 特征抽取
1. ## 处理Date方便特征提取
2. train.Date = pd.to_datetime(train.Date, errors='coerce')
3. test.Date = pd.to_datetime(test.Date, errors='coerce')
4.
5. ## 使用features数组储存使用的特征
6. features = []
7.
8. ## 对train与test特征提取
9. build_features(features, train)
10. build_features([], test)
11.
12. ## 打印使用的特征
13. print(features)
5. 基准模型与评估
5.1 定义评估准则函数
由于需要预测连续值,因此需要采用回归模型。由于该项目是 Kaggle 赛题,测试集是使用根均方百分比误差
(Root Mean Square Percentage Error,RMSPE)评测的,因此这里只能使用RMSPE。RMSPE的计算公式为:
其中 与 分别为第 个样本标签的真实值与预测值。
1. ## 评价函数Rmspe
2. ## 参考:https://www.kaggle.com/justdoit/xgboost-in-python-with-rmspe
3.
4. def ToWeight(y):
5. w = np.zeros(y.shape, dtype=float)
6. ind = y != 0
7. w[ind] = 1./(y[ind]**2)
8. return w
9.
10. def rmspe(yhat, y):
11. w = ToWeight(y)
12. rmspe = np.sqrt(np.mean(w * (y-yhat)**2))
13. return rmspe
14.
15. def rmspe_xg(yhat, y):
16. y = y.get_label()
17. y = np.expm1(y)
18. yhat = np.expm1(yhat)
19. w = ToWeight(y)
20. rmspe = np.sqrt(np.mean(w * (y-yhat)**2))
牛博带你学AI 搜集整理
21. return "rmspe", rmspe
22.
23. def neg_rmspe(yhat, y):
24. y = np.expm1(y)
25. yhat = np.expm1(yhat)
26. w = ToWeight(y)
27. rmspe = np.sqrt(np.mean(w * (y-yhat)**2))
28. return -rmspe
5.2 基准模型评估
我们构建回归树模型作为基础模型进行建模和评估。回归树我们直接使用 SKLearn
的 DecisionTreeRegressor 即可,搭配 折交叉验证与网格搜索进行调参,主要调节的超参数是树的最大深
度 max_depth 。
1. ## 显示最佳超参数
2. DTR.get_params()
1. {'criterion': 'mse',
2. 'max_depth': 30,
3. 'max_features': None,
4. 'max_leaf_nodes': None,
5. 'min_impurity_decrease': 0.0,
6. 'min_impurity_split': None,
7. 'min_samples_leaf': 1,
8. 'min_samples_split': 2,
9. 'min_weight_fraction_leaf': 0.0,
10. 'presort': False,
11. 'random_state': 2,
12. 'splitter': 'best'}
1. ## 生成上传文件
2. submission = pd.DataFrame({"Id": test["Id"], "Sales": np.expm1(DTR.predict(test[features]))})
3. submission.to_csv("benchmark.csv", index=False)
牛博带你学AI 搜集整理
模型在测试集上的 Public Score 为 ,Private Score为 。下面使用 XGBoost 对基准测试结果
进行提升。
6. XGBoost建模与调优
6.1 模型参数
XGBoost是比较强大的模型,可调参数较多(具体可以参考ShowMeAI文章 XGBoost建模应用详解),我们主要调
整下列的超参数:
eta :学习率。
max_depth :单颗回归树的最大深度,较小导致欠拟合,较大导致过拟合。
subsample :0-1之间,控制每棵树随机采样的比例,减小这个参数的值,算法会更加保守,避免过拟合。
但如果这个值设置得过小,可能会导致欠拟合。
colsample_bytree :0-1之间,用来控制每棵随机采样的特征的占比。
num_trees :树的棵树,也就是迭代步数。
1. ## # 默认的一版参数
2. ## params = {'objective': 'reg:linear',
3. ## 'eta': 0.01,
4. ## 'max_depth': 11,
5. ## 'subsample': 0.5,
6. ## 'colsample_bytree': 0.5,
7. ## 'silent': 1,
8. ## 'seed': 1
9. ## }
10. ## num_trees = 10000
1. ## 第二次调参,学习率过大,效果下降
2. ## params = {"objective": "reg:linear",
3. ## "booster" : "gbtree",
4. ## "eta": 0.3,
5. ## "max_depth": 10,
6. ## "subsample": 0.9,
7. ## "colsample_bytree": 0.7,
8. ## "silent": 1,
9. ## "seed": 1301
10. ## }
11. ## num_trees = 10000
1. ## 第三次调参,步长适中,收敛速度快,结果优
2. params = {"objective": "reg:linear",
3. "booster" : "gbtree",
4. "eta": 0.1,
5. "max_depth": 10,
6. "subsample": 0.85,
7. "colsample_bytree": 0.4,
8. "min_child_weight": 6,
9. "silent": 1,
10. "thread": 1,
11. "seed": 1301
12. }
1200
牛博带你学AI 搜集整理
13. num_trees = 1200
6.2 模型训练
1. ## 随机划分训练集与验证集
2. from sklearn.model_selection import train_test_split
3.
4. #X_train, X_test = train_test_split(train, test_size=0.2, random_state=2)
5. X_train, X_test = K_Fold_spilt(10,5,train)
6.
7. dtrain = xgb.DMatrix(X_train[features], np.log1p(X_train.Sales))
8. dvalid = xgb.DMatrix(X_test[features], np.log1p(X_test.Sales))
9. dtest = xgb.DMatrix(test[features])
10.
11. watchlist = [(dtrain, 'train'),(dvalid, 'eval')]
12. gbm = xgb.train(params, dtrain, num_trees, evals=watchlist, early_stopping_rounds=50,
feval=rmspe_xg, verbose_eval=False)
6.3 提交结果文件
1. ## 生成提交文件
2. test_probs = gbm.predict(xgb.DMatrix(test[features]), ntree_limit=gbm.best_ntree_limit)
3. indices = test_probs < 0
4. test_probs[indices] = 0
5. submission = pd.DataFrame({"Id": test["Id"], "Sales": np.expm1(test_probs)})
6. submission.to_csv("xgboost.csv", index=False)
6.4 特征优化
在电商类场景中,过往的历史统计特征也非常重要,我们可以通过对历史销量数据,做不同时间粒度的统计构建
统计特征作为补充信息,对于建模效果优化也有帮助,如下是一些示例:
1. sales_mean_bystore = X_train.groupby(['Store'])
['Sales'].mean().reset_index(name='MeanLogSalesByStore')
2. sales mean bystore['MeanLogSalesByStore'] = np.log1p(sales mean bystore['MeanLogSalesByStore'])
牛博带你学AI 搜集整理
2. sales_mean_bystore[ MeanLogSalesByStore ] np.log1p(sales_mean_bystore[ MeanLogSalesByStore ])
3.
4. sales_mean_bydow = X_train.groupby(['DayOfWeek'])
['Sales'].mean().reset_index(name='MeanLogSalesByDOW')
5. sales_mean_bydow['MeanLogSalesByDOW'] = np.log1p(sales_mean_bydow['MeanLogSalesByStore'])
6.
7. sales_mean_bymonth = X_train.groupby(['Month'])
['Sales'].mean().reset_index(name='MeanLogSalesByMonth')
8. sales_mean_bymonth['MeanLogSalesByMonth'] =
np.log1p(sales_mean_bymonth['MeanLogSalesByMonth'])
牛博带你学AI 搜集整理