To forecast multi-horizon sales with exogenous features using an LSTM in TensorFlow/Keras, use a seq-to-seq pattern: encoder processes historical windows, decoder (or a dense head) predicts H future steps. Key points: input shapes, masking for missing values, multi-step loss (optionally horizon-weighted), and time-series-aware validation (rolling-origin).Approach:1) Inputs:- past_sales: shape (batch, T_in, 1)- exog_past: shape (batch, T_in, E_past) (promotions, price history, holidays)- exog_future: shape (batch, H, E_future) (known future exog like planned promotions/holidays)Concatenate past features into (batch, T_in, 1+E_past).2) Model (simple encoder + repeat + decoder):python
import tensorflow as tf
from tensorflow.keras import layers, Model
T_in = 28 # input window
H = 14 # forecast horizon
E_past = 2
E_future = 3
past_input = layers.Input(shape=(T_in, 1+E_past), name="past_input")
future_exog = layers.Input(shape=(H, E_future), name="future_exog")
# Masking to handle padded/missing timesteps (use NaN->mask_value or use mask tensor)
x = layers.Masking(mask_value=0.0)(past_input)
x = layers.LSTM(128, return_sequences=False)(x)
# optional: repeat context and concatenate future exog
context = layers.RepeatVector(H)(x)
decoder_input = layers.Concatenate(axis=-1)([context, future_exog])
y = layers.LSTM(64, return_sequences=True)(decoder_input)
out = layers.TimeDistributed(layers.Dense(1))(y)
model = Model([past_input, future_exog], out)
Loss for multi-step outputs:- Use timestep-wise MSE: loss = mean_over_horizons(MSE). To emphasize near-term accuracy, use a weighted MSE:python
horizon_weights = tf.constant([...], dtype=tf.float32) # shape (H,)
def weighted_mse(y_true, y_pred):
se = tf.square(y_true - y_pred) # (batch,H,1)
se = tf.squeeze(se, -1) * horizon_weights # broadcast weights
return tf.reduce_mean(se)
- Handle missing target values with a mask: pass sample_weight or multiply loss by mask (1 for observed, 0 for missing).Handling missing values:- For exogenous features: impute (forward-fill, median) and add binary indicators for imputed points.- For targets: if occasional NaNs, mask them in loss or remove affected windows. Use layers.Masking for input masks; for irregular time intervals consider time-lag feature.- Normalize numeric features per time series (scaler saved per series).Training & validation strategies:- Use rolling-origin (walk-forward) validation: create multiple train/val splits by moving the forecast origin, preserving temporal order.- Avoid random shuffles; set shuffle=False in fit and use stateful batches carefully or stateless LSTM with sequence batching.- Use TimeSeriesGenerator or custom windowing to produce (past_window, future_exog, future_targets).- Use callbacks: EarlyStopping (monitor val_loss), ReduceLROnPlateau, and checkpointing.- Evaluation metrics: per-horizon RMSE/MASE and aggregated business metrics (e.g., bias on promo periods).- For cross-series learning (many SKUs), include SKU embeddings or categorical features.Other considerations / trade-offs:- Stateful LSTM can capture long continuity but complicates batching; prefer stateless with longer windows.- Transformer/TCN may outperform LSTM for long horizons.- Calibration: quantify uncertainty via quantile loss (pinball) or Monte Carlo dropout / outputting prediction intervals.This architecture balances simplicity and practical needs: masking/imputation for missingness, horizon-aware loss, and robust time-series validation. Adjust hyperparameters (layers, units, window length) and incorporate business constraints (shelf capacity, promo rules) as needed.