Dieses Projekt prognostiziert die Stromerzeugung einer Windkraftanlage mithilfe von Wetterdaten und Machine-Learning-Verfahren. Ziel war es, ein vollständiges End-to-End-System zu entwickeln – von der Datenbeschaffung über das Modelltraining bis hin zur produktionsreifen Bereitstellung in der Cloud.
Im Mittelpunkt des Projekts steht die Windkraftanlage AN Windenergie SWT-0.6-44 MK4 mit einer Nennleistung von 600 kW. Die Anlage befindet sich in einem Windpark an der A27 bei Bremen (Nabenhöhe 58 m, Rotordurchmesser 44 m, Baujahr 1997). Für die Vorhersage der Energieerzeugung werden historische ERA5-Reanalyse-Daten verwendet, die über die Open-Meteo-API bezogen werden.
Das Projekt bildet den gesamten Machine-Learning-Lebenszyklus ab und orientiert sich an modernen MLOps- und Software-Engineering-Standards.
- Git-Repository initialisiert, saubere Ordnerstruktur angelegt (
data/,src/,notebooks/,tests/,models/) - Conda-Umgebung (
wind, Python 3.11) mit versioniertemrequirements.txt config.yamlals zentrale Konfigurationsdatei: Turbinenparameter, Koordinaten, Zeitraum – kein Hardcoding von Werten im Code- Koordinaten der Anlage über Google Maps und OpenStreetMap kreuzvalidiert (53.1165290, 8.8298645)
src/ingest.py: Ruft historische Wetterdaten (2020–2025, stündlich, 52.608 Werte) von der Open-Meteo-API ab- Windgeschwindigkeit explizit in m/s angefordert (Standard der API ist km/h – ein klassischer Stolperstein, der durch einen Sanity-Check mit Plausibilitätsprüfung der Werte aufgedeckt wurde)
- Rohdaten werden unverändert als zeitgestempelte JSON-Dateien in
data/raw/gespeichert – nie überschrieben, jederzeit reproduzierbar
src/transform.py: Umrechnung der Windgeschwindigkeit von Messhöhe (10 m/100 m) auf Nabenhöhe (58 m) mittels Power Law (Hellmann-Exponent), dynamisch pro Stunde berechnet aus den zwei verfügbaren Messhöhen- Randfälle (Windstille, führt zu unendlichem Exponenten) werden durch Median-Imputation robust behandelt – dokumentiert im Code, keine stille Datenkorruption
- Bias-Korrektur: ERA5 unterschätzt die lokale Windgeschwindigkeit systematisch (Gitterauflösung ~9 km glättet lokale Effekte). Korrektur auf das Referenzmittel des Global Wind Atlas (6,2 m/s) mittels konstantem Faktor – Limitation transparent dokumentiert
- Leistungskurve: Physikalisch hergeleitet aus der Windleistungsformel (P ∝ v³, Betz-Theorie), da keine Herstellerkurve verfügbar war. Cut-in-, Rated- und Cut-out-Bereiche berücksichtigt
- Ergebnis:
data/processed/dataset.parquet– ein sauberer, dokumentierter Datensatz mit Windgeschwindigkeit und Leistung
- Windverteilung folgt einer Weibull-Verteilung (rechtsschief, Mittelwert > Median)
- Klare Saisonalität: Winterhalbjahr deutlich windreicher als Sommer (atlantische Tiefdruckgebiete vs. Azorenhoch)
src/features.py: Erzeugung von Lag-Features (t-1 bis t-48), Rolling-Statistiken (gleitender Mittelwert/Standardabweichung) und zyklisch kodierten Zeitmerkmalen (Sin/Cos für Stunde und Monat – verhindert, dass z. B. Dezember und Januar als maximal unähnlich behandelt werden)- Zielvariable: Leistung 24 Stunden in der Zukunft (
target_24h)
Chronologischer Train/Test-Split (2020–2024 Training, 2025 Test) – kein zufälliger Split, da dieser bei Zeitreihen zu Data Leakage führen würde.
| Modell | nMAE | Bewertungsmethode |
|---|---|---|
| Persistence (naive Baseline) | 17,74 % | Statisch |
| Seasonal Naive | 19,64 % | Statisch |
| XGBoost (Gradient Boosting) | 15,82 % | Statisch |
| LSTM (Deep Learning, PyTorch) | 15,65 % | Statisch |
| SARIMA (klassische Zeitreihenstatistik) | 13,69 % | Walk-Forward (tägliches Update) |
Wichtige Erkenntnis: SARIMA erzielte das beste Ergebnis, profitiert dabei aber vom Walk-Forward-Verfahren (tägliche Aktualisierung mit echten Beobachtungen). XGBoost und LSTM wurden statisch bewertet. Ein vollständig fairer Vergleich würde ein äquivalentes Update-Verfahren für alle Modelle erfordern – diese Einschränkung wird bewusst offengelegt statt verschwiegen.
Für alle Modelle wurde konsequent auf Data Leakage geachtet: zeitreihenkorrekte Kreuzvalidierung (TimeSeriesSplit), kein Blick in die Zukunft bei Rolling-Fenstern, getrennte Feature-Berechnung für Train und Test.
src/api.py: FastAPI-Service mit/predict- und/health-Endpunkt- Bewusste Modellwahl für das Serving: XGBoost statt SARIMA – trotz leicht schlechterem nMAE, da XGBoost zustandslos (stateless) ist und sich damit für eine einfache, skalierbare API eignet, während SARIMA ein tägliches Update-Verfahren benötigen würde
- Kein Training-Serving-Skew: Die Feature-Berechnung im API-Endpunkt nutzt exakt dieselben Funktionen wie das Training (
src/features.py) – Client muss nur Rohdaten (Windgeschwindigkeit, Leistung der letzten 48h) senden, keine internen Features - Automatisierte Tests (
pytest) prüfen Health-Check, Plausibilität der Vorhersage und korrekte Fehlerbehandlung bei unvollständigen Eingaben - Containerisierung mit Docker: Eigene, minimale
requirements-api.txt(ohne Jupyter/TensorFlow/W&B) für schlanke, schnelle Images
- AWS EC2 (Frankfurt, t2.micro, Free Tier): Docker-Container erfolgreich deployt und öffentlich erreichbar gemacht
- CI/CD mit GitHub Actions: Bei jedem Push werden automatisch alle Tests in einer isolierten Ubuntu-Umgebung ausgeführt – Codequalität wird kontinuierlich abgesichert
- Streamlit-Dashboard (
src/dashboard.py): Interaktive Oberfläche, die live gegen die deployte API kommuniziert und die letzten 48 Stunden sowie die 24h-Prognose visualisiert - Nach erfolgreichem End-to-End-Test wurde die EC2-Instanz gestoppt, um Kosten zu vermeiden (Wiederinbetriebnahme jederzeit möglich)
| Bereich | Werkzeuge |
|---|---|
| Datenquelle | Open-Meteo API (ERA5-Reanalyse) |
| Datenverarbeitung | pandas, NumPy, PyArrow (Parquet) |
| Modellierung | XGBoost, statsmodels (SARIMA), PyTorch (LSTM), scikit-learn |
| Experiment-Tracking | TensorBoard |
| Serving | FastAPI, Uvicorn, Pydantic |
| Containerisierung | Docker |
| Cloud | AWS EC2 (Free Tier) |
| CI/CD | GitHub Actions, pytest |
| Dashboard | Streamlit |
| Versionierung | Git / GitHub |
wind_power_forecast/
├── config.yaml # Zentrale Konfiguration (Turbine, Zeitraum, Bias-Korrektur)
├── data/
│ ├── raw/ # Unveränderte Rohdaten (zeitgestempelt)
│ └── processed/ # Verarbeitete Datensätze (Parquet)
├── src/
│ ├── config.py # Lädt config.yaml
│ ├── ingest.py # Datenabruf von Open-Meteo
│ ├── transform.py # Windphysik, Bias-Korrektur, Leistungskurve
│ ├── features.py # Feature Engineering (Lags, Rolling, zyklische Zeit)
│ ├── baseline.py # Naive Baselines, Zeitsplit, nMAE-Metrik
│ ├── train_xgboost.py # XGBoost-Training mit TimeSeriesSplit
│ ├── sarima.py # SARIMA mit Walk-Forward-Evaluierung
│ ├── train_lstm.py # LSTM-Training (PyTorch)
│ ├── api.py # FastAPI-Service
│ └── dashboard.py # Streamlit-Dashboard
├── tests/ # Automatisierte Tests (pytest)
├── notebooks/ # Explorative Datenanalyse
├── models/ # Trainierte Modelle, Grafiken
├── Dockerfile # Container-Definition für die API
└── .github/workflows/ # CI-Pipeline
- Reproduzierbarkeit: Rohdaten werden nie verändert, jeder Verarbeitungsschritt ist eine reine Funktion, die aus den Rohdaten neu ausgeführt werden kann
- Kein Data Leakage: Chronologischer Split, zeitreihenkorrekte Kreuzvalidierung, Feature-Fenster ohne Blick in die Zukunft
- Ehrliche Modellbewertung: Jede Limitation (Bias-Korrektur, Walk-Forward-Vorteil von SARIMA, idealisierter Kapazitätsfaktor) wird offen dokumentiert statt verschwiegen
- Trennung von Exploration und Produktion: Notebooks für Exploration,
src/-Module für wiederverwendbaren, testbaren, produktionsreifen Code - Kein Training-Serving-Skew: Identische Funktionen für Feature-Berechnung in Training und API
- Der berechnete Kapazitätsfaktor (~22 %) ist eine idealisierte Obergrenze ohne Verfügbarkeitsverluste, Wake-Effekte oder reale, windabhängige Cp-Kurve
- Die Bias-Korrektur nutzt einen konstanten Faktor, der nur das Mittel, nicht die Form der Windverteilung korrigiert
- Der Modellvergleich in Phase 4 ist nicht vollständig fair, da SARIMA von Walk-Forward-Updates profitiert, während XGBoost und LSTM statisch bewertet wurden
Alle Phasen (0–5) sowie Deployment, Dashboard und CI/CD sind abgeschlossen. Das System wurde erfolgreich End-to-End getestet: von der Datenbeschaffung bis zur öffentlich erreichbaren, containerisierten API in der Cloud.