Compartir a través de


Desarrollo, evaluación y puntuación de un modelo de previsión de ventas de hipermercado

En este tutorial se muestra un ejemplo completo de un flujo de trabajo de ciencia de datos de Synapse en Microsoft Fabric. En este escenario se crea un modelo de previsión que use datos históricos de ventas para predecir las ventas de diferentes categorías de productos en un hipermercado.

La previsión es un activo fundamental en las ventas. Combina datos históricos y métodos predictivos para proporcionar información sobre las tendencias futuras. La previsión puede analizar las ventas pasadas para identificar patrones y aprender del comportamiento del consumidor a fin de optimizar las estrategias de marketing, el inventario y la producción. Este enfoque proactivo mejora las capacidades de adaptación y respuesta, y el rendimiento general de las empresas en un marketplace dinámico.

En este tutorial se describen estos pasos:

  • Carga de los datos
  • Usar los análisis de datos exploratorios para comprender y procesar los datos
  • Entrene un modelo de Machine Learning con un paquete de software de código abierto y realice un seguimiento de experimentos mediante MLflow y la característica de registro automático de Fabric.
  • Guarde el modelo de Machine Learning final y realice predicciones.
  • Mostrar el rendimiento del modelo con visualizaciones de Power BI

Requisitos previos

Seguir en un cuaderno

Puede elegir una de estas opciones para seguir en un cuaderno:

  • Abra y ejecute el cuaderno integrado en la experiencia de ciencia de datos de Synapse
  • Cargue su cuaderno desde GitHub a la experiencia de ciencia de datos de Synapse

Abra el cuaderno integrado

El cuaderno de muestra Sales forecasting acompaña a este tutorial.

Para abrir el cuaderno de muestra integrado en el tutorial en la experiencia de ciencia de datos de Synapse:

  1. Vaya a la página principal de ciencia de datos de Synapse.

  2. Seleccione Utilizar una muestra.

  3. Seleccione la muestra correspondiente:

    • Desde la pestaña predeterminada Flujos de trabajo de un extremo a otro (Python), si la muestra es para un tutorial de Python.
    • Desde la pestaña Flujos de trabajo de un extremo a otro (R), si la muestra es para un tutorial de R.
    • En la pestaña Tutoriales rápidos, si la muestra es para un tutorial rápido.
  4. Adjunte un almacén de lago al cuaderno antes de empezar a ejecutar código.

Importación del cuaderno desde GitHub

El cuaderno AIsample - Superstore Forecast.ipynb acompaña a este tutorial.

Para abrir el cuaderno complementario para este tutorial, siga las instrucciones en Preparación del sistema para los tutoriales de ciencia de datos para importar el cuaderno en el área de trabajo.

Si prefiere copiar y pegar el código de esta página, puede crear un cuaderno nuevo.

Asegúrese de adjuntar una instancia de LakeHouse al cuaderno antes de empezar a ejecutar código.

Paso 1: Carga de los datos

El conjunto de datos contiene 9995 instancias de ventas de varios productos. También incluye 21 atributos. La tabla siguiente procede del archivo Superstore.xlsx usado en este cuaderno.

Identificador de fila Id. de pedido Fecha pedido Ship Date Modo de envío Id. de cliente Nombre del cliente Segmento Country Ciudad State Código postal Region Id. del producto Category Subcategoría Nombre del producto Ventas Cantidad Descuento Beneficios
4 US-2015-108966 11/10/2015 18/10/2015 Clase estándar SO-20335 Sean O'Donnell Consumidor Estados Unidos Fort Lauderdale Florida 33311 South FUR-TA-10000577 Mobiliario Tablas Mesa rectangular delgada de la serie CR4500 de Bretford 957,5775 5 0.45 -383,0310
11 CA-2014-115812 09/06/2014 09/06/2014 Clase estándar Clase estándar Brosina Hoffman Consumidor Estados Unidos Los Angeles California 90032 West FUR-TA-10001539 Mobiliario Tablas Mesas de conferencias rectangulares de Chromcraft 1706,184 9 0,2 85,3092
31 US-2015-150630 17/09/2015 21/09/2015 Clase estándar TB-21520 Tracy Blumstein Consumidor Estados Unidos Filadelfia Pensilvania 19140 East OFF-EN-10001509 Material de oficina Sobres Sobres con cierre de cuerda de poliéster 3,264 2 0,2 1,1016

