Kapitel 15: Predict-then-Optimize — die bessere Prognose, die schlechtere Entscheidung

📌 Kapitel auf einen Blick

Worum geht es? Um die Naht zwischen zwei Welten, die in getrennten Abteilungen sitzen: Ein Modell prognostiziert, ein anderes entscheidet. Beide arbeiten sauber — und genau an der Naht entsteht ein Fehler, den keine der beiden Seiten sieht.

Voraussetzungen: Kapitel 12, insbesondere das Newsvendor-Problem und das kritische Verhältnis. Etwas Regression hilft, ist aber nicht nötig.

Danach können Sie: begründen, warum ein Prognosemodell mit kleinerem MSE teurere Entscheidungen erzeugen kann; die richtige Zielgröße einer Prognose bestimmen; und Prognosemodelle an Entscheidungskosten statt an Fehlermaßen bewerten.

Zeitbedarf: ca. 3,5 Stunden.

Programme:
Predict_then_Optimize.py

Notebook: prognose.ipynb — herunterladen und in Jupyter öffnen, in Colab hochladen oder mit dem Kurs-Image starten


15.1 In 5 Minuten gelöst

🚀 In 5 Minuten gelöst: Der Durchschnitt ist die falsche Zahl

Eine Bäckerei kennt die Nachfrage der letzten 20 Tage. Ein Brot bringt 6 € Marge, ein übriges kostet 3 € Einkauf. Wie viele soll sie ansetzen?

Die naheliegende Antwort — den Durchschnitt — ist nachweislich falsch:

import numpy as np

nachfrage = np.array([104, 138, 96, 151, 118, 127, 143, 109, 162, 121,   # 20 Tage
                      133, 115, 148, 102, 129, 156, 111, 140, 124, 135])
preis, einkauf = 9.0, 3.0
fehl, ueber = preis - einkauf, einkauf          # 6 EUR zu wenig, 3 EUR zu viel

kosten = lambda menge: (fehl * np.maximum(0, nachfrage - menge)
                        + ueber * np.maximum(0, menge - nachfrage)).mean()

erwartungswert = round(nachfrage.mean())
kritisch = round(np.quantile(nachfrage, fehl / (fehl + ueber)))
print(f"Erwartungswert bestellen: {erwartungswert} Stueck -> {kosten(erwartungswert):5.2f} EUR/Tag")
print(f"kritisches Quantil      : {kritisch} Stueck -> {kosten(kritisch):5.2f} EUR/Tag")
print(f"bestmoegliche feste Menge: {int(np.argmin([kosten(m) for m in range(200)]))} Stueck"
      f" -> {min(kosten(m) for m in range(200)):5.2f} EUR/Tag")

Ausgabe:

Erwartungswert bestellen: 128 Stueck -> 69.45 EUR/Tag
kritisches Quantil      : 137 Stueck -> 62.25 EUR/Tag
bestmoegliche feste Menge: 138 Stueck -> 62.10 EUR/Tag

Und jetzt der Punkt. Der Durchschnitt ist perfekt bestimmt — es ist der Mittelwert derselben Daten, aus denen auch das Quantil kommt. Trotzdem kostet er 7,20 € je Tag mehr, gut 10 %.

Der Grund steht in den Preisen: Ein fehlendes Brot kostet doppelt so viel wie ein übriges. Bei asymmetrischen Kosten liegt die optimale Menge nicht in der Mitte der Verteilung, sondern beim kritischen Verhältnis c_-/(c_- + c_+) = 6/9 = 66{,}7\,\% — das kennen Sie aus Kapitel 12.

Bemerkenswert ist die dritte Zeile: Die Quantilregel trifft mit 137 fast genau die beste überhaupt mögliche feste Menge (138). Sie ist nicht ungefähr richtig, sie ist richtig.

🎯 Merksatz Eine Prognose ist kein Selbstzweck. Sie liefert eine Zahl, die in eine Entscheidung eingeht — und welche Zahl das sein muss, bestimmt die Entscheidung, nicht die Statistik.

Warum funktioniert das? Weil die Kostenfunktion des Newsvendors geknickt und unsymmetrisch ist. Der Mittelwert minimiert den quadratischen Fehler; die Kosten sind aber stückweise linear mit verschiedenen Steigungen nach oben und unten. Zwei verschiedene Zielfunktionen haben zwei verschiedene Optima — das ist kein Paradox, sondern Arithmetik.


15.2 Lernziele

Nach diesem Kapitel können Sie …

  1. … die Zweiteilung predict / optimize benennen und sagen, wo dabei Information verloren geht.
  2. … begründen, warum das MSE-beste Modell nicht das kostenbeste sein muss.
  3. … die richtige Prognosegröße aus der Entscheidung ableiten statt aus Gewohnheit.
  4. … erklären, warum ein pauschaler Sicherheitszuschlag schwächer ist als ein Modell, das die Unsicherheit aus den Merkmalen liest.
  5. … Prognosemodelle an Entscheidungskosten messen — und wissen, wie viele Testdaten ein solcher Vergleich braucht.

