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.pyNotebook: 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 …
- … die Zweiteilung predict / optimize benennen und sagen, wo dabei Information verloren geht.
- … begründen, warum das MSE-beste Modell nicht das kostenbeste sein muss.
- … die richtige Prognosegröße aus der Entscheidung ableiten statt aus Gewohnheit.
- … erklären, warum ein pauschaler Sicherheitszuschlag schwächer ist als ein Modell, das die Unsicherheit aus den Merkmalen liest.
- … 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.
====================================================================================
Was Sie in der Abbildung sehen. Links und rechts gewinnt nicht dasselbe Verfahren. Die Punktprognose hat den besten Prognosefehler und die höchsten Kosten — beide Maßstäbe messen etwas anderes, und nur einer davon steht am Ende in der Bilanz.
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
- Erklären Sie in zwei Sätzen, warum „predict then optimize” eine Naht hat, an der Information verlorengeht.
- Welche Größe muss ein Prognosemodell liefern, wenn die Entscheidung ein Newsvendor ist — und woher kennen Sie den Wert?
- Warum ist ein pauschaler Sicherheitszuschlag schwächer als eine Quantilregression? Nennen Sie die Dateneigenschaft, um die es geht.
- Ihre Prognoseabteilung meldet 18 % besseren MSE. Welche eine Frage stellen Sie?
- 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.