Defina estos parámetros para poder utilizar este cuaderno con diferentes conjuntos de datos:

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

Descarga del conjunto de datos y carga en un almacén de lago de datos

El código siguiente descarga una versión disponible públicamente del conjunto de datos y, a continuación, la almacena en un almacén de lago de Fabric:

Importante

Asegúrese de agregar un almacén de lago al cuaderno antes de ejecutarlo. De lo contrario, recibirá un error.

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.")

Configuración del seguimiento del experimento de MLflow

Microsoft Fabric captura automáticament los valores de los parámetros de entrada y las métricas de salida de un modelo de Machine Learning a medida que lo entrena. Esto amplía las funcionalidades de registro automático de MLflow. A continuación, la información se registra en el área de trabajo, donde puede acceder a ella y visualizarla con las API de MLflow o el experimento correspondiente en el área de trabajo. Para obtener más información sobre el registro automático, consulte Registro automático en Microsoft Fabric.

Para desactivar el registro automático de Microsoft Fabric en una sesión de cuaderno, llame a mlflow.autolog() y establezca disable=True:

# Set up MLflow for experiment tracking
import mlflow

mlflow.set_experiment(EXPERIMENT_NAME)
mlflow.autolog(disable=True)  # Turn off MLflow autologging

Lectura de datos sin procesar desde el lago de datos

Leer los datos sin procesar del Archivos del lago de datos. Agregue más columnas para diferentes partes de fecha. La misma información se utiliza para crear una tabla diferencial con particiones. Debido a que los datos sin procesar se almacenan como un archivo de Excel, debe utilizar Pandas para leerlos:

import pandas as pd
df = pd.read_excel("/lakehouse/default/Files/salesforecast/raw/Superstore.xlsx")

Paso 2: Realización de un análisis exploratorio de los datos

Importación de bibliotecas

Antes de cualquier análisis, importe las bibliotecas necesarias:

# 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

Mostrar los datos sin procesar

Revise manualmente un subconjunto de los datos para comprender mejor el propio conjunto de datos y use la función display para imprimir el DataFrame. Además, las vistas de Chart pueden visualizar fácilmente subconjuntos del conjunto de datos.

display(df)

Este cuaderno se centra principalmente en la previsión de las ventas de la categoría Furniture. Esto acelera el cálculo y ayuda a mostrar el rendimiento del modelo. Sin embargo, este cuaderno usa técnicas adaptables. Puede ampliar esas técnicas para predecir las ventas de otras categorías de productos.

# Select "Furniture" as the product category
furniture = df.loc[df['Category'] == 'Furniture']
print(furniture['Order Date'].min(), furniture['Order Date'].max())

Preprocesamiento de los datos

En los escenarios empresariales reales, a menudo es necesario predecir las ventas en tres categorías distintas:

  • Una categoría de producto específica
  • Una categoría de cliente específica
  • Una combinación específica de categoría de productos y categoría de clientes

En primer lugar, anule las columnas innecesarias para preprocesar los datos. Algunas de las columnas (Row ID, Order ID, Customer ID y Customer Name) no son necesarias porque no tienen ningún impacto. Queremos predecir las ventas generales en el estado y la región de una categoría específica de un producto (Furniture) así que podemos anular las columnas State, Region, Country, City y Postal Code. Para predecir las ventas de una ubicación o categoría específicas, es posible que tenga que ajustar el paso de preprocesamiento en consecuencia.

# 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()

El conjunto de datos se estructura a diario. Debemos crear un nuevo muestreo en la columna Order Date, ya que queremos desarrollar un modelo para predecir las ventas mensualmente.

En primer lugar, agrupe la categoría Furniture según Order Date. A continuación, calcule la suma de la columna Sales para cada grupo a fin de determinar las ventas totales de cada valor de Order Date único. Cree un nuevo muestreo de la columna Sales mediante la frecuencia MS para agregar los datos por mes. Por último, calcule el valor medio de ventas de cada mes.

# 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()

Demuestre el impacto de Order Date en Sales de la categoría Furniture:

# Impact of order date on the sales
y.plot(figsize=(12, 3))
plt.show()

