English version

← Learning Hub Lektion 8 Dauer: ca. 13 Min. Thema: Ausgabe & Integration

Gold Layer & Bereitstellung nach außen

Diese Lektion behandelt die letzte Stufe: die Prognoseausgabe im Gold Layer strukturieren, Konfidenzintervalle berechnen, von der Turbine auf Parkebene aggregieren und die Vorhersagen an nachgelagerte Systeme liefern (Handelstische, Fahrplanmanagement, Netzbetreiber). Am Ende weißt du, wie du die Gold-Tabelle entwirfst und über welche Integrationsmuster du sie bereitstellst.

Was Gold liefern muss

Deine nachgelagerten Verbraucher interessieren sich nicht für Bronze, Silver oder Features. Sie brauchen genau eines: eine verlässliche Prognose mit einem klaren Vertrag.

VerbraucherBedarfAktualitäts-SLA
Handelstisch MW-Prognose auf Parkebene, 72 h voraus, mit Konfidenzbändern Verfügbar innerhalb 1 h nach neuem Wettermodell
Fahrplanmanagement Prognose je Park in 15-Minuten-Auflösung Vor Schluss des Day-Ahead-Marktes (10:00 UTC)
Meldung an den Netzbetreiber Aggregierte Portfolio-Prognose Täglich bis 06:00 UTC
Interne Analysten Vergleich Prognose vs. Ist, Trends der Modellgüte Nahezu in Echtzeit (Minuten)

Schema der Gold-Tabelle

CREATE TABLE prod.gold.forecast_ts (
  asset_id            STRING NOT NULL,
  asset_type          STRING NOT NULL,        -- "turbine" oder "wind_park"
  timestamp           TIMESTAMP NOT NULL,     -- Gültigkeitszeitpunkt der Prognose
  forecast_power_kw   DOUBLE NOT NULL,
  confidence_low_kw   DOUBLE,                 -- P10 (10. Perzentil)
  confidence_high_kw  DOUBLE,                 -- P90 (90. Perzentil)
  capacity_kw         DOUBLE,                 -- für die Berechnung des Kapazitätsfaktors
  capacity_factor     DOUBLE,                 -- forecast_power / capacity
  forecast_ref_time   TIMESTAMP NOT NULL,     -- welcher Wettermodell-Lauf
  model_version       STRING NOT NULL,        -- URI des MLflow-Modells
  produced_at         TIMESTAMP NOT NULL,     -- wann diese Zeile berechnet wurde
  source_model        STRING                  -- verwendetes Wettermodell
)
USING DELTA
PARTITIONED BY (asset_id)
TBLPROPERTIES (
  'delta.autoOptimize.optimizeWrite' = 'true',
  'delta.autoOptimize.autoCompact'   = 'true'
);
Die Gold-Tabelle ist ein Vertrag

Diese Tabelle ist deine API. Nachgelagerte Systeme hängen an diesem Schema. Änderungen daran sind Breaking Changes und müssen abgestimmt werden. Dokumentiere sie (Tabellenkommentare, Beschreibungen im Unity Catalog), versioniere sie und ändere sie nur über einen kontrollierten Prozess. Nimm produced_at und model_version mit auf, damit jede Zeile lückenlos nachvollziehbar ist.

Aggregation Turbine → Park

Das ML-Modell prognostiziert je Turbine. Nachgelagerte Systeme brauchen oft Aggregate auf Parkebene. Berechne beides und lege es in derselben Tabelle ab:

from pyspark.sql import functions as F

def aggregate_to_park(turbine_forecasts, dim_assets):
    """
    Aggregate turbine-level forecasts to park level.
    Returns park-level rows to append alongside turbine rows in Gold.
    """
    # Join, um den übergeordneten Park zu ermitteln
    with_park = turbine_forecasts.join(
        dim_assets.select("asset_id", "parent_asset_id", "capacity_kw"),
        on="asset_id",
        how="inner"
    ).filter(F.col("parent_asset_id").isNotNull())  # nur Turbinen

    # Aggregation je Park × Zeitstempel
    park_forecast = with_park.groupBy(
        F.col("parent_asset_id").alias("asset_id"),
        "timestamp",
        "forecast_ref_time",
        "model_version",
        "produced_at",
        "source_model"
    ).agg(
        F.sum("forecast_power_kw").alias("forecast_power_kw"),
        F.sum("confidence_low_kw").alias("confidence_low_kw"),
        F.sum("confidence_high_kw").alias("confidence_high_kw"),
        F.sum("capacity_kw").alias("capacity_kw"),
    ).withColumn(
        "asset_type", F.lit("wind_park")
    ).withColumn(
        "capacity_factor", 
        F.col("forecast_power_kw") / F.col("capacity_kw")
    )

    return park_forecast

