jinzhao:用户增长手册--1. 数据指标体系
jinzhao:用户增长手册--2. 用户分群
jinzhao:用户增长手册--4. 用户流失预测
jinzhao:用户增长手册--3.用户生命周期价值预测
jinzhao: 用户增长手册--5. 预测下一个购买日
jinzhao: 用户增长手册--6. 策略综合收益建模
jinzhao:用户增长手册--7.销量预测
jinzhao: 用户增长手册--8. 预测促销活动的增量收益
第6部分:销量预测
在本节之前,我们几乎所有的预测模型都是基于客户级别的(例如,客户流失预测,下一个购买日等)。放大视角并概览全局也很有用。顺理成章的,我们应该考虑一个问题就是:我们在客户级别方面的所有努力,能否如何影响整体销售?
时间序列预测是机器学习的主要组成部分之一。文献中有许多方法可以实现此目的,例如:
自回归综合移动平均值(ARIMA),
季节性自回归综合移动平均值(SARIMA),
矢量自回归(VAR)等。
在本文中,我们将重点介绍LSTM方法,如果您想使用深度学习,这是一种非常流行的方法。我们将在项目中使用Keras来实施LSTM。
最后,预测销量对我们的业务有何帮助?
首先,它是一个基准。如果我们的战略没有任何变化,我们可以将其用作将要实现的常规水平。此外,我们可以在此基准基础上计算新操作的增量值。
其次,它可以用于计划。通过查看预测,我们可以计划需求和供应行动。这有助于了解在何处进行更多投资。
最后,预测结果是规划预算和目标的绝佳指南。
现在是时候进入编码并构建我们的第一个深度学习模型了。我们模型的实现将包括3个步骤:
数据整理
进行数据转换以使其稳定并受监督
建立LSTM模型与评估
1. 数据整理
在此示例中,我们使用Kaggle竞赛中的数据集。它代表每个商店和商品的每日销售额。像往常一样,我们从导入所需的库并从CSV导入数据开始:
from datetime import datetime, timedelta,date
import pandas as pd
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
from __future__ import division
import warnings
warnings.filterwarnings("ignore")
import plotly.plotly as py
import plotly.offline as pyoff
import plotly.graph_objs as go
#import Keras
import keras
from keras.layers import Dense
from keras.models import Sequential
from keras.optimizers import Adam
from keras.callbacks import EarlyStopping
from keras.utils import np_utils
from keras.layers import LSTM
from sklearn.model_selection import KFold, cross_val_score, train_test_split
#initiate plotly
pyoff.init_notebook_mode()
#read the data in csv
df_sales = pd.read_csv('sales_data.csv')
#convert date field from string to datetime
df_sales['date'] = pd.to_datetime(df_sales['date'])
#show first 10 rows
df_sales.head(10)
我们的数据如下所示:

我们的任务是预测每月总销售额。我们需要按月汇总我们的数据并汇总销售列。
#represent month in date field as its first day
df_sales['date'] = df_sales['date'].dt.year.astype('str') + '-' + df_sales['date'].dt.month.astype('str') + '-01'
df_sales['date'] = pd.to_datetime(df_sales['date'])
#groupby date and sum the sales
df_sales = df_sales.groupby('date').sales.sum().reset_index()

2. 数据转换
为了更轻松,更准确地对我们的预测建模,我们将进行以下转换:
我们会将数据转换为平稳数据
从时间序列转换为监督模型,成为我们的LSTM模型的特征;
缩放数据
2.1 首先,我们如何检查数据是否稳定?
让我们来绘制它,看看:
#plot monthly sales
plot_data = [
go.Scatter(
x=df_sales['date'],
y=df_sales['sales'],
)
]
plot_layout = go.Layout(
title='Montly Sales'
)
fig = go.Figure(data=plot_data, layout=plot_layout)
pyoff.iplot(fig)
月度销售图:

显然,它不是固定的,并且在过去的几个月中呈上升趋势。采用的方法是计算与上个月相比的销售额差异(微分),熟悉时间序列的同学可以用lag来处理,为了方便初学者理解,我们用拆解开来处理:
#create a new dataframe to model the difference
df_diff = df_sales.copy()
#add previous sales to the next row
df_diff['prev_sales'] = df_diff['sales'].shift(1)
#drop the null values and calculate the difference
df_diff = df_diff.dropna()
df_diff['diff'] = (df_diff['sales'] - df_diff['prev_sales'])
df_diff.head(10)
现在,我们具有建模差异所需的数据框:

让我们绘制它,并检查它现在是否平稳:
#plot sales diff
plot_data = [
go.Scatter(
x = df_diff ['date'],
y = df_diff ['diff'],
)
]
plot_layout = go.Layout(
title =' Montly Sales Diff'
)
fig = go.Figure(data = plot_data,layout = plot_layout)
pyoff.iplot(fig)