15.3 Die Naht zwischen zwei Abteilungen

In fast jedem Unternehmen sieht der Ablauf so aus:

   Verkaufsdaten  ->  [ PROGNOSE ]  ->  Nachfrageschätzung  ->  [ PLANUNG ]  ->  Bestellung
                        Data Science                              Disposition

Beide Seiten arbeiten sorgfältig. Die Prognoseabteilung optimiert ihr Modell auf ein Fehlermaß — meist den mittleren quadratischen Fehler (MSE) oder MAPE — und berichtet stolz eine Verbesserung von 12 %. Die Disposition nimmt die Zahl entgegen und rechnet ihre Bestellmenge aus.

Der Fehler steckt nicht in einer der beiden Hälften, sondern in der Naht:

📖 Definition: Predict-then-Optimize

Das übliche zweistufige Vorgehen: erst eine unbekannte Größe schätzen, dann mit der Schätzung optimieren, als wäre sie die Wahrheit.

Das Problem: Die erste Stufe wird auf ein statistisches Maß trainiert, die zweite erzeugt ökonomische Kosten. Niemand garantiert, dass ein besseres statistisches Maß zu geringeren Kosten führt — und in diesem Kapitel ist es nachweislich umgekehrt.

⚠️ Warum das nicht auffällt

Beide Abteilungen erfüllen ihre Kennzahl. Die Prognose wird besser (MSE sinkt), die Disposition arbeitet korrekt (sie wendet die Formel richtig an). Es gibt keinen Ort, an dem der Verlust sichtbar würde — außer man misst die Entscheidungskosten, und das ist niemandes Kennzahl.

Das ist dieselbe Struktur wie in Abschnitt 23.9: Der Fehler entsteht zwischen zwei Zuständigkeiten und wird deshalb von keiner Prüfung gefunden, die innerhalb einer der beiden liegt.


15.4 Das Programm

Dieselbe Bäckerei, aber mit Merkmalen: Wochentag, Temperatur, Aktionstage. Die Nachfrage muss jetzt modelliert werden.

Eine Eigenschaft der Daten ist dabei entscheidend und in der Praxis der Normalfall: An Aktionstagen ist die Nachfrage nicht nur höher, sondern auch viel unsicherer (heteroskedastisch).

#!/usr/bin/env python3

# Predict_then_Optimize.py
"""
Kapitel Prognose: Die bessere Prognose trifft die schlechtere Entscheidung.

Eine Baeckerei muss jeden Abend entscheiden, wie viel sie fuer den naechsten Tag
ansetzt. Zu wenig kostet die Marge des entgangenen Verkaufs, zu viel kostet den
Einkaufspreis der Retoure. Das ist das Newsvendor-Problem aus dem Kapitel
Unsicherheit - nur dass die Nachfrage diesmal nicht aus einer Verteilung kommt,
sondern PROGNOSTIZIERT werden muss: aus Wochentag, Temperatur und Aktionstagen.

Damit zerfaellt die Aufgabe in zwei Schritte, und genau an der Naht entsteht der
Fehler, um den es hier geht:

    PREDICT     ein Modell schaetzt die Nachfrage
    OPTIMIZE    daraus wird eine Bestellmenge

Der Prognostiker optimiert seinen Modellfehler, meist den MSE. Der Planer traegt
die Kosten. Beide messen etwas anderes - und die beiden Masse widersprechen
einander. Das Programm zeigt:

  1. Vier Verfahren, verglichen nach MSE UND nach Entscheidungskosten. Das
     Verfahren mit dem BESTEN MSE hat die HOECHSTEN Kosten.
  2. Warum ein pauschaler Sicherheitszuschlag zu kurz greift - die Streuung der
     Nachfrage haengt selbst von den Merkmalen ab.
  3. Eine Messfalle, in die der Autor dieses Programms zuerst selbst getappt
     ist: Bei kurzen Testzeitraeumen ist der MSE-Vergleich nicht stabil.

Benoetigt: numpy, scipy, scikit-learn
"""

from __future__ import annotations

import numpy as np
from scipy.stats import norm
from sklearn.linear_model import LinearRegression, QuantileRegressor

VERKAUFSPREIS = 9.0
EINKAUFSPREIS = 3.0
KOSTEN_FEHLMENGE = VERKAUFSPREIS - EINKAUFSPREIS      # entgangene Marge: 6 EUR
KOSTEN_UEBERHANG = EINKAUFSPREIS                      # Retoure:           3 EUR
KRITISCHES_VERHAELTNIS = KOSTEN_FEHLMENGE / (KOSTEN_FEHLMENGE + KOSTEN_UEBERHANG)

TAGE = 5000               # Simulation, siehe Hinweis unten
TRAINING = 1000
SAAT = 11