Warum werden Konfidenzintervalle summiert?

P10/P90 über Turbinen hinweg zu summieren ist eine Vereinfachung. Streng genommen sind Konfidenzintervalle auf Portfolioebene schmaler als die Summe der Einzelintervalle (Diversifikationseffekt). Für eine erste Iteration ist Summieren konservativ (die Unsicherheit wird überschätzt). Für eine Verfeinerung modellierst du die Unsicherheit direkt auf Parkebene.

Konfidenzintervalle

Punktprognosen reichen nicht. Handel und Fahrplanmanagement brauchen eine Quantifizierung der Unsicherheit. Dafür gibt es zwei Wege:

Variante A: Quantilsregression

# Separate Modelle für P10, P50 und P90 trainieren
# LightGBM unterstützt das von Haus aus:
model_p10 = lgb.LGBMRegressor(objective="quantile", alpha=0.10, **params)
model_p50 = lgb.LGBMRegressor(objective="quantile", alpha=0.50, **params)
model_p90 = lgb.LGBMRegressor(objective="quantile", alpha=0.90, **params)

Variante B: Prognoseintervalle aus Residuen

# Historische Prognosefehler zur Schätzung der Intervalle nutzen
residuals = actuals - predictions  # aus dem Validierungsset
p10_offset = np.percentile(residuals, 10)
p90_offset = np.percentile(residuals, 90)
# Bei der Inferenz: confidence_low = prediction + p10_offset
#                   confidence_high = prediction + p90_offset
Empfehlung: Quantilsregression

Die Quantilsregression passt die Unsicherheit an die Bedingungen an — breite Intervalle bei stark schwankendem Wetter, schmale bei stabiler Lage. Der Residuen-Ansatz liefert statische Intervalle, unabhängig von der Wetterlage. Starte mit der Quantilsregression (LightGBM unterstützt sie nativ, ohne Änderung an der Architektur).

Muster für die Bereitstellung

Wie greifen nachgelagerte Systeme auf die Gold-Daten zu?

Integrationsmuster für nachgelagerte Systeme prod.gold.forecast_ts Delta-Tabelle (führende Quelle) SQL Warehouse JDBC/ODBC-Abfrage REST API Statement Execution API Delta Sharing Offen, org-übergreifend Datei-Export CSV/Parquet auf SFTP/Blob
MusterAm besten fürLatenzDatabricks-Funktion
SQL Warehouse (JDBC/ODBC) BI-Tools, interne Dashboards, Ad-hoc-Abfragen Sekunden Serverless SQL Warehouse
REST API Externe Anwendungen (Handelsplattform), die die aktuelle Prognose abrufen Unter einer Sekunde bis wenige Sekunden Statement Execution API oder Model Serving
Delta Sharing Austausch mit Partnern und Netzbetreibern (organisationsübergreifend) Minuten (Polling) Offenes Protokoll, beim Empfänger ist kein Databricks nötig
Datei-Export Altsysteme, die CSV- oder Flat-Files erwarten Abhängig von der Pipeline Job-Task, der nach Blob/SFTP schreibt

Tabelle zur Prognosegüte

Gold sollte außerdem eine Qualitäts- bzw. Monitoring-Tabelle enthalten, die Prognosen mit Ist-Werten vergleicht:

