{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# Lesson 07: Convolutions for Temporal Patterns\n",
        "\n",
        "**CS230 inspiration:** Foundations of convolutional neural networks, deep convolutional models, and residual ideas.\n",
        "\n",
        "**Demand forecasting focus:** Use 1D convolutions to extract local weekly and promotional demand patterns from sequences.\n",
        "\n",
        "**Learning objectives**\n",
        "\n",
        "- Translate ConvNet intuition from images to temporal windows.\n",
        "- Track tensor shape changes through Conv1d layers.\n",
        "- Train a direct multi-horizon CNN forecaster.\n",
        "\n",
        "**Senior-readiness checkpoint:** You can explain when temporal convolutions are a strong baseline for sequence forecasting.\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Code Cell 1 Explanation\n",
        "\n",
        "**What this cell does:** This setup cell imports the shared scientific Python stack and the curriculum helpers used by later cells. It also establishes the deterministic seed convention so repeated runs are comparable.\n",
        "\n",
        "**Why it matters:** A restart-and-run notebook should make its dependencies explicit before any modeling work begins. For forecasting, reproducibility is especially important because small data or seed changes can change validation conclusions.\n",
        "\n",
        "**Key operations to notice:** confirm that shared helper imports match the lesson's code path; check that `SEED` is defined before any generated data or model training\n",
        "\n",
        "**What to inspect:** Check that imports are grouped by purpose: general analysis packages, forecasting data helpers, metrics, plotting utilities, and any lesson-specific libraries.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "import matplotlib.pyplot as plt\n",
        "import numpy as np\n",
        "import pandas as pd\n",
        "\n",
        "from demand_forecasting_curriculum.data import (\n",
        "    DEFAULT_FEATURE_COLUMNS,\n",
        "    generate_daily_demand,\n",
        "    make_sequence_dataset,\n",
        "    make_supervised_table,\n",
        "    time_series_split,\n",
        ")\n",
        "from demand_forecasting_curriculum.metrics import (\n",
        "    interval_coverage,\n",
        "    mase,\n",
        "    pinball_loss,\n",
        "    smape,\n",
        "    wmape,\n",
        ")\n",
        "from demand_forecasting_curriculum.plotting import plot_predictions, plot_series\n",
        "\n",
        "SEED = 42\n",
        "np.random.seed(SEED)\n",
        "\n",
        "import torch\n",
        "from torch import nn\n",
        "from torch.utils.data import DataLoader, TensorDataset\n",
        "from demand_forecasting_curriculum.modeling import set_seed\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Code Cell 2 Explanation\n",
        "\n",
        "**What this cell does:** This cell advances **Convolutions for Temporal Patterns** by working on synthetic demand generation, sequence-window construction. It creates or updates `df`, `X`, `y`, `meta`, `target_start`, `train_idx` and prepares evidence that later cells use rather than hiding state in prose.\n",
        "\n",
        "**Why it matters:** The goal is tied to this lesson's forecasting focus: Use 1D convolutions to extract local weekly and promotional demand patterns from sequences. Each object is kept inspectable so the learner can connect code, assumptions, and modeling consequences.\n",
        "\n",
        "**Key operations to notice:** creates a synthetic retail panel with known demand drivers; builds lookback windows for sequence models; batches examples for neural-network training; packages tensors into a PyTorch dataset\n",
        "\n",
        "**What to inspect:** Before moving on, inspect shapes, date ranges, metric values, segment behavior, or generated tables. If a value looks surprisingly good, surprisingly bad, or unavailable at forecast time, treat that as a modeling clue.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "set_seed(SEED)\n",
        "df = generate_daily_demand(n_stores=4, n_items=6, periods=520, seed=SEED)\n",
        "X, y, meta = make_sequence_dataset(df, lookback=56, horizon=14)\n",
        "\n",
        "target_start = pd.to_datetime(meta[\"target_start\"])\n",
        "train_idx = target_start < target_start.quantile(0.70)\n",
        "valid_idx = (target_start >= target_start.quantile(0.70)) & (target_start < target_start.quantile(0.85))\n",
        "\n",
        "x_mean = X[train_idx].mean(axis=(0, 1), keepdims=True)\n",
        "x_std = X[train_idx].std(axis=(0, 1), keepdims=True)\n",
        "x_std = np.where(x_std == 0, 1, x_std)\n",
        "X_scaled = (X - x_mean) / x_std\n",
        "\n",
        "train_ds = TensorDataset(torch.tensor(X_scaled[train_idx]).float(), torch.tensor(np.log1p(y[train_idx])).float())\n",
        "valid_ds = TensorDataset(torch.tensor(X_scaled[valid_idx]).float(), torch.tensor(np.log1p(y[valid_idx])).float())\n",
        "train_loader = DataLoader(train_ds, batch_size=256, shuffle=True)\n",
        "valid_loader = DataLoader(valid_ds, batch_size=512)\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Code Cell 3 Explanation\n",
        "\n",
        "**What this cell does:** This cell advances **Convolutions for Temporal Patterns** by working on model definition. It creates or updates `model`, `xb`, `yb` and prepares evidence that later cells use rather than hiding state in prose.\n",
        "\n",
        "**Why it matters:** The goal is tied to this lesson's forecasting focus: Use 1D convolutions to extract local weekly and promotional demand patterns from sequences. Each object is kept inspectable so the learner can connect code, assumptions, and modeling consequences.\n",
        "\n",
        "**Key operations to notice:** extracts local temporal patterns from demand history\n",
        "\n",
        "**What to inspect:** Before moving on, inspect shapes, date ranges, metric values, segment behavior, or generated tables. If a value looks surprisingly good, surprisingly bad, or unavailable at forecast time, treat that as a modeling clue.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "class TemporalCNN(nn.Module):\n",
        "    def __init__(self, n_features, horizon):\n",
        "        super().__init__()\n",
        "        self.net = nn.Sequential(\n",
        "            nn.Conv1d(n_features, 32, kernel_size=3, padding=1),\n",
        "            nn.ReLU(),\n",
        "            nn.Conv1d(32, 64, kernel_size=7, padding=3),\n",
        "            nn.ReLU(),\n",
        "            nn.AdaptiveAvgPool1d(1),\n",
        "        )\n",
        "        self.head = nn.Linear(64, horizon)\n",
        "\n",
        "    def forward(self, x):\n",
        "        x = x.transpose(1, 2)\n",
        "        x = self.net(x).squeeze(-1)\n",
        "        return self.head(x)\n",
        "\n",
        "model = TemporalCNN(n_features=X.shape[-1], horizon=y.shape[-1])\n",
        "xb, yb = next(iter(train_loader))\n",
        "model(xb).shape, yb.shape\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Code Cell 4 Explanation\n",
        "\n",
        "**What this cell does:** This cell advances **Convolutions for Temporal Patterns** by working on forecast metrics, model training. It creates or updates `optimizer`, `loss_fn`, `pred`, `actual` and prepares evidence that later cells use rather than hiding state in prose.\n",
        "\n",
        "**Why it matters:** The goal is tied to this lesson's forecasting focus: Use 1D convolutions to extract local weekly and promotional demand patterns from sequences. Each object is kept inspectable so the learner can connect code, assumptions, and modeling consequences.\n",
        "\n",
        "**Key operations to notice:** evaluates scale-weighted forecast error; updates model weights after backpropagation; computes gradients for model parameters; runs inference without tracking gradients\n",
        "\n",
        "**What to inspect:** Before moving on, inspect shapes, date ranges, metric values, segment behavior, or generated tables. If a value looks surprisingly good, surprisingly bad, or unavailable at forecast time, treat that as a modeling clue.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "optimizer = torch.optim.AdamW(model.parameters(), lr=1e-3, weight_decay=1e-4)\n",
        "loss_fn = nn.L1Loss()\n",
        "\n",
        "for epoch in range(4):\n",
        "    model.train()\n",
        "    for xb, yb in train_loader:\n",
        "        optimizer.zero_grad(set_to_none=True)\n",
        "        loss = loss_fn(model(xb), yb)\n",
        "        loss.backward()\n",
        "        optimizer.step()\n",
        "\n",
        "model.eval()\n",
        "with torch.no_grad():\n",
        "    preds, actuals = [], []\n",
        "    for xb, yb in valid_loader:\n",
        "        preds.append(model(xb).numpy())\n",
        "        actuals.append(yb.numpy())\n",
        "\n",
        "pred = np.expm1(np.vstack(preds))\n",
        "actual = np.expm1(np.vstack(actuals))\n",
        "{\"valid_wmape_all_horizons\": wmape(actual.ravel(), pred.ravel())}\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Lesson Recap\n",
        "\n",
        "In this lesson, we used 4 runnable code cells to work through **Convolutions for Temporal Patterns**. The practical focus was: Use 1D convolutions to extract local weekly and promotional demand patterns from sequences.\n",
        "\n",
        "We did this because deep learning for demand forecasting is not only about choosing an architecture. The learner needs to understand the data contract, the target and horizon, the validation path, the metric, and the operational decision supported by the forecast.\n",
        "\n",
        "The core takeaways were:\n",
        "\n",
        "- Translate ConvNet intuition from images to temporal windows.\n",
        "- Track tensor shape changes through Conv1d layers.\n",
        "- Train a direct multi-horizon CNN forecaster.\n",
        "\n",
        "By the end, you should be able to explain not just what the code produced, but why each step was necessary and what could go wrong if the same step were skipped or performed with leakage.\n",
        "\n",
        "**Senior-readiness reminder:** You can explain when temporal convolutions are a strong baseline for sequence forecasting.\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Exercises\n",
        "\n",
        "- Change kernel size 7 to 14 and explain whether it helps weekly pattern recognition.\n",
        "- Add a residual connection around the convolution stack.\n",
        "- Compute WMAPE by forecast horizon from 1 through 14.\n"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python (deep-learning-demand)",
      "language": "python",
      "name": "deep-learning-demand"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.12"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}