Antes de realizar cualquier análisis estadístico, debe importar el módulo statsmodels de Python. Proporciona clases y funciones para la estimación de numerosos modelos estadísticos. También proporciona clases y funciones para hacer pruebas estadísticas y exploración de datos estadísticos.

import statsmodels.api as sm

Realización de análisis estadísticos

Una serie temporal realiza un seguimiento de estos elementos de datos a intervalos establecidos para determinar la variación de esos elementos en el patrón de serie temporal:

  • Nivel: el componente fundamental que representa el valor medio correspondiente a un periodo de tiempo específico

  • Tendencia: describe si la serie temporal disminuye, se mantiene constante o aumenta con el tiempo

  • Temporalidad: describe la señal periódica en la serie temporal y busca apariciones cíclicas que afectan a los patrones de aumento o disminución de la serie temporal

  • Ruido/residual: hace referencia a las fluctuaciones aleatorias y la variabilidad en los datos de serie temporal que el modelo no puede explicar.

En este código, se observan estos elementos para el conjunto de datos después del procesamiento previo:

# 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()

Los trazados describen la temporalidad, las tendencias y el ruido en los datos de previsión. Puede capturar los patrones subyacentes y desarrollar modelos que realicen predicciones precisas y sean resistentes a las fluctuaciones aleatorias.

Paso 3: Entrenamiento y seguimiento del modelo

Ahora que tiene los datos disponibles, defina el modelo de previsión. En este cuaderno, aplique el modelo de pronóstico denominado media móvil integrada autorregresiva de temporada con factores exógenos (SARIMAX). SARIMAX combina componentes de regresión automática (AR) y media móvil (MA), diferenciación de temporada y predictores externos para realizar previsiones precisas y flexibles de los datos de serie temporal.

Use también MLflow y el registro automático de Fabric para realizar un seguimiento de los experimentos. Aquí cargará la tabla delta desde el almacén de lago. Puede usar otras tablas diferenciales que tengan en cuenta el almacén de lago como origen.

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

Ajuste de hiperparámetros

SARIMAX tiene en cuenta los parámetros implicados en el modo de media móvil integrada autorregresiva (ARIMA) normal (p, d, q) y también agrega los parámetros de temporalidad (P, D, Q, s). Estos argumentos para el modelo de SARIMAX se denominan order (p, d y q) y seasonal order (P, D, Q y s), respectivamente. Por lo tanto, para entrenar el modelo, primero debemos ajustar siete parámetros.

Parámetros de order:

  • p: el orden del componente de AR representa el número de observaciones pasadas de la serie temporal que se usan para predecir el valor actual.

    Normalmente, este parámetro debería ser un entero no negativo. Los valores comunes suelen estar en el intervalo de 0 a 3, aunque es posible alcanzar valores más altos en función de las características específicas de los datos. Un valor de p más alto indica una memoria más larga de valores anteriores en el modelo.

  • d: el orden de diferenciación representa el número de veces que debe diferenciarse la serie temporal para lograr la estacionalidad.

    Este parámetro debe ser un entero no negativo. Los valores comunes están en el intervalo de 0 a 2. Un valor de d de 0 significa que la serie temporal ya es estacionaria. Los valores más altos indican el número de operaciones de diferenciación necesarias para que sea estacionaria.

  • q: el orden del componente de MA representa el número de términos de error de ruido blanco pasados que se usan para predecir el valor actual.

    Este parámetro debe ser un entero no negativo. Los valores comunes están en el intervalo de 0 a 3, pero puede que se necesiten unos valores más altos en el caso de determinadas series temporales. Un valor de q más alto indica una mayor dependencia de los términos de error pasados para realizar predicciones.

Parámetros de seasonal order:

  • P: el orden de temporada del componente de AR, similar a p, pero para la parte de temporada
  • D: el orden de temporada de diferenciación, similar a d, pero para la parte de temporada
  • Q: el orden de temporada del componente de MA, similar a q, pero para la parte de temporada
  • s: el número de pasos de tiempo por ciclo de temporada (por ejemplo, 12 para datos mensuales con una temporalidad anual)