-- Prognosegüte materialisieren, sobald Ist-Werte eintreffen
CREATE OR REPLACE TABLE prod.gold.forecast_accuracy AS
SELECT
  f.asset_id,
  f.timestamp,
  f.forecast_power_kw,
  p.power_kw AS actual_power_kw,
  f.forecast_power_kw - p.power_kw AS error_kw,
  ABS(f.forecast_power_kw - p.power_kw) AS abs_error_kw,
  f.model_version,
  f.forecast_ref_time,
  f.source_model,
  -- Lag der Ist-Wert innerhalb des Konfidenzbandes?
  CASE WHEN p.power_kw BETWEEN f.confidence_low_kw AND f.confidence_high_kw
       THEN TRUE ELSE FALSE END AS within_confidence
FROM prod.gold.forecast_ts f
JOIN prod.silver.power_ts p
  ON f.asset_id = p.asset_id AND f.timestamp = p.timestamp
WHERE f.asset_type = 'turbine'
  AND p.power_kw IS NOT NULL;

Prüfe dein Verständnis

Ein Handelssystem braucht die aktuelle 72-Stunden-Prognose auf Parkebene. Welches Bereitstellungsmuster passt am besten?

Richtig. Eine Abfrage über das SQL Warehouse trifft die richtige Balance: niedrige Latenz (Sekunden), Standardschnittstelle (JDBC), und der Filter steckt direkt in der Abfrage (WHERE asset_type = 'wind_park' AND timestamp > NOW()). Die Daten liegen bereits vorberechnet in Gold — Echtzeit-Inferenz ist nicht nötig.
Das SQL Warehouse per JDBC ist hier die beste Wahl. Die Prognosen werden stündlich in Gold vorberechnet — das Handelssystem muss nur den jeweils aktuellen Stand lesen. Echtzeit-Serving wäre überdimensioniert (zwischen zwei Pipeline-Läufen ändern sich die Daten nicht), und der Datei-Export bringt unnötige Latenz und Fehlerquellen.

Warum sollte Gold die Spalten produced_at und model_version enthalten?

Richtig. Nachvollziehbarkeit ist essenziell. Wenn eine Prognose falsch aussieht, musst du beantworten können: „Welche Modellversion hat sie erzeugt? Wann wurde sie berechnet? Welche Wetterdaten sind eingeflossen?“ produced_at und model_version liefern den Audit-Trail und ermöglichen eine Ursachenanalyse.
Nachvollziehbarkeit ist die Antwort. Diese Spalten erzeugen einen Audit-Trail für jede einzelne Prognose. Wenn nachgelagert etwas schiefgeht, kannst du zurückverfolgen: model_version → MLflow → Trainingsdaten → Feature Store → Silver → Bronze — vollständige Lineage.

Die Konfidenzintervalle eines Parks entstehen durch Summieren der P10/P90-Werte je Turbine. Warum ist das konservativ?

Genau. Die Prognosefehler einzelner Turbinen sind nicht perfekt korreliert — wird eine Turbine überschätzt, wird eine andere vielleicht unterschätzt. Auf Portfolio- bzw. Parkebene heben sich diese Fehler teilweise auf, sodass die tatsächliche Unsicherheit schmaler ist als die schlichte Summe der Einzelintervalle.
Der Diversifikationseffekt. Prognosefehler einzelner Turbinen sind nicht perfekt korreliert — beim Aggregieren gleichen sie sich teilweise aus. Das echte Konfidenzintervall auf Parkebene ist schmaler als die Summe der Einzelintervalle. Summieren überschätzt die Unsicherheit (konservativ = sicher für Handelsentscheidungen).

Primärquelle

📖 Empfohlene Lektüre

Dokumentation zu Delta Sharing — das offene Protokoll für organisationsübergreifenden Datenaustausch. Sieh dir außerdem SQL Warehouses an, um die Compute-Ressourcen für nachgelagerte Abfragen zu konfigurieren.

Wie geht es weiter

Die Pipeline steht durchgängig. Die nächste Lektion behandelt Workflows & Orchestrierung — wie du alle Stufen zu einem produktionsreifen Workflow mit Abhängigkeiten, Retries, Alerting und Zeitplan verdrahtest.


Fragen zum Aufsetzen von Delta Sharing, zu KPIs für die Prognosegüte oder zum Umgang mit negativen Prognosewerten (physikalisch unmöglich)? Frag deinen Lehrer.

Rechtliches: Impressum