Share via


開發、評估和評分超級商店銷售的預測模型

本教學課程提供 Microsoft Fabric 中 Synapse 資料科學 工作流程的端對端範例。 此案例會建置預測模型,該模型會使用歷史銷售數據來預測超市的產品類別銷售。

預測是銷售中的重要資產。 它會結合歷史數據和預測方法,以提供未來趨勢的見解。 預測可以分析過去的銷售以識別模式,並從取用者行為中學習,以優化庫存、生產和行銷策略。 此主動式方法可增強動態市集中企業的適應性、回應性和整體效能。

本教學課程涵蓋下列步驟:

  • 載入資料
  • 使用探勘數據分析來了解和處理數據
  • 使用開放原始碼軟體套件定型機器學習模型,並使用 MLflow 和網狀架構自動記錄功能來追蹤實驗
  • 儲存最終的機器學習模型,並進行預測
  • 使用Power BI視覺效果顯示模型效能

必要條件

在筆記本中跟著

您可以選擇下列其中一個選項,以遵循筆記本:

  • 在 Synapse 資料科學 體驗中開啟並執行內建筆記本
  • 將筆記本從 GitHub 上傳至 Synapse 資料科學 體驗

開啟內建筆記本

本教學課程隨附銷售預測筆記本範例

若要在 Synapse 資料科學 體驗中開啟教學課程的內建範例筆記本:

  1. 移至 Synapse 資料科學 首頁。

  2. 選取 [ 使用範例]。

  3. 選取對應的範例:

    • 如果範例適用於 Python 教學課程,請從預設 的端對端工作流程 (Python) 索引標籤。
    • 如果範例適用於 R 教學課程,請從 [端對端工作流程] 索引標籤。
    • 如果範例適用於快速教學課程,請從 [ 快速教學 課程] 索引卷標。
  4. 開始執行程序代碼之前,請先將 Lakehouse 附加至筆記本

從 GitHub 匯入筆記本

AIsample - Superstore Forecast.ipynb 筆記本會伴隨本教學課程。

若要開啟本教學課程隨附的筆記本,請遵循準備系統以進行數據科學教學課程中的指示,將筆記本匯入您的工作區。

如果您想要複製並貼上此頁面中的程式碼,您可以 建立新的筆記本

開始執行程序代碼之前,請務必將 Lakehouse 附加至筆記本

步驟 1:載入數據

數據集包含各種產品的9,995個銷售實例。 它也包含21個屬性。 此數據表來自 此筆記本中使用的Superstore.xlsx 檔案:

資料列識別碼 訂單識別碼 訂單日期 出貨日期 出貨模式 客戶識別碼 客戶名稱 區段 Country 縣/市 州/省 郵遞區號 區域 產品識別碼 類別 子類別 產品名稱 Sales 數量 折扣 收益
4 US-2015-108966 2015-10-11 2015-10-18 標準類別 SO-20335 肖恩·奧唐內爾 消費者 美國 勞德代爾 佛羅里達州 33311 FUR-TA-10000577 傢俱 資料表 佈雷特福德 CR4500 系列苗條矩形表格 957.5775 5 0.45 -383.0310
11 CA-2014-115812 2014-06-09 2014-06-09 標準類別 標準類別 布羅西娜·霍夫曼 消費者 美國 Los Angeles 加州 90032 West 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 辦公室用品 信封 Poly 字串系結信封 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 中:

重要

在執行筆記本之前, 請務必將 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

從 Lakehouse 讀取原始數據

Lakehouse 的 [檔案 ] 區段讀取原始數據。 為不同的日期元件新增更多數據行。 相同的資訊是用來建立數據分割的差異數據表。 因為原始數據會儲存為 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 式來列印 DataFrame。 此外,檢視 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 IDOrder IDCustomer ID、 和 Customer Name) 是不必要的,因為它們沒有影響。 我們想要針對特定產品類別 (Furniture) 預測整個州和區域的整體銷售額,因此我們可以卸除 StateRegionCountryCityPostal 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,因為我們想要開發模型,以每月預測銷售額。

首先,依 FurnitureOrder Date將類別分組。 然後,計算每個群組的數據行總 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()

示範類別對 Furniture 的影響Order DateSales

# 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 自動記錄來追蹤實驗。 在這裡,從 Lakehouse 載入差異數據表。 您可以使用將 Lakehouse 視為來源的其他差異數據表。

# Import required libraries for model evaluation
from sklearn.metrics import mean_squared_error, mean_absolute_percentage_error

調整超參數

SARIMAX 會考慮一般自動回歸整合移動平均 (ARIMA) 模式 (、 、 ) 所涉及的參數,並新增季節性參數 (pPQsdDq 這些 SARIMAX 模型自變數分別稱為 orderp、 、 q) 和季節性順序PDd、 、 sQ 因此,若要定型模型,我們必須先微調七個參數。

順序參數:

  • p:AR元件的順序,代表用來預測目前值的時間序列中過去觀察的數目。

    一般而言,此參數應該是非負整數。 一般值的範圍是 03雖然可能會有較高的值,但視特定數據特性而定。 較高的 p 值表示模型中過去值的記憶體較長。

  • d:差異順序,代表時間序列需要差異的次數,以達到固定性。

    此參數應該是非負整數。 一般值的範圍是 02d0表示時間序列已經固定。 較高的值表示使它靜止所需的差異作業數目。

  • q:MA元件的順序,代表用來預測目前值的過去白雜訊誤差字詞數目。

    此參數應該是非負整數。 一般值的範圍為 03,但某些時間序列可能需要較高的值。 較高的 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_stationarityTrue,並讓 SARIMAX 模型處理差異以達到固定性。 針對固定時間序列(例如,沒有趨勢或季節性的序列),設定 enforce_stationarityFalse 以避免不必要的差異。

  • enforce_invertibility:控制模型是否應該在優化程式期間對估計參數強制執行不可反轉。

    如果 enforce_invertibility 設定為 True (預設值),則表示 SARIMAX 模型應該在估計參數上強制執行不可逆性。 不可逆性可確保模型已妥善定義,且預估的 AR 和 MA 係數落在固定性範圍內。

    反向強制執行有助於確保 SARIMAX 模型符合穩定時間序列模型的理論需求。 它也有助於防止模型估計和穩定性的問題。

預設值為 AR(1) 模型。 這是指 (1, 0, 0)。 不過,嘗試順序參數和季節性順序參數的不同組合,並評估數據集的模型效能是常見的做法。 適當的值可能會因時間序列而異。

判斷最佳值通常牽涉到分析時間序列數據的自動更正函式(ACF)和部分自動更正函式。 它通常也涉及使用模型選取準則-例如,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

Screenshot of a Power BI report.

根據此觀察,我們可以對模型預測功能有信心,2023 年過去 6 個月的整體銷售額,並延伸到 2024 年。 這種信心可以通知有關庫存管理、原材料採購和其他商務相關考慮的戰略決策。