8  8 Forecasting-Baselines & Bewertung

9 8 Forecasting-Baselines & Bewertung

Bevor komplexe Modelle eingesetzt werden, sollten immer einfache Baseline-Modelle definiert werden.

Baselines beantworten die Frage:

Ist mein Modell wirklich besser als eine einfache, nachvollziehbare Strategie?


Merke

Ein komplexes Modell ist nur dann sinnvoll, wenn es robuste Baselines klar übertrifft.


9.1 8.1 Beispielzeitreihe

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

rng = np.random.default_rng(123)

t = pd.date_range("2025-01-01", periods=24*14, freq="H")
trend = np.linspace(0, 2, len(t))
season = 0.9 * np.sin(2*np.pi*(t.hour)/24)
noise = 0.3 * rng.normal(size=len(t))

s = pd.Series(20 + trend + season + noise, index=t, name="signal")

s.plot(title="Zeitreihe für Forecasting")
plt.show()
/var/folders/p_/ks3trxjx0jd839_g4g0vm4nc0000gn/T/ipykernel_19421/1656542722.py:7: FutureWarning: 'H' is deprecated and will be removed in a future version, please use 'h' instead.
  t = pd.date_range("2025-01-01", periods=24*14, freq="H")


9.2 8.2 Train-Test-Split

Wir verwenden 80% der Daten für Training und 20% für Test.

split = int(len(s) * 0.8)

train = s.iloc[:split]
test = s.iloc[split:]

train.index.min(), train.index.max(), test.index.min(), test.index.max()
(Timestamp('2025-01-01 00:00:00'),
 Timestamp('2025-01-12 03:00:00'),
 Timestamp('2025-01-12 04:00:00'),
 Timestamp('2025-01-14 23:00:00'))

9.3 8.3 Naive Forecast

Nächster Wert = letzter beobachteter Wert.

naive = test.copy()
naive[:] = train.iloc[-1]
y = s.to_numpy()
train_np = y[:split]
test_np = y[split:]

naive_np = np.full_like(test_np, train_np[-1])

9.4 8.4 Seasonal Naive (24 Stunden)

Nächster Wert = Wert von vor 24 Stunden.

seasonal_naive = s.shift(24).iloc[split:]
seasonal_naive_np = y[split-24:-24]

9.5 8.5 Moving Average Baseline

Forecast = Mittelwert der letzten 24 Stunden.

ma_value = train.iloc[-24:].mean()
ma_forecast = test.copy()
ma_forecast[:] = ma_value
ma_value_np = np.mean(train_np[-24:])
ma_forecast_np = np.full_like(test_np, ma_value_np)

9.6 8.6 Vergleich visualisieren

plt.figure()
train.plot(label="Train")
test.plot(label="Test")
naive.plot(label="Naive")
seasonal_naive.plot(label="Seasonal Naive")
ma_forecast.plot(label="MA(24)")
plt.legend()
plt.title("Forecast Baselines")
plt.show()


9.7 8.7 Fehlermaße

Wir verwenden den Mean Absolute Error (MAE):

[ MAE = |y_{true} - y_{pred}| ]

def mae(y_true, y_pred):
    return np.mean(np.abs(y_true - y_pred))

print("MAE Naive:", mae(test, naive))
print("MAE Seasonal Naive:", mae(test, seasonal_naive))
print("MAE MA(24):", mae(test, ma_forecast))
MAE Naive: 0.6298837368696829
MAE Seasonal Naive: 0.3320412574311427
MAE MA(24): 0.6228787570177815
def mae_np(y_true, y_pred):
    return np.mean(np.abs(y_true - y_pred))

print("MAE Naive:", mae_np(test_np, naive_np))
print("MAE Seasonal:", mae_np(test_np, seasonal_naive_np))
print("MAE MA(24):", mae_np(test_np, ma_forecast_np))
MAE Naive: 0.6298837368696829
MAE Seasonal: 0.3320412574311427
MAE MA(24): 0.6228787570177815

9.8 8.8 Interpretation

Typische Beobachtungen:

  • Bei starker Saison gewinnt häufig Seasonal Naive.
  • Bei schwachem Muster ist Naive oft überraschend stark.
  • Moving Average glättet, reagiert aber träge auf Trendänderungen.

Methodischer Hinweis

Baseline-Vergleich ist keine Formalität, sondern eine Qualitätskontrolle.


9.9 8.9 Mini-Aufgaben

  1. Erhöhen Sie die Stärke der Saison.
    • Welche Baseline gewinnt?
  2. Entfernen Sie die Saison vollständig.
    • Welche Baseline wird besser?
  3. Fügen Sie einen starken Trend hinzu.
    • Wie reagieren die Modelle?