完美!现在,我们可以开始构建特征。我们需要使用以前的每月销售数据来预测下一个。每种型号的回溯期可能会有所不同。在此示例中,我们将为12。因此,我们需要做的是创建从lag1到lag12的列,并使用shift()方法进行处理:
#create dataframe for transformation from time series to supervised
df_supervised = df_diff.drop(['prev_sales'],axis=1)
#adding lags
for inc in range(1,13):
field_name = 'lag_' + str(inc)
df_supervised[field_name] = df_supervised['diff'].shift(inc)
#drop null values
df_supervised = df_supervised.dropna().reset_index(drop=True)
查看df_supervised的新数据框:
现在我们有了特征集合。让我们思考问这个问题:
我们的特征有用吗?
调整R方是衡量标准。它表明我们特征如何解释标签的变化(在我们的示例中diff为lag1到lag12)。让我们看一个例子:
# Import statsmodels.formula.api
import statsmodels.formula.api as smf
# Define the regression formula
model = smf.ols(formula='diff ~ lag_1', data=df_supervised)
# Fit the regression
model_fit = model.fit()
# Extract the adjusted r-squared
regression_adj_rsq = model_fit.rsquared_adj
print(regression_adj_rsq)
那么上面看出什么?
基本上,我们拟合线性回归模型(OLS-普通最小二乘)并计算调整后的R平方。对于上面的示例,我们仅使用lag_1来查看它能解释diff列中的变化的程度。该代码块的输出为:

lag_1解释了变化的3%。让我们看看其特征:

再添加四个功能会将得分从3%提高到44%。如果使用整个特征集,每个特征表现如何:

结果是令人惊喜的,因为分数是98%。现在,我们可以在缩放数据后放心地构建模型。但是,在preprocess 之前还有一步。我们应该将数据分为训练集和测试集。作为测试集,我们选择了最近6个月的销售。
#import MinMaxScaler and create a new dataframe for LSTM model
from sklearn.preprocessing import MinMaxScaler
df_model = df_supervised.drop(['sales','date'],axis=1)
#split train and test set
train_set, test_set = df_model[0:-6].values, df_model[-6:].values
作为缩放器,我们将使用MinMaxScaler,它将在-1和1之间缩放每个未来:
#apply Min Max Scaler
scaler = MinMaxScaler(feature_range=(-1, 1))
scaler = scaler.fit(train_set)
# reshape training set
train_set = train_set.reshape(train_set.shape[0], train_set.shape[1])
train_set_scaled = scaler.transform(train_set)
# reshape test set
test_set = test_set.reshape(test_set.shape[0], test_set.shape[1])
test_set_scaled = scaler.transform(test_set)
3. 建立LSTM模型
一切准备就绪,可以构建我们的第一个深度学习模型。让我们从缩放的数据集中创建特征和标签集:
X_train, y_train = train_set_scaled[:, 1:], train_set_scaled[:, 0:1]
X_train = X_train.reshape(X_train.shape[0], 1, X_train.shape[1])
X_test, y_test = test_set_scaled[:, 1:], test_set_scaled[:, 0:1]
X_test = X_test.reshape(X_test.shape[0], 1, X_test.shape[1])
让我们拟合我们的LSTM模型:
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.compile(loss='mean_squared_error', optimizer='adam')
model.fit(X_train, y_train, nb_epoch=100, batch_size=1, verbose=1, shuffle=False)
上面的代码块显示了模型如何改进自身并减少每个迭代的error:
让我们进行预测,看看结果如何:
y_pred = model.predict(X_test,batch_size=1)
#for multistep prediction, you need to replace X_test values with the predictions coming from t-1

结果看起来很相似,但是并不能告诉我们太多,因为这些都是缩放后的数据。我们如何看到实际的销售预测?首先,我们需要进行逆变换以进行缩放:
#reshape y_pred
y_pred = y_pred.reshape(y_pred.shape[0], 1, y_pred.shape[1])
#rebuild test set for inverse transform
pred_test_set = []
for index in range(0,len(y_pred)):
print np.concatenate([y_pred[index],X_test[index]],axis=1)
pred_test_set.append(np.concatenate([y_pred[index],X_test[index]],axis=1))
#reshape pred_test_set
pred_test_set = np.array(pred_test_set)
pred_test_set = pred_test_set.reshape(pred_test_set.shape[0], pred_test_set.shape[2])
#inverse transform
pred_test_set_inverted = scaler.inverse_transform(pred_test_set)
其次,我们需要建立具有日期和预测的数据框。转换后的预测显示出差异。我们应该计算预测的销售数量:
#create dataframe that shows the predicted sales
result_list = []
sales_dates = list(df_sales[-7:].date)
act_sales = list(df_sales[-7:].sales)
for index in range(0,len(pred_test_set_inverted)):
result_dict = {}
result_dict['pred_value'] = int(pred_test_set_inverted[index][0] + act_sales[index])
result_dict['date'] = sales_dates[index+1]
result_list.append(result_dict)
df_result = pd.DataFrame(result_list)
#for multistep prediction, replace act_sales with the predicted sales

我们已经预测了未来六个月的销售数字。让我们在图中检查它们,看看我们的模型效果:
#merge with actual sales dataframe
df_sales_pred = pd.merge(df_sales,df_result,on='date',how='left')
#plot actual and predicted
plot_data = [
go.Scatter(
x=df_sales_pred['date'],
y=df_sales_pred['sales'],
name='actual'
),
go.Scatter(
x=df_sales_pred['date'],
y=df_sales_pred['pred_value'],
name='predicted'
)
]
plot_layout = go.Layout(
title='Sales Prediction'
)
fig = go.Figure(data=plot_data, layout=plot_layout)
pyoff.iplot(fig)
实际与预测:

对于简单的模型来说看起来不错。
我们可以对该模型进行的一项改进是增加假期,休息时间和其他季节性影响。它们可以简单地添加为新功能。
通过使用此模型,我们可以获得基线销售预测。但是,我们如何预测促销对销售的影响?我们将在第7部分中对此进行研究.