def erzeuge_daten(tage: int = TAGE, saat: int = SAAT):
    """Taegliche Nachfrage mit Wochentag, Temperatur und Aktionstagen.

    Die entscheidende Eigenschaft steckt in 'streuung': An Aktionstagen ist die
    Nachfrage nicht nur hoeher, sondern auch viel UNSICHERER. Solche
    heteroskedastischen Daten sind der Normalfall - und der Grund, warum ein
    pauschaler Sicherheitszuschlag nicht genuegt.
    """
    rng = np.random.default_rng(saat)
    wochentag = np.arange(tage) % 7
    temperatur = (12 + 10 * np.sin(2 * np.pi * np.arange(tage) / 365)
                  + rng.normal(0, 3, tage))
    aktion = (rng.random(tage) < 0.15).astype(float)

    merkmale = np.column_stack([np.eye(7)[wochentag][:, 1:], temperatur, aktion])
    erwartung = (120
                 + np.eye(7)[wochentag] @ np.array([0, 10, 12, 14, 18, 35, -40])
                 + 1.8 * temperatur + 45 * aktion)
    streuung = 8 + 22 * aktion
    nachfrage = np.maximum(0.0, erwartung + rng.normal(0, 1, tage) * streuung)
    return merkmale, nachfrage, aktion


def tageskosten(bestellung: np.ndarray, nachfrage: np.ndarray) -> float:
    """Die Zahl, auf die es ankommt - und die kein Prognosemass kennt."""
    fehlmenge = np.maximum(0.0, nachfrage - bestellung)
    ueberhang = np.maximum(0.0, bestellung - nachfrage)
    return float((KOSTEN_FEHLMENGE * fehlmenge
                  + KOSTEN_UEBERHANG * ueberhang).mean())