# 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 tiene otros parámetros:

  • enforce_stationarity: si el modelo debe aplicar la estacionalidad o no en los datos de serie temporal antes de ajustar el modelo SARIMAX.

    Si enforce_stationarity se establece en True (valor predeterminado), indica que el modelo SARIMAX debe aplicar la estacionalidad en los datos de serie temporal. En tal caso, el modelo SARIMAX aplica automáticamente la diferenciación a los datos para que sean fijos, según lo especificado por los órdenes d y D, antes de ajustar el modelo. Se trata de una práctica común, ya que muchos modelos de serie temporal, entre ellos SARIMAX, dan por hecho que los datos son fijos.

    En el caso de las series temporales que no son fijas (por ejemplo, muestra tendencias o temporalidad), se recomienda establecer enforce_stationarity en True y permitir que el modelo SARIMAX controle la diferenciación para lograr la estacionalidad. En el caso de las series temporales fijas (por ejemplo, no tiene tendencias ni temporalidad), establezca enforce_stationarity en False para evitar la diferenciación innecesaria.

  • enforce_invertibility: controla si el modelo debe aplicar la invertibilidad o no en los parámetros estimados durante el proceso de optimización.

    Si enforce_invertibility se establece en True (valor predeterminado), indica que el modelo SARIMAX debe aplicar la invertibilidad en los parámetros estimados. La invertibilidad garantiza que el modelo esté bien definido y que los coeficientes estimados de AR y MA estén dentro del intervalo de estacionalidad.

    La obligatoriedad de la invertibilidad ayuda a asegurarse de que el modelo SARIMAX cumple los requisitos teóricos de un modelo de serie temporal estable. También ayuda a evitar problemas con la estimación y estabilidad del modelo.

El valor predeterminado es un modelo de AR(1) Esto hace referencia a (1, 0, 0). Sin embargo, es habitual probar diferentes combinaciones de los parámetros de order y seasonal order, y evaluar el rendimiento del modelo para un conjunto de datos. Los valores adecuados pueden variar de una serie temporal a otra.

Determinar los valores óptimos a menudo implica analizar la función de autocorrelación (ACF) y la función de autocorrelación parcial (PACF) de los datos de la serie temporal. También suele implicar el uso de criterios de selección de modelos, como el criterio de información de Akaike (AIC) o el criterio de información bayesiano (BIC).

Ajuste de los hiperparámetros:

# 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

Tras la evaluación de los resultados anteriores, puede determinar los valores de los parámetros de order y, a continuación, los de seasonal order. La opción es order=(0, 1, 1) y seasonal_order=(0, 1, 1, 12), que ofrecen el AIC más bajo (por ejemplo, 279,58). Utilice estos valores para entrenar el modelo.

Entrenamiento del modelo

# 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])

Este código visualiza una previsión de series temporales para los datos de ventas de muebles. Los resultados trazados muestran los datos observados y el pronóstico de un paso adelante, con una región sombreada para el intervalo de confianza.

# 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)

Use predictions para evaluar el rendimiento del modelo comparándolo con los valores reales. El valor de predictions_future indica la previsión futura.

# 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)

Paso 4: Puntuación del modelo y guardado de las predicciones

Integre los valores reales con los valores previstos para crear el informe de Power BI. Almacene estos resultados en una tabla dentro del almacén de lago.

# 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}")

Paso 5: Visualización en Power BI

En el informe de Power BI se muestra un error de porcentaje absoluto medio (MAPE) de 16,58. La métrica MAPE define la precisión de un método de pronóstico. Representa la precisión de las cantidades pronosticadas en comparación con las cantidades reales.

MAPE es una métrica sencilla. Un MAPE del 10 % representa que la desviación media entre los valores previstos y los valores reales es del 10 %, independientemente de si la desviación era positiva o negativa. Los estándares de valores MAPE deseables varían en todos los sectores.

La línea azul claro de este gráfico representa los valores reales de ventas. La línea azul oscuro representa los valores de ventas pronosticados. La comparación entre las ventas reales y previstas revela que el modelo predice eficazmente las ventas de la categoría Furniture durante los primeros seis meses de 2023.

Screenshot of a Power BI report.

En función de esta observación, podemos tener confianza en las funcionalidades de previsión del modelo para las ventas generales en los últimos seis meses de 2023 y hasta 2024. Esta confianza puede hacer que tomen forma decisiones estratégicas sobre la gestión de inventarios, la adquisición de materias primas y otras consideraciones relacionadas con la empresa.