5种用于预测销售的机器学习技术
即将开播:4月29日,民生银行郭庆谈商业银行金融科技赋能的探索与实践
预测销售是机器学习(ML)的常见且必不可少的用途。预测销售可用于确定基准并确定新计划的增量影响,根据预期需求规划资源以及规划未来预算。在本文中,我将展示如何实现5种不同的ML模型来预测销售。
初期准备
首先我们先加载数据并将其转换为一个结构,然后将其用于每个模型。以原始格式,每一行数据代表十个商店中一天的销售额。我们的目标是预测月度销售额,因此我们将首先将所有商店和天数合并为月度总销售额。
def load_data(): return pd.read_csv('D:\Jupyter\dataset\demand-forecasting-kernels-only/train.csv') def monthly_sales(data): monthly_data = data.copy() monthly_datamonthly_data.date = monthly_data.date.apply(lambda x: str(x)[:-3]) monthly_datamonthly_data = monthly_data.groupby('date')['sales'].sum().reset_index() monthly_data.date = pd.to_datetime(monthly_data.date) return monthly_data monthly_df = monthly_sales(sales_data) monthly_df.head()
在我们的新数据框中,每一行现在代表所有商店在给定月份的总销售额。
如果我们绘制随时间变化的每月总销售量,我们会看到平均每月销售量随时间增加,这意味着我们的数据不是固定的。为了使其平稳,我们将计算每月销售额之间的差异,并将其作为新列添加到我们的数据框中。
def get_diff(data): data['sales_diff'] = data.sales.diff() datadata = data.dropna() data.to_csv('D:/Jupyter/dataset/demand-forecasting-kernels-only/stationary_df.csv') return data stationary_df = get_diff(monthly_df)
下面是差异转换前后数据外观的直观表示:
比较差异前后的平稳性
现在,我们的数据代表了每月的销售额,并且已经将其转换为固定值,接下来我们将为不同的模型类型设置数据。为此,我们将定义两种不同的结构:一种将用于ARIMA建模,另一种将用于其余的模型。
对于我们的Arima模型,我们只需要一个日期时间索引和因变量(销售额差异)列。
def generate_arima_data(data): dt_data = data.set_index('date').drop('sales', axis=1) dt_data.dropna(axis=0) dt_data.to_csv('D:/Jupyter/dataset/demand-forecasting-kernels-only/arima_df.csv') return dt_data arima_datetime = generate_arima_data(stationary_df)
对于其他模型,我们将创建一个新的数据框,其中每个特征代表上个月的销售额。为了确定在我们的特征集中包含多少个月,我们将观察自相关和部分自相关图,并在ARIMA建模中使用选择滞后时间的规则。这样,我们就可以为我们的ARIMA和回归模型保持一致的回顾周期。
自相关和局部自相关图
基于上述情况,我们将选择回溯期为12个月。因此,我们将生成一个包含13列的数据框,12个月每个月为1列,而我们的因变量即销售额差异列为第1列。
def generate_supervised(data): supervised_df = data.copy() #create column for each lag for i in range(1,13): col_name = 'lag_' + str(i) supervised_df[col_name] = supervised_df['sales_diff'].shift(i) #drop null values supervised_dfsupervised_df = supervised_df.dropna().reset_index(drop=True) supervised_df.to_csv('D:/Jupyter/dataset/demand-forecasting-kernels-only/model_df.csv', index=False) return supervised_df model_df = generate_supervised(stationary_df)
现在我们有两个独立的数据结构,一个是Arima结构,它包含一个datetime索引,另一个是监督结构,它包含滞后时间特征。
ARIMA和受监督的数据框用于销售预测
建模
为了创建和评估所有的模型,我们使用了一系列执行以下函数的辅助函数。
- 训练、测试、拆分:我们将数据分开,使过去的12个月成为测试集的一部分,其余数据用于训练我们的模型
- 缩放数据:使用最小-最大缩放器,我们将缩放数据,以便所有变量都在-1到1的范围内
- 反向缩放:运行模型后,我们将使用此辅助函数来反转步骤2的缩放
- 创建一个预测数据框:生成一个数据框,其中包括在测试集中捕获的实际销售额和模型的预测结果,以便我们能够量化我们的成功
- 对模型评分:这个辅助函数将保存我们预测的均方根误差(RMSE)和均值绝对误差(MAE),以比较五个模型的性能
1. 回归模型:线性回归,随机森林回归,XGBoost
对于我们的回归模型,我们可以使用scikit-learn库的fit-predict结构。因此,我们可以建立一个基础建模结构,我们将针对每个模型进行调用。下面的函数调用上面概述的许多辅助函数来拆分数据,运行模型并输出RMSE和MAE分数。
def regressive_model(train_data, test_data, model, model_name): # Call helper functions to create X & y and scale data X_train, y_train, X_test, y_test, scaler_object = scale_data(train_data, test_data) # Run regression model mod = model mod.fit(X_train, y_train) predictions = mod.predict(X_test) # Call helper functions to undo scaling & create prediction df original_df = pd.read_csv('D:/Jupyter/dataset/demand-forecasting-kernels-only/train.csv') unscaled = undo_scaling(predictions, X_test, scaler_object) unscaled_df = predict_df(unscaled, original_df) # Call helper functions to print scores and plot results get_scores(unscaled_df, original_df, model_name) plot_results(unscaled_df, original_df, model_name) # Separate data into train and test sets train, test = tts(model_df) # Call model frame work for linear regression regressive_model(train, test, LinearRegression(),'LinearRegression') # Call model frame work for random forest regressor regressive_model(train, test, RandomForestRegressor(n_estimators=100, max_depth=20), 'RandomForest') # Call model frame work for XGBoost regressive_model(train, test, XGBRegressor(n_estimators=100, learning_rate=0.2), 'XGBoost')
下面的输出显示了每个回归模型的预测(红色)覆盖在实际销售(蓝色)之上。虽然结果看起来很相似,但细微的差别相当于几千美元的销售额,我们将在下面的比较部分中看到。
2. 长短期记忆(LSTM)
LSTM是一种递归神经网络,对于使用顺序数据进行预测特别有用。为此,我们将使用非常简单的LSTM。为了提高准确性,可以添加周期性特征和附加模型复杂性。
def lstm_model(train_data, test_data): X_train, y_train, X_test, y_test, scaler_object = scale_data(train_data, test_data) X_trainX_train = X_train.reshape(X_train.shape[0], 1, X_train.shape[1]) X_testX_test = X_test.reshape(X_test.shape[0], 1, X_test.shape[1]) model = Sequential() model.add(LSTM(4, batch_input_shape=(1, X_train.shape[1], X_train.shape[2]), stateful=True)) model.add(Dense(1)) model.add(Dense(1)) model.compile(loss='mean_squared_error', optimizer='adam') model.fit(X_train, y_train, epochs=200, batch_size=1, verbose=1, shuffle=False) predictions = model.predict(X_test,batch_size=1) original_df = load_original_df() unscaled = undo_scaling(predictions, X_test, scaler_object, lstm=True) unscaled_df = predict_df(unscaled, original_df) get_scores(unscaled_df, original_df, 'LSTM') plot_results(unscaled_df, original_df, 'LSTM')
生成的图看起来与上面的三个回归图相似,因此我们将继续比较结果,直到我们看到下面的误差为止。
LSTM模型预测与实际销售额
3. ARIMA
ARIMA模型看起来与上面的模型略有不同。我们使用statsmodels SARIMAX软件包来训练模型并生成动态预测。SARIMA模型分为几个部分。
- AR:表示为p,是自回归模型
- I:用d表示,是微分项
- MA:表示为q,是移动平均模型
- S:使我们能够添加周期性成分
在下面的代码中,我们定义我们的模型,然后对数据的最后12个月进行动态预测。对于标准的非动态预测,下个月的预测是使用前几个月的实际销售额进行的。相反,对于动态预测,使用前几个月的预测销售额进行下个月的预测。
def lstm_model(train_data, test_data): X_train, y_train, X_test, y_test, scaler_object = scale_data(train_data, test_data) X_trainX_train = X_train.reshape(X_train.shape[0], 1, X_train.shape[1]) X_testX_test = X_test.reshape(X_test.shape[0], 1, X_test.shape[1]) model = Sequential() model.add(LSTM(4, batch_input_shape=(1, X_train.shape[1], X_train.shape[2]), stateful=True)) model.add(Dense(1)) model.add(Dense(1)) model.compile(loss='mean_squared_error', optimizer='adam') model.fit(X_train, y_train, epochs=200, batch_size=1, verbose=1, shuffle=False) predictions = model.predict(X_test,batch_size=1) original_df = load_original_df() unscaled = undo_scaling(predictions, X_test, scaler_object, lstm=True) unscaled_df = predict_df(unscaled, original_df) get_scores(unscaled_df, original_df, 'LSTM') plot_results(unscaled_df, original_df, 'LSTM')
同样,结果看起来还不错。我们将在下面进一步进行挖掘。
ARIMA模型预测与实际销售额
比较模型
为了比较模型性能,我们将查看均方根误差(RMSE)和均值绝对误差(MAE)。这些指标通常都用于比较模型性能,但是它们的直觉和数学含义略有不同。
- MAE:均值绝对误差告诉我们,我们的预测与真实值之间的距离。在这种情况下,所有误差的权重都相同。
- RMSE:我们通过取所有平方误差之和的平方根来计算RMSE。当我们平方时,较大的误差对整体误差有较大的影响,而较小的误差对整体误差没有太大的影响。
从上面的辅助函数中,我们使用get_scores计算每个模型的RMSE和MAE分数。这些分数保存在字典中并保存起来。为了进行比较,我们将把字典转换成Pandas数据框并绘制结果。
def create_results_df(): results_dict = pickle.load(open("model_scores.p", "rb")) results_dict.update(pickle.load(open("arima_model_scores.p", "rb"))) restults_df = pd.DataFrame.from_dict(results_dict, orient='index', columns=['RMSE', 'MAE','R2']) restults_dfrestults_df = restults_df.sort_values(by='RMSE', ascending=False).reset_index() return restults_df results = create_results_df()
这为我们提供了以下数据框。
我们可以看到,尽管我们的模型输出在上图中看起来相似,但它们的准确度确实有所不同。下面是可以帮助我们看到差异的视觉效果。
比较模型性能
结论