if __name__ == "__main__":
    merkmale, nachfrage, aktion = erzeuge_daten()
    lernen = slice(0, TRAINING)
    pruefen = slice(TRAINING, TAGE)

    print("=" * 84)
    print("  DIE BESSERE PROGNOSE TRIFFT DIE SCHLECHTERE ENTSCHEIDUNG")
    print("=" * 84)
    print(f"Verkaufspreis {VERKAUFSPREIS:.0f} EUR, Einkauf {EINKAUFSPREIS:.0f} EUR.")
    print(f"Fehlmenge kostet {KOSTEN_FEHLMENGE:.0f} EUR, Ueberhang "
          f"{KOSTEN_UEBERHANG:.0f} EUR je Stueck.")
    print(f"Kritisches Verhaeltnis: {KRITISCHES_VERHAELTNIS:.3f} - der Planer sollte "
          f"also das")
    print(f"{KRITISCHES_VERHAELTNIS:.1%}-Quantil der Nachfrage bestellen, nicht ihren "
          f"Erwartungswert.")
    print(f"\nTraining: Tag 1 bis {TRAINING}. Bewertung: die restlichen "
          f"{TAGE - TRAINING} Tage.\n")

    # --- Die vier Verfahren ----------------------------------------------
    kleinste_quadrate = LinearRegression().fit(merkmale[lernen], nachfrage[lernen])
    punktprognose = kleinste_quadrate.predict(merkmale[pruefen])

    restfehler = nachfrage[lernen] - kleinste_quadrate.predict(merkmale[lernen])
    pauschalzuschlag = norm.ppf(KRITISCHES_VERHAELTNIS) * restfehler.std()

    # Ein Zuschlag, der nicht aus der Normalverteilung kommt, sondern direkt
    # auf den Trainingsdaten die Kosten minimiert.
    kandidaten = np.linspace(-10.0, 30.0, 401)
    trainingsprognose = kleinste_quadrate.predict(merkmale[lernen])
    kostenzuschlag = float(kandidaten[np.argmin(
        [tageskosten(trainingsprognose + z, nachfrage[lernen]) for z in kandidaten])])

    # Und das Verfahren, das von vornherein das richtige Quantil schaetzt.
    quantilmodell = QuantileRegressor(quantile=KRITISCHES_VERHAELTNIS,
                                      alpha=0.0, solver="highs")
    quantilmodell.fit(merkmale[lernen], nachfrage[lernen])
    quantilprognose = quantilmodell.predict(merkmale[pruefen])

    verfahren = [
        ("bestelle die Punktprognose", punktprognose, punktprognose),
        ("+ Zuschlag aus der Normalverteilung",
         punktprognose, punktprognose + pauschalzuschlag),
        ("+ Zuschlag auf Kosten trainiert",
         punktprognose, punktprognose + kostenzuschlag),
        ("Quantilregression aufs kritische Quantil",
         quantilprognose, quantilprognose),
    ]

    print(f"  {'Verfahren':<42} {'MSE':>9} {'Kosten/Tag':>12} {'gegen Zeile 1':>14}")
    print("  " + "-" * 80)
    ergebnisse = {}
    for name, prognose, bestellung in verfahren:
        mse = float(((prognose - nachfrage[pruefen]) ** 2).mean())
        kosten = tageskosten(bestellung, nachfrage[pruefen])
        ergebnisse[name] = (mse, kosten)
        basis = ergebnisse[verfahren[0][0]][1]
        vergleich = "" if name == verfahren[0][0] else f"{(kosten - basis) / basis:+13.1%}"
        print(f"  {name:<42} {mse:>9.1f} {kosten:>10.2f} EUR {vergleich:>14}")

    bester_mse = min(ergebnisse, key=lambda k: ergebnisse[k][0])
    beste_kosten = min(ergebnisse, key=lambda k: ergebnisse[k][1])
    print(f"\n  bester MSE:      {bester_mse}")
    print(f"  beste Kosten:    {beste_kosten}")
    print(f"\n  Das Verfahren mit dem besten MSE hat die HOECHSTEN Kosten, und das")
    print(f"  Verfahren mit den besten Kosten hat einen um "
          f"{(ergebnisse[beste_kosten][0] / ergebnisse[bester_mse][0] - 1):.0%} SCHLECHTEREN MSE.")
    print("  Wer Prognosemodelle nach MSE auswaehlt, waehlt hier das falsche.")

    # --- Warum der pauschale Zuschlag zu kurz greift ---------------------
    print("\n" + "-" * 84)
    print("Warum ein pauschaler Zuschlag nicht genuegt\n")
    ist_aktion = aktion[pruefen] > 0.5
    print(f"  {'Verfahren':<42} {'normale Tage':>14} {'Aktionstage':>14}")
    print("  " + "-" * 74)
    for name, _, bestellung in verfahren:
        normal = tageskosten(bestellung[~ist_aktion], nachfrage[pruefen][~ist_aktion])
        aktionstag = tageskosten(bestellung[ist_aktion], nachfrage[pruefen][ist_aktion])
        print(f"  {name:<42} {normal:>10.2f} EUR {aktionstag:>10.2f} EUR")

    # Nachgerechnet statt behauptet: Welcher Zuschlag waere je Tagesart richtig?
    aktion_training = aktion[lernen] > 0.5
    z = norm.ppf(KRITISCHES_VERHAELTNIS)
    richtig_normal = z * restfehler[~aktion_training].std()
    richtig_aktion = z * restfehler[aktion_training].std()
    print(f"\n  Der pauschale Zuschlag betraegt {pauschalzuschlag:.1f} Stueck. Aus den")
    print(f"  Trainingsresten getrennt nach Tagesart waere richtig:")
    print(f"    normale Tage : {richtig_normal:5.1f} Stueck")
    print(f"    Aktionstage  : {richtig_aktion:5.1f} Stueck")
    print(f"  Ein Zuschlag fuer alle Tage kann nur einen Mittelweg treffen - hier")
    print(f"  ist er an normalen Tagen {pauschalzuschlag / richtig_normal:.1f}-mal zu gross und an")
    print(f"  Aktionstagen nur {pauschalzuschlag / richtig_aktion:.0%} dessen, was noetig waere.")
    print(f"\n  Die Quantilregression schaetzt das {KRITISCHES_VERHAELTNIS:.1%}-Quantil "
          f"direkt aus den")
    print(f"  Merkmalen und darf deshalb an verschiedenen Tagen verschieden weit")
    print(f"  ueber dem Erwartungswert liegen. Genau das ist der Unterschied")
    print(f"  zwischen 'ein Modell und danach eine Formel' und 'ein Modell, das")
    print(f"  weiss, wofuer es gebraucht wird'.")

    # --- Die Messfalle ---------------------------------------------------
    print("\n" + "-" * 84)
    print("Eine Messfalle, in die der Autor zuerst selbst getappt ist\n")
    print("  Der erste Entwurf dieses Programms bewertete auf 230 Testtagen - ein")
    print("  realistischer Zeitraum. Dort hatte die Quantilregression den BESSEREN")
    print("  MSE, und die ganze Aussage des Kapitels stand auf dem Kopf.")
    print("\n  Wie oft das passiert, laesst sich ausmessen:\n")
    rng = np.random.default_rng(0)
    print(f"  {'Testfenster':>14} {'QR sieht MSE-besser aus':>26}")
    print("  " + "-" * 42)
    for fenster in (180, 365, 730, 2000):
        treffer = 0
        versuche = 400
        for _ in range(versuche):
            start = int(rng.integers(TRAINING, TAGE - fenster))
            ausschnitt = slice(start, start + fenster)
            mse_punkt = ((kleinste_quadrate.predict(merkmale[ausschnitt])
                          - nachfrage[ausschnitt]) ** 2).mean()
            mse_quantil = ((quantilmodell.predict(merkmale[ausschnitt])
                            - nachfrage[ausschnitt]) ** 2).mean()
            treffer += mse_quantil < mse_punkt
        print(f"  {fenster:>10} Tage {treffer / versuche:>24.1%}")

    print("\n  Bei einem halben Jahr Testdaten sieht das schlechtere Modell in gut")
    print("  jedem zehnten Fall besser aus. Das ist keine grosse Zahl - aber wer")
    print("  EINMAL misst, hat genau eine Ziehung aus dieser Verteilung.")
    print("\n  Die Lehre ist nicht 'nimm 4.000 Testtage' - die hat niemand. Sie")
    print("  lautet: Ein Kennzahlenvergleich ohne Angabe seiner Streuung ist keine")
    print("  Aussage. Bei kurzen Zeitraeumen gehoert eine Kreuzvalidierung dazu.")

    print("\n" + "=" * 84)
    print("  WAS MAN DARAUS MITNIMMT")
    print("=" * 84)
    print("Der Prognostiker optimiert den MSE, der Planer traegt die Kosten - und")
    print("die beiden Masse zeigen hier in verschiedene Richtungen. Drei Saetze:")
    print()
    print("  1. Sagen Sie nicht den Erwartungswert vorher, sondern die Groesse, die")
    print("     in die Entscheidung eingeht. Beim Newsvendor ist das das kritische")
    print("     Quantil - und das kann man direkt schaetzen.")
    print("  2. Bewerten Sie Prognosemodelle an den ENTSCHEIDUNGSKOSTEN. Die sind")
    print("     in Euro und damit vergleichbar; ein MSE ist es nicht.")
    print("  3. Ein pauschaler Sicherheitszuschlag ist besser als nichts und")
    print("     schlechter als ein Modell, das die Unsicherheit selbst aus den")
    print("     Merkmalen liest.")
    print()
    print("Der naechste Schritt - Prognosemodelle so zu trainieren, dass sie die")
    print("Entscheidungskosten direkt minimieren (Smart Predict-then-Optimize,")
    print("differenzierbare Optimierungsschichten) - ist Forschungsstand und")
    print("erfordert Bibliotheken wie cvxpylayers. Die dritte Zeile der Tabelle")
    print("oben ist seine einfachste denkbare Form: ein einziger Parameter, auf")
    print("Kosten statt auf Fehler trainiert.")
    print("=" * 84)

