为超级商店销售额开发、评估和评分预测模型
本教程演示了 Microsoft Fabric 中 Synapse 数据科学工作流的端到端示例。 此方案生成一个预测模型,该模型使用历史销售数据来预测超级商店的产品类别销售额。
预测是销售中的关键资产。 它结合了历史数据和预测方法,提供未来趋势的见解。 预测可以分析过去的销售额,以识别模式,并从消费者行为中学习,以优化库存、生产和营销策略。 这种主动方法可增强动态市场中企业的适应性、响应能力和整体性能。
本教程介绍以下步骤:
- 加载数据
- 使用探索性数据分析来了解和处理数据
- 使用开源软件包训练机器学习模型,并使用 MLflow 和 Fabric 自动记录功能跟踪试验
- 保存最终的机器学习模型并进行预测
- 使用 Power BI 可视化效果显示模型性能
先决条件
获取 Microsoft Fabric 订阅。 或者,注册免费的 Microsoft Fabric 试用版。
登录 Microsoft Fabric。
使用主页左下侧的体验切换器切换到 Fabric。
- 如有必要,请创建 Microsoft Fabric 湖屋,如在 Microsoft Fabric 中创建湖屋中所述。
在笔记本中继续操作
可以选择以下选项之一以在笔记中继续操作:
- 在 Synapse Data Science 体验中打开并运行内置笔记本
- 将笔记本从 GitHub 上传到 Synapse 数据科学体验
打开内置笔记本
本教程随附示例“销售预测”笔记本。
若要打开本教程的示例笔记本,请按照 为数据科学教程准备系统中的说明进行操作。
在开始运行代码之前,请务必将湖屋附加到笔记本。
从 GitHub 导入笔记本
本教程随附 AIsample - Superstore Forecast.ipynb 笔记本。
若要打开本教程随附的笔记本,请按照 为数据科学教程准备系统 中的说明将笔记本导入工作区。
如果要复制并粘贴此页面中的代码,可以 创建新的笔记本。
在开始运行代码之前,请务必将湖屋连接到笔记本。
步骤 1:加载数据
数据集包含 9,995 个各种产品的销售额实例。 它还包括 21 个属性。 此表来自此笔记本中使用的 Superstore.xlsx 文件:
行 ID | 订单 ID | 订单日期 | 发货日期 | 发货模式 | 客户 ID | 客户名称 | 客户细分 | 国家 | 城市 | 州 | 邮政编码 | 区域 | 产品 ID | 类别 | 子类别 | 产品名称 | 销售 | 数量 | 折扣 | 利润 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
4 | US-2015-108966 | 2015-10-11 | 2015-10-18 | 标准类 | SO-20335 | Sean O'Donnell | 消费者 | 美国 | 劳德代尔堡 | 佛罗里达州 | 33311 | 南 | FUR-TA-10000577 | 家具 | 表 | 布雷特福德 CR4500 系列纤细矩形桌子 | 957.5775 | 5 | 0.45 | -383.0310 |
11 | CA-2014-115812 | 2014-06-09 | 2014-06-09 | 标准类 | 标准类 | 布罗西娜·霍夫曼 | 消费者 | 美国 | 洛杉矶 | 加州 | 90032 | 西部 | FUR-TA-10001539 | 家具 | 表 | Chromcraft 矩形会议桌 | 1706.184 | 9 | 0.2 | 85.3092 |
31 | US-2015-150630 | 2015-09-17 | 2015-09-21 | 标准类 | TB-21520 | 特蕾西·布卢姆斯坦 | 消费者 | 美国 | 费城 | 宾夕法尼亚州 | 19140 | 东部 | OFF-EN-10001509 | Office 用品 | 信封 | Poly String Tie Envelopes | 3.264 | 2 | 0.2 | 1.1016 |
定义这些参数,以便可以将此笔记本用于不同的数据集:
IS_CUSTOM_DATA = False # If TRUE, the dataset has to be uploaded manually
IS_SAMPLE = False # If TRUE, use only rows of data for training; otherwise, use all data
SAMPLE_ROWS = 5000 # If IS_SAMPLE is True, use only this number of rows for training
DATA_ROOT = "/lakehouse/default"
DATA_FOLDER = "Files/salesforecast" # Folder with data files
DATA_FILE = "Superstore.xlsx" # Data file name
EXPERIMENT_NAME = "aisample-superstore-forecast" # MLflow experiment name
下载数据集并上传到湖仓 (Lakehouse)
此代码下载公开可用的数据集版本,然后将其存储在 Fabric Lakehouse 中:
重要
在运行笔记本之前,请务必向笔记本添加湖屋。 否则,会发生错误。
import os, requests
if not IS_CUSTOM_DATA:
# Download data files into the lakehouse if they're not already there
remote_url = "https://synapseaisolutionsa.blob.core.windows.net/public/Forecast_Superstore_Sales"
file_list = ["Superstore.xlsx"]
download_path = "/lakehouse/default/Files/salesforecast/raw"
if not os.path.exists("/lakehouse/default"):
raise FileNotFoundError(
"Default lakehouse not found, please add a lakehouse and restart the session."
)
os.makedirs(download_path, exist_ok=True)
for fname in file_list:
if not os.path.exists(f"{download_path}/{fname}"):
r = requests.get(f"{remote_url}/{fname}", timeout=30)
with open(f"{download_path}/{fname}", "wb") as f:
f.write(r.content)
print("Downloaded demo data files into lakehouse.")
设置 MLflow 试验跟踪
Microsoft Fabric 会在训练机器学习模型时自动捕获输入参数和输出指标的值。 这扩展了 MLflow 自动记录功能。 然后,信息将记录到工作区,你可以在其中使用 MLflow API 或工作区中的相应试验访问和可视化它。 若要了解有关自动记录的详细信息,请参阅 Microsoft Fabric 中的自动记录。
若要在笔记本会话中关闭 Microsoft Fabric 自动记录,请调用 mlflow.autolog()
并设置 disable=True
:
# Set up MLflow for experiment tracking
import mlflow
mlflow.set_experiment(EXPERIMENT_NAME)
mlflow.autolog(disable=True) # Turn off MLflow autologging
从湖仓中读取原始数据
从湖屋的“文件”部分读取原始数据。 为各个日期部分添加更多列。 相同的信息用于创建分区的Delta表。 由于原始数据存储为 Excel 文件,因此必须使用 pandas 读取它:
import pandas as pd
df = pd.read_excel("/lakehouse/default/Files/salesforecast/raw/Superstore.xlsx")
步骤 2:执行探索性数据分析
导入库
在进行任何分析之前,请导入所需的库:
# Importing required libraries
import warnings
import itertools
import numpy as np
import matplotlib.pyplot as plt
warnings.filterwarnings("ignore")
plt.style.use('fivethirtyeight')
import pandas as pd
import statsmodels.api as sm
import matplotlib
matplotlib.rcParams['axes.labelsize'] = 14
matplotlib.rcParams['xtick.labelsize'] = 12
matplotlib.rcParams['ytick.labelsize'] = 12
matplotlib.rcParams['text.color'] = 'k'
from sklearn.metrics import mean_squared_error,mean_absolute_percentage_error
显示原始数据
手动查看数据的子集,以便更好地了解数据集本身,并使用 display
函数打印数据帧。 此外,Chart
视图可以轻松可视化数据集的子集。
display(df)
此笔记本主要侧重于预测 Furniture
类别销售额。 这会加快计算速度,并帮助显示模型的性能。 但是,此笔记本使用适应性强的技术。 可以扩展这些技术来预测其他产品类别的销售情况。
# Select "Furniture" as the product category
furniture = df.loc[df['Category'] == 'Furniture']
print(furniture['Order Date'].min(), furniture['Order Date'].max())
预处理数据
实际业务方案通常需要以三个不同的类别预测销售额:
- 特定产品类别
- 特定客户类别
- 产品类别和客户类别的特定组合
首先,删除不必要的列来预处理数据。 某些列(Row ID
、Order ID
、Customer ID
和 Customer Name
)是不必要的,因为它们没有影响。 我们希望预测特定产品类别(Furniture
)在全州及整个地区的总体销售额,从而可以删除 State
、Region
、Country
、City
和 Postal Code
列。 若要预测特定位置或类别的销售情况,可能需要相应地调整预处理步骤。
# Data preprocessing
cols = ['Row ID', 'Order ID', 'Ship Date', 'Ship Mode', 'Customer ID', 'Customer Name',
'Segment', 'Country', 'City', 'State', 'Postal Code', 'Region', 'Product ID', 'Category',
'Sub-Category', 'Product Name', 'Quantity', 'Discount', 'Profit']
# Drop unnecessary columns
furniture.drop(cols, axis=1, inplace=True)
furniture = furniture.sort_values('Order Date')
furniture.isnull().sum()
数据集每天进行结构化。 我们必须对列 Order Date
进行重新采样,以开发按月预测销售额的模型。
首先,按 Order Date
对 Furniture
类别进行分组。 然后,计算每个组的 Sales
列的总和,以确定每个唯一 Order Date
值的总销售额。 使用 MS
频率对 Sales
列进行重新采样,以实现按月聚合数据。 最后,计算每个月的平均销售值。
# Data preparation
furniture = furniture.groupby('Order Date')['Sales'].sum().reset_index()
furniture = furniture.set_index('Order Date')
furniture.index
y = furniture['Sales'].resample('MS').mean()
y = y.reset_index()
y['Order Date'] = pd.to_datetime(y['Order Date'])
y['Order Date'] = [i+pd.DateOffset(months=67) for i in y['Order Date']]
y = y.set_index(['Order Date'])
maximim_date = y.reset_index()['Order Date'].max()
演示 Order Date
对 Sales
的影响在 Furniture
类别中:
# Impact of order date on the sales
y.plot(figsize=(12, 3))
plt.show()
在进行任何统计分析之前,必须导入 statsmodels
Python 模块。 它提供用于估计许多统计模型的类和函数。 它还提供用于进行统计测试和统计数据探索的类和函数。
import statsmodels.api as sm
执行统计分析
时序以设置间隔跟踪这些数据元素,以确定时序模式中这些元素的变体:
级别:表示特定时间段平均值的基本组件
趋势:描述时序是减少、保持不变还是随时间而增加
季节性:描述时序中的周期性信号,并查找影响时序模式增加或减少的循环事件
干扰/残差:指模型无法解释的时序数据中的随机波动和可变性。
在此代码中,在预处理后观察数据集的这些元素:
# Decompose the time series into its components by using statsmodels
result = sm.tsa.seasonal_decompose(y, model='additive')
# Labels and corresponding data for plotting
components = [('Seasonality', result.seasonal),
('Trend', result.trend),
('Residual', result.resid),
('Observed Data', y)]
# Create subplots in a grid
fig, axes = plt.subplots(nrows=4, ncols=1, figsize=(12, 7))
plt.subplots_adjust(hspace=0.8) # Adjust vertical space
axes = axes.ravel()
# Plot the components
for ax, (label, data) in zip(axes, components):
ax.plot(data, label=label, color='blue' if label != 'Observed Data' else 'purple')
ax.set_xlabel('Time')
ax.set_ylabel(label)
ax.set_xlabel('Time', fontsize=10)
ax.set_ylabel(label, fontsize=10)
ax.legend(fontsize=10)
plt.show()
这些图描述了预测数据中的季节性、趋势和噪音。 可以捕获基础模式,并开发能够灵活应对随机波动的准确预测的模型。
步骤 3:训练和跟踪模型
有了可用的数据后,请定义预测模型。 在此笔记本中,应用称为“带有外生因子的季节性自动回归集成移动平均”(SARIMAX) 的预测模型。 SARIMAX 结合了自动回归(AR)和移动平均值(MA)组件、季节性差异和外部预测器,对时序数据进行准确灵活的预测。
还可以使用 MLflow 和 Fabric 自动记录来跟踪试验。 在此处,从湖屋加载增量表。 你可能会使用其他将湖屋视为源的增量表。
# Import required libraries for model evaluation
from sklearn.metrics import mean_squared_error, mean_absolute_percentage_error
优化超参数
SARIMAX 考虑了常规自动回归集成移动平均(ARIMA)模式(p
、d
、q
)所涉及的参数,并添加季节性参数(P
、D
、Q
、s
)。 这些 SARIMAX 模型参数分别称为 顺序(p
、d
、q
)和 季节性顺序(P
、D
、Q
、s
)。 因此,若要训练模型,必须先优化七个参数。
阶数参数:
p
:AR 组件的顺序,表示用于预测当前值的时序中的过去观测值数。通常,此参数应为非负整数。 常见值在
0
到3
的范围内,尽管可能会有较高的值,具体取决于特定的数据特征。 较高的p
值表示模型中过去值的内存更长。d
:差异顺序,表示时序需要差异的次数,以实现固定性。此参数应为非负整数。 常见值在
0
2
范围内。0
的d
值表示时序已经平稳。 较高的值表示使其平稳所需的差分操作数。q
:MA 分量的阶数,表示用于预测当前值的过去白噪声误差项的数量。此参数应为非负整数。 常见值在
0
到3
的范围内,但某些时序可能需要更高的值。 较高的q
值表示更依赖过去的误差词进行预测。
季节性顺序参数:
P
:AR 组件的季节性阶数,类似于p
,但适用于季节性部分。D
:季节性差分阶次,类似于d
,但用于季节性部分。Q
:MA 组件的季节性顺序,类似于q
,但针对季节性部分。s
:每个季节性周期的时间步骤数(例如,对于具有每年季节性的每月数据,为 12 个)
# Hyperparameter tuning
p = d = q = range(0, 2)
pdq = list(itertools.product(p, d, q))
seasonal_pdq = [(x[0], x[1], x[2], 12) for x in list(itertools.product(p, d, q))]
print('Examples of parameter combinations for Seasonal ARIMA...')
print('SARIMAX: {} x {}'.format(pdq[1], seasonal_pdq[1]))
print('SARIMAX: {} x {}'.format(pdq[1], seasonal_pdq[2]))
print('SARIMAX: {} x {}'.format(pdq[2], seasonal_pdq[3]))
print('SARIMAX: {} x {}'.format(pdq[2], seasonal_pdq[4]))
SARIMAX 具有其他参数:
enforce_stationarity
:在拟合 SARIMAX 模型之前,模型是否应对时序数据强制实施固定性。如果
enforce_stationarity
设置为True
(默认值),则表示 SARIMAX 模型应对时序数据强制执行固定性。 然后,SARIMAX 模型会根据d
和D
阶数,自动对数据应用差分以使其平稳,然后再拟合模型。 这是一种常见做法,因为许多时序模型(包括 SARIMAX)都假定数据是固定的。对于非静止时序(例如,它表现出趋势或季节性),最好将
enforce_stationarity
设置为True
,让 SARIMAX 模型处理差异以实现固定性。 对于固定时序(例如,没有趋势或季节性的时序),请将enforce_stationarity
设置为False
以避免不必要的差异。enforce_invertibility
:控制模型是否应在优化过程中对估计参数强制实施不可逆性。如果
enforce_invertibility
设置为True
(默认值),则表示 SARIMAX 模型应对估计参数强制实施不可逆性。 不可逆性可确保模型定义良好,并且估计的 AR 和 MA 系数位于固定范围内。不可逆强制有助于确保 SARIMAX 模型符合稳定时序模型的理论要求。 它还有助于防止模型估计和稳定性问题。
默认值为 AR(1)
模型。 这指的是 (1, 0, 0)
。 但是,常见的做法是尝试顺序参数和季节性顺序参数的不同组合,并评估数据集的模型性能。 相应的值可能因不同的时序而异。
确定最佳值通常涉及分析时序数据的自动更正函数(ACF)和部分自动更正函数(PACF)。 它还经常涉及使用模型选择条件 -例如,Akaike 信息条件(AIC)或贝伊西亚信息条件(BIC)。
优化超参数:
# Tune the hyperparameters to determine the best model
for param in pdq:
for param_seasonal in seasonal_pdq:
try:
mod = sm.tsa.statespace.SARIMAX(y,
order=param,
seasonal_order=param_seasonal,
enforce_stationarity=False,
enforce_invertibility=False)
results = mod.fit(disp=False)
print('ARIMA{}x{}12 - AIC:{}'.format(param, param_seasonal, results.aic))
except:
continue
评估上述结果后,可以确定订单参数和季节性订单参数的值。 选择是 order=(0, 1, 1)
和 seasonal_order=(0, 1, 1, 12)
,它们具有最低的 AIC(例如 279.58)。 使用这些值来训练模型。
训练模型
# Model training
mod = sm.tsa.statespace.SARIMAX(y,
order=(0, 1, 1),
seasonal_order=(0, 1, 1, 12),
enforce_stationarity=False,
enforce_invertibility=False)
results = mod.fit(disp=False)
print(results.summary().tables[1])
此代码可视化家具销售数据的时序预测。 绘制的结果显示了观测到的数据和一步向前预测,其中阴影区域表示置信区间。
# Plot the forecasting results
pred = results.get_prediction(start=maximim_date, end=maximim_date+pd.DateOffset(months=6), dynamic=False) # Forecast for the next 6 months (months=6)
pred_ci = pred.conf_int() # Extract the confidence intervals for the predictions
ax = y['2019':].plot(label='observed')
pred.predicted_mean.plot(ax=ax, label='One-step ahead forecast', alpha=.7, figsize=(12, 7))
ax.fill_between(pred_ci.index,
pred_ci.iloc[:, 0],
pred_ci.iloc[:, 1], color='k', alpha=.2)
ax.set_xlabel('Date')
ax.set_ylabel('Furniture Sales')
plt.legend()
plt.show()
# Validate the forecasted result
predictions = results.get_prediction(start=maximim_date-pd.DateOffset(months=6-1), dynamic=False)
# Forecast on the unseen future data
predictions_future = results.get_prediction(start=maximim_date+ pd.DateOffset(months=1),end=maximim_date+ pd.DateOffset(months=6),dynamic=False)
使用 predictions
通过将其与实际值对比来评估模型的性能。 predictions_future
值指示未来的预测。
# Log the model and parameters
model_name = f"{EXPERIMENT_NAME}-Sarimax"
with mlflow.start_run(run_name="Sarimax") as run:
mlflow.statsmodels.log_model(results,model_name,registered_model_name=model_name)
mlflow.log_params({"order":(0,1,1),"seasonal_order":(0, 1, 1, 12),'enforce_stationarity':False,'enforce_invertibility':False})
model_uri = f"runs:/{run.info.run_id}/{model_name}"
print("Model saved in run %s" % run.info.run_id)
print(f"Model URI: {model_uri}")
mlflow.end_run()
# Load the saved model
loaded_model = mlflow.statsmodels.load_model(model_uri)
步骤 4:为模型评分并保存预测
将实际值与预测值集成,以创建 Power BI 报表。 将这些结果存储在 Lakehouse 中的表中。
# Data preparation for Power BI visualization
Future = pd.DataFrame(predictions_future.predicted_mean).reset_index()
Future.columns = ['Date','Forecasted_Sales']
Future['Actual_Sales'] = np.NAN
Actual = pd.DataFrame(predictions.predicted_mean).reset_index()
Actual.columns = ['Date','Forecasted_Sales']
y_truth = y['2023-02-01':]
Actual['Actual_Sales'] = y_truth.values
final_data = pd.concat([Actual,Future])
# Calculate the mean absolute percentage error (MAPE) between 'Actual_Sales' and 'Forecasted_Sales'
final_data['MAPE'] = mean_absolute_percentage_error(Actual['Actual_Sales'], Actual['Forecasted_Sales']) * 100
final_data['Category'] = "Furniture"
final_data[final_data['Actual_Sales'].isnull()]
input_df = y.reset_index()
input_df.rename(columns = {'Order Date':'Date','Sales':'Actual_Sales'}, inplace=True)
input_df['Category'] = 'Furniture'
input_df['MAPE'] = np.NAN
input_df['Forecasted_Sales'] = np.NAN
# Write back the results into the lakehouse
final_data_2 = pd.concat([input_df,final_data[final_data['Actual_Sales'].isnull()]])
table_name = "Demand_Forecast_New_1"
spark.createDataFrame(final_data_2).write.mode("overwrite").format("delta").save(f"Tables/{table_name}")
print(f"Spark DataFrame saved to delta table: {table_name}")
步骤 5:在 Power BI 中可视化
Power BI 报表显示平均绝对百分比误差(MAPE)为 16.58。 MAPE 指标定义预测方法的准确性。 它表示预测数量与实际数量相比的准确性。
MAPE 是一个简单的指标。 10% MAPE 表示预测值与实际值之间的平均偏差是 10%,无论偏差是正还是负。 所需 MAPE 值的标准因行业而异。
此图中的浅蓝色线条表示实际销售值。 深蓝色线条表示预测的销售值。 实际销售额和预测销售额的比较显示,模型可有效预测 2023 年上半年 Furniture
类别的销售情况。
根据这一观察,我们对模型在预测2023年下半年及延续至2024年总销售额的能力充满信心。 这种信心可以告知有关库存管理、原材料采购和其他与业务相关的注意事项的战略决策。
相关内容
- 如何使用 Microsoft Fabric 笔记本
- Microsoft Fabric 中的 机器学习模型
- 训练机器学习模型
- Microsoft Fabric 中的机器学习试验