Erwartete Ausgabe:

====================================================================================
  DIE BESSERE PROGNOSE TRIFFT DIE SCHLECHTERE ENTSCHEIDUNG
====================================================================================
Verkaufspreis 9 EUR, Einkauf 3 EUR.
Fehlmenge kostet 6 EUR, Ueberhang 3 EUR je Stueck.
Kritisches Verhaeltnis: 0.667 - der Planer sollte also das
66.7%-Quantil der Nachfrage bestellen, nicht ihren Erwartungswert.

Training: Tag 1 bis 1000. Bewertung: die restlichen 4000 Tage.

  Verfahren                                        MSE   Kosten/Tag  gegen Zeile 1
  --------------------------------------------------------------------------------
  bestelle die Punktprognose                     201.0      42.63 EUR               
  + Zuschlag aus der Normalverteilung            201.0      39.51 EUR          -7.3%
  + Zuschlag auf Kosten trainiert                201.0      39.14 EUR          -8.2%
  Quantilregression aufs kritische Quantil       227.6      38.08 EUR         -10.7%

  bester MSE:      bestelle die Punktprognose
  beste Kosten:    Quantilregression aufs kritische Quantil

  Das Verfahren mit dem besten MSE hat die HOECHSTEN Kosten, und das
  Verfahren mit den besten Kosten hat einen um 13% SCHLECHTEREN MSE.
  Wer Prognosemodelle nach MSE auswaehlt, waehlt hier das falsche.

------------------------------------------------------------------------------------
Warum ein pauschaler Zuschlag nicht genuegt

  Verfahren                                    normale Tage    Aktionstage
  --------------------------------------------------------------------------
  bestelle die Punktprognose                      29.67 EUR     108.59 EUR
  + Zuschlag aus der Normalverteilung             27.53 EUR     100.49 EUR
  + Zuschlag auf Kosten trainiert                 26.67 EUR     102.58 EUR
  Quantilregression aufs kritische Quantil        26.49 EUR      97.07 EUR

  Der pauschale Zuschlag betraegt 5.9 Stueck. Aus den
  Trainingsresten getrennt nach Tagesart waere richtig:
    normale Tage :   3.5 Stueck
    Aktionstage  :  12.3 Stueck
  Ein Zuschlag fuer alle Tage kann nur einen Mittelweg treffen - hier
  ist er an normalen Tagen 1.7-mal zu gross und an
  Aktionstagen nur 48% dessen, was noetig waere.

  Die Quantilregression schaetzt das 66.7%-Quantil direkt aus den
  Merkmalen und darf deshalb an verschiedenen Tagen verschieden weit
  ueber dem Erwartungswert liegen. Genau das ist der Unterschied
  zwischen 'ein Modell und danach eine Formel' und 'ein Modell, das
  weiss, wofuer es gebraucht wird'.

------------------------------------------------------------------------------------
Eine Messfalle, in die der Autor zuerst selbst getappt ist

  Der erste Entwurf dieses Programms bewertete auf 230 Testtagen - ein
  realistischer Zeitraum. Dort hatte die Quantilregression den BESSEREN
  MSE, und die ganze Aussage des Kapitels stand auf dem Kopf.

  Wie oft das passiert, laesst sich ausmessen:

     Testfenster    QR sieht MSE-besser aus
  ------------------------------------------
         180 Tage                    11.8%
         365 Tage                     5.5%
         730 Tage                     0.0%
        2000 Tage                     0.0%

  Bei einem halben Jahr Testdaten sieht das schlechtere Modell in gut
  jedem zehnten Fall besser aus. Das ist keine grosse Zahl - aber wer
  EINMAL misst, hat genau eine Ziehung aus dieser Verteilung.

  Die Lehre ist nicht 'nimm 4.000 Testtage' - die hat niemand. Sie
  lautet: Ein Kennzahlenvergleich ohne Angabe seiner Streuung ist keine
  Aussage. Bei kurzen Zeitraeumen gehoert eine Kreuzvalidierung dazu.

====================================================================================
  WAS MAN DARAUS MITNIMMT
====================================================================================
Der Prognostiker optimiert den MSE, der Planer traegt die Kosten - und
die beiden Masse zeigen hier in verschiedene Richtungen. Drei Saetze:

  1. Sagen Sie nicht den Erwartungswert vorher, sondern die Groesse, die
     in die Entscheidung eingeht. Beim Newsvendor ist das das kritische
     Quantil - und das kann man direkt schaetzen.
  2. Bewerten Sie Prognosemodelle an den ENTSCHEIDUNGSKOSTEN. Die sind
     in Euro und damit vergleichbar; ein MSE ist es nicht.
  3. Ein pauschaler Sicherheitszuschlag ist besser als nichts und
     schlechter als ein Modell, das die Unsicherheit selbst aus den
     Merkmalen liest.

Der naechste Schritt - Prognosemodelle so zu trainieren, dass sie die
Entscheidungskosten direkt minimieren (Smart Predict-then-Optimize,
differenzierbare Optimierungsschichten) - ist Forschungsstand und
erfordert Bibliotheken wie cvxpylayers. Die dritte Zeile der Tabelle
oben ist seine einfachste denkbare Form: ein einziger Parameter, auf
Kosten statt auf Fehler trainiert.
====================================================================================

15.5 Der Befund

Verfahren MSE Kosten je Tag
bestelle die Punktprognose 201,0 (bester) 42,63 € (schlechteste)
+ Zuschlag aus der Normalverteilung 201,0 39,51 €
+ Zuschlag auf Kosten trainiert 201,0 39,14 €
Quantilregression aufs kritische Quantil 227,6 (schlechtester) 38,08 € (beste)

Das Modell mit dem besten MSE hat die höchsten Kosten. Das kostenbeste Modell hat einen um 13 % schlechteren MSE — und spart 10,7 % der Kosten.

Wer Prognosemodelle nach MSE auswählt (und das ist die Voreinstellung jeder Modellauswahl-Bibliothek), wählt hier das falsche.

🔤 Formel-Übersetzer: zwei Zielfunktionen

Mathematik Was sie belohnt
\min \sum_t (\hat{d}_t - d_t)^2 „Liege im Mittel richtig.” Abweichungen nach oben und unten zählen gleich, große Abweichungen überproportional.
\min \sum_t \big[c_-(d_t - q_t)^+ + c_+(q_t - d_t)^+\big] „Sei lieber etwas zu großzügig als etwas zu knapp.” Beide Richtungen zählen verschieden und beide nur linear.
(x)^+ „nur der positive Teil” — also \max(0, x).
c_- \ne c_+ Der ganze Grund, warum die beiden Optima auseinanderfallen.

Wären c_- und c_+ gleich groß, läge das Optimum beim Median — immer noch nicht beim Mittelwert, aber wenigstens in der Mitte. Erst die Asymmetrie schiebt es ans Quantil.

Warum ein pauschaler Zuschlag zu kurz greift

Die zweite Zeile der Tabelle ist die verbreitete Praxis: Punktprognose plus Sicherheitsbestand. Sie hilft (−7,3 %), bleibt aber hinter der Quantilregression zurück. Das Programm rechnet nach, warum:

benötigter Zuschlag
normale Tage 3,5 Stück
Aktionstage 12,3 Stück
pauschal verwendet 5,9 Stück

Ein einziger Wert kann nur einen Mittelweg treffen: An normalen Tagen ist er 1,7-mal zu groß, an Aktionstagen erreicht er nur 48 % dessen, was nötig wäre.

Die Quantilregression schätzt das 66,7-%-Quantil direkt aus den Merkmalen und darf deshalb an verschiedenen Tagen verschieden weit über dem Erwartungswert liegen. Das ist der Unterschied zwischen „ein Modell und danach eine Formel” und „ein Modell, das weiß, wofür es gebraucht wird”.

🎯 Merksatz Sagen Sie nicht den Erwartungswert vorher, sondern die Größe, die in die Entscheidung eingeht. Beim Newsvendor ist das das kritische Quantil — und das lässt sich direkt schätzen, statt es nachträglich aus einer Punktprognose zu basteln.


15.6 Eine Messfalle, in die der Autor selbst getappt ist

Der erste Entwurf dieses Programms bewertete auf 230 Testtagen — ein realistischer Zeitraum, gut sieben Monate. Dort hatte die Quantilregression den besseren MSE, und die ganze Aussage dieses Kapitels stand auf dem Kopf.

Das war kein Denkfehler, sondern Zufall. Wie viel Zufall, lässt sich ausmessen:

Testfenster Quantilregression sieht MSE-besser aus
180 Tage 11,8 %
365 Tage 5,5 %
730 Tage 0,0 %
2 000 Tage 0,0 %

Bei einem halben Jahr Testdaten sieht das (bezogen auf den MSE) schlechtere Modell in gut jedem zehnten Fall besser aus. Das ist keine große Zahl — aber wer einmal misst, hat genau eine Ziehung aus dieser Verteilung.

⚠️ Was daraus folgt — und was nicht

Die Lehre ist nicht „nimm 4 000 Testtage”. Die hat niemand; das Programm arbeitet deshalb mit simulierten Daten und sagt das auch.

Die Lehre ist: Ein Kennzahlenvergleich ohne Angabe seiner Streuung ist keine Aussage. Bei kurzen Zeiträumen gehört eine Kreuzvalidierung dazu, und der Unterschied zwischen zwei Modellen muss größer sein als die Schwankung zwischen zwei Zeitfenstern.

Verwandt, aber nicht dasselbe: Kapitel 21, Data_Snooping.py. Dort geht es um die Zahl der Versuche, hier um die Zahl der Beobachtungen. Beide Male führt eine weggelassene Angabe zu einer Aussage, die nicht trägt.


15.7 Wie weit das Verfahren reicht

Die dritte Zeile der Ergebnistabelle — „Zuschlag auf Kosten trainiert” — ist die einfachste denkbare Form einer Idee, die derzeit erforscht wird: das Prognosemodell direkt auf die Entscheidungskosten zu trainieren statt auf ein Fehlermaß.

Hier war es ein einziger Parameter, mit einer Rasterschleife bestimmt. Der allgemeine Fall ist schwieriger, weil die Optimierung zwischen Modell und Kosten steht: Um den Gradienten der Kosten nach den Modellparametern zu bilden, muss man durch das Optimierungsproblem hindurch ableiten.

Ansatz Idee Bibliothek
Richtige Zielgröße (dieses Kapitel) Schätze das Quantil, das die Entscheidung braucht sklearn
SPO+ (Elmachtoub/Grigas) Ein Ersatzverlust, der die Entscheidungskosten nach oben abschätzt und konvex ist
Differenzierbare Optimierung Das Optimierungsproblem wird eine Schicht im neuronalen Netz cvxpylayers

💡 Womit man anfängt

Nicht mit cvxpylayers. Der Ertrag der ersten Zeile ist in der Praxis meist der größte und kostet einen Nachmittag: Fragen Sie, welche Größe die Entscheidung wirklich braucht, und schätzen Sie diese. In diesem Kapitel bringt das 10,7 % — die aufwendigen Verfahren ringen danach um die letzten Prozentpunkte.


15.8 Übungsaufgaben

Lösungen: Abschnitt A.15.

Aufgabe 15.1 ⭐ — Andere Preise. Die Bäckerei kann übrige Ware am Folgetag zum halben Preis abgeben; der Überhang kostet dann nur noch 1,50 € statt 3 €. Wie ändert sich das kritische Verhältnis, und in welche Richtung verschiebt sich die Bestellmenge?

Aufgabe 15.2 ⭐ — Wann ist der Mittelwert richtig? Nennen Sie die Bedingung, unter der Erwartungswert und optimale Bestellmenge zusammenfallen. Wie realistisch ist sie?

Aufgabe 15.3 ⭐⭐ — Die Kennzahl der Prognoseabteilung. Ersetzen Sie im Programm den MSE durch den MAPE (mittlerer absoluter prozentualer Fehler). Ändert sich die Rangfolge der vier Verfahren? Begründen Sie, warum das kein Zufall ist.

Aufgabe 15.4 ⭐⭐ — Zwei Quantile. Schätzen Sie zusätzlich das 5-%- und das 95-%-Quantil und zeichnen Sie für 30 Testtage ein Band um die Punktprognose. An welchen Tagen ist es breit — und passt das zu Ihrer Erwartung?

Aufgabe 15.5 ⭐⭐⭐ — Der Wert der Merkmale. Entfernen Sie das Merkmal „Aktion” aus dem Modell. Wie verschlechtern sich MSE und Kosten — und zwar in unterschiedlichem Ausmaß? Was sagt das über den Wert eines Merkmals aus?


15.9 Finde den Denkfehler

🐛 „Wir haben die Prognose um 18 % verbessert”

Ein Data-Science-Team stellt sein Quartalsergebnis vor:

„Wir haben das alte lineare Modell durch ein Gradient-Boosting-Modell ersetzt. Der MSE > auf den Testdaten ist um 18 % gesunken, der MAPE um 14 %. Das ist die größte > Prognoseverbesserung, die wir je erreicht haben. Die Disposition bekommt ab nächstem > Monat die neuen Werte.”

Sechs Wochen später meldet die Disposition, die Retouren seien gestiegen.

Das Team prüft alles nach: Die Modellgüte stimmt, die Testdaten waren sauber getrennt, es gibt kein Leck. Das neue Modell prognostiziert die Nachfrage tatsächlich deutlich besser als das alte.

Wie kann eine bessere Prognose zu schlechteren Ergebnissen führen — und was hätte das Team messen müssen?

Ein zweiter Hinweis für den zweiten Teil der Antwort: Die Disposition rechnet mit Punktprognose plus einem Sicherheitszuschlag, der aus den Residuen des alten Modells stammt und seit Jahren unverändert ist.


15.10 Micro-Quiz

❓ Drei Fragen

1. Warum ist der Erwartungswert beim Newsvendor die falsche Bestellmenge? a) Weil er statistisch unzuverlässig geschätzt wird. b) Weil die Kosten für Fehlmenge und Überhang verschieden hoch sind und das Optimum deshalb am kritischen Quantil liegt. c) Weil die Nachfrage nicht normalverteilt ist.

2. Ein Kollege schlägt vor, den Sicherheitszuschlag jährlich aus den Residuen neu zu berechnen. Was löst das nicht? a) Nichts — das ist die richtige Lösung. b) Der Zuschlag bleibt für alle Tage gleich, obwohl die Unsicherheit von den Merkmalen abhängt. c) Residuen sind grundsätzlich ungeeignet, um Unsicherheit zu schätzen.

3. Bei 180 Testtagen sieht das MSE-schlechtere Modell in 11,8 % der Fälle besser aus. Was folgt daraus für die Praxis? a) Man braucht mindestens 730 Testtage, sonst ist jeder Vergleich wertlos. b) Ein einzelner Kennzahlenvergleich ohne Streuungsangabe ist keine belastbare Aussage; bei kurzen Zeiträumen gehört eine Kreuzvalidierung dazu. c) Der MSE ist grundsätzlich unbrauchbar.


15.11 Selbsttest

  1. Erklären Sie in zwei Sätzen, warum „predict then optimize” eine Naht hat, an der Information verlorengeht.
  2. Welche Größe muss ein Prognosemodell liefern, wenn die Entscheidung ein Newsvendor ist — und woher kennen Sie den Wert?
  3. Warum ist ein pauschaler Sicherheitszuschlag schwächer als eine Quantilregression? Nennen Sie die Dateneigenschaft, um die es geht.
  4. Ihre Prognoseabteilung meldet 18 % besseren MSE. Welche eine Frage stellen Sie?
  5. Wie viele Testdaten braucht ein Modellvergleich? Formulieren Sie die Antwort ohne eine konkrete Zahl.

15.12 Zusammenfassung

  • Predict-then-Optimize trennt Schätzen und Entscheiden. Die erste Stufe wird auf ein statistisches Maß trainiert, die zweite erzeugt ökonomische Kosten — und die beiden Maße können in verschiedene Richtungen zeigen.
  • Im Beispiel hat das Verfahren mit dem besten MSE die höchsten Kosten; das kostenbeste Verfahren hat einen um 13 % schlechteren MSE und spart 10,7 %.
  • Der Grund ist die Asymmetrie der Kosten: Fehlmenge und Überhang kosten verschieden viel, deshalb liegt das Optimum am kritischen Quantil und nicht am Erwartungswert.
  • Ein pauschaler Sicherheitszuschlag hilft, greift aber zu kurz, wenn die Unsicherheit selbst von den Merkmalen abhängt: Hier wäre er an normalen Tagen 1,7-mal zu groß und an Aktionstagen halb so groß wie nötig.
  • Bewerten Sie Prognosemodelle an den Entscheidungskosten. Sie sind in Euro und damit vergleichbar; ein MSE ist es nicht.
  • Ein Kennzahlenvergleich braucht eine Streuungsangabe. Bei 180 Testtagen sah das schlechtere Modell in gut jedem zehnten Fall besser aus — und wer einmal misst, hat genau eine Ziehung.