- Python 97.6%
- Dockerfile 2.4%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| examples | ||
| forecast_service | ||
| scripts | ||
| tests | ||
| .dockerignore | ||
| .editorconfig | ||
| .env.example | ||
| .gitattributes | ||
| .gitignore | ||
| compose.yaml | ||
| Dockerfile | ||
| README.md | ||
| requirements-dev.txt | ||
| requirements.txt | ||
| VERIFICATION.md | ||
Forecast Benchmark
Samodzielny serwis do testów treningu, wersjonowania, predykcji i odbioru wyników.
Obejmuje prices, demand, pv, fw i oze (oze uruchamia PV i wiatr).
Nie wymaga importowania projektu nadrzędnego, Databricks, kolejek ani istniejących modeli.
W paczce nie ma danych źródłowych, sekretów ani modeli produkcyjnych.
Struktura repozytorium
forecast_service/ API, pipeline treningu, modele i adaptery danych
forecast_service/sql/ Parametryzowane zapytania do istniejącego schematu SQL
examples/ Payloady train/predict i syntetyczne przykłady wyników
scripts/run_demo.py Kompletny demonstracyjny przebieg wszystkich obszarów
tests/ Testy integracyjne i kontraktowe
.env.example Konfiguracja bez sekretów
Dockerfile Obraz serwisu z LightGBM i sterownikiem ODBC
compose.yaml Lokalne uruchomienie kontenera z trwałym wolumenem
Szybki start
Python 3.10–3.12. Polecenia wykonuj w tym folderze:
python -m venv .venv
.\.venv\Scripts\python.exe -m pip install -r requirements.txt
Copy-Item .env.example .env
.\.venv\Scripts\python.exe -m uvicorn forecast_service.api:app --host 127.0.0.1 --port 8000 --workers 1 --no-access-log
Opis API i interaktywny formularz: http://127.0.0.1:8000/docs.
Domyślnie DATA_SOURCE=synthetic: dane są generowane matematycznie, a modele naprawdę
się trenują. Odpowiedź zawsze podaje źródło danych. Błąd SQL nigdy nie przełącza na dane syntetyczne.
W drugim terminalu, w tym samym folderze:
$trained = Invoke-RestMethod http://127.0.0.1:8000/train -Method Post -ContentType application/json -Body (Get-Content examples/train_prices.json -Raw)
$predicted = Invoke-RestMethod http://127.0.0.1:8000/predict -Method Post -ContentType application/json -Body (Get-Content examples/predict_prices.json -Raw)
$predicted
Invoke-WebRequest ("http://127.0.0.1:8000" + $predicted.outputs[0].url) -OutFile forecast.csv
Analogicznie użyj train_demand.json, predict_demand.json, train_oze.json
i predict_oze.json. Aby uruchomić PV albo wiatr osobno, ustaw stage na pv lub fw.
Model wytrenowany przez oze obsługuje też osobne predykcje pv i fw.
Kompletny test wszystkich obszarów bez serwera i bez dostępu do baz:
.\.venv\Scripts\python.exe scripts/run_demo.py --quick
Bez --quick wykona się pełny wariant 1500 iteracji × 3 ziarna.
Demo jawnie używa danych syntetycznych, niezależnie od .env, i zapisuje wyniki
oraz modele w outputs/demo/. Modele demo nie są automatycznie publikowane w rejestrze API.
Model i obliczenia
Każdy target ma ensemble trzech modeli LightGBM: domyślnie 1500 iteracji,
90 liści, głębokość 10, uczenie 0.025, próbkowanie cech i wierszy oraz regularyzację.
Ceny mają trzy targety: Fix1, Fix2, RB; pozostałe obszary po jednym.
Pełny trening wszystkich obszarów tworzy 18 modeli.
Cechy: kalendarz, cykle dobowe i roczne, pogoda, moc zainstalowana, opóźnienia 24/48/72/168 h oraz średnie i odchylenia kroczące. Modele PV/FW są wspólne dla punktów danej technologii; identyfikatory i współrzędne nie są cechami.
Walidacja odbywa się po okresie treningowym. Prognoza działa w blokach do 24 h: przyszłe wartości rzeczywiste są usuwane, a dalsze opóźnienia korzystają z prognoz. Raport zawiera MAE, RMSE, liczbę obserwacji oraz pokrycie walidacji. Brak wystarczających danych powoduje błąd.
Można zmienić obciążenie w payloadzie treningu:
{
"type": "train",
"stage": "oze",
"train_start": "2026-01-01",
"train_end": "2026-04-01",
"valid_start": "2026-04-01",
"valid_end": "2026-04-08",
"parameters": {
"rounds": 2000,
"num_leaves": 90,
"max_depth": 10,
"learning_rate": 0.025,
"seeds": [42, 43, 44]
}
}
Liczbę wątków ustawia MODEL_THREADS (domyślnie 2).
Zapis modeli: natywne pliki LightGBM .txt i manifest .json.
Żadna ścieżka aplikacji nie odczytuje ani nie zapisuje pickle/joblib.
Endpointy i kontrakt
| Metoda i ścieżka | Działanie |
|---|---|
GET /health |
Stan procesu i źródło danych |
GET /ready |
Gotowość API i liczba zapisanych zestawów modeli; bez sprawdzania połączenia SQL |
POST /train |
Trening, walidacja, zapis modeli i CSV z walidacji |
POST /predict |
Predykcja z zapisanej wersji i eksport CSV |
POST /jobs |
Te same operacje, rozdzielane polem type |
GET /runs/{run_id} |
Wynik ukończonego zadania, metryki i lista plików |
GET /runs/{run_id}/{filename} |
Pobranie pliku wynikowego |
Zadania są synchroniczne. Jeden proces wykonuje jedno zadanie naraz; kolejne dostaje HTTP 409. Na platformie ustaw odpowiedni timeout albo uruchamiaj CLI jako zadanie kontenerowe:
python -m forecast_service examples/train_prices.json
python -m forecast_service examples/predict_prices.json
CLI wypisuje JSON i zwraca niezerowy kod przy błędzie.
Podstawowe pola to type, stage, model_name, model_version, save_model
i zakresy dat. Nieobsługiwane opcje powodują HTTP 422.
model_name to etykieta własnego treningu, domyślnie benchmark; nie wybiera
starych strategii rynkowych. Przy predykcji musi odpowiadać treningowi.
Wszystkie okna są półotwarte [od, do), według Europe/Warsaw.
2026-04-08 → 2026-04-09 oznacza jeden dzień. train_end <= valid_start;
minimum treningu to 14 dni, maksimum prognozy/walidacji wraz z przerwą to 31 dni.
Przy braku dat trening obejmuje 180 dni, potem 7 dni walidacji;
predykcja bez dat obejmuje jutro.
Predykcja wymaga wcześniejszego treningu. Bez model_version wybierany jest najnowszy
kompletny zestaw dla danego źródła danych, nazwy i obszaru.
model_version zwrócone przez trening pozwala przypiąć konkretną wersję.
Nie można prognozować dat wcześniejszych od końca treningu wybranego modelu.
save_model=false daje walidację i wyniki bez rejestracji modelu.
Zmiana źródła, tabel lub klucza pseudonimizacji wymaga ponownego treningu.
Wyniki
outputs/<run_id>/ zawiera CSV i result.json.
API zwraca metryki, wersję modelu, liczbę wierszy, linki i po trzy wiersze podglądu.
Eksporty mają nagłówki zgodne z podstawowymi eksportami projektu:
| Obszar | Najważniejsze kolumny |
|---|---|
| Ceny | DatetimePolska, DatetimeUTC, RetrievalTimestampUTC, Rozdzielczosc, Fix1_pred, Fix2_pred, RB_pred |
| Zapotrzebowanie | PredZuzycieMWh, Actual, WolumenRoczny, TrainingCutoffLabel, Model, ModelName i znaczniki czasu |
| PV/wiatr | EnergyGenerationSiteId, EnergyMeteringPointId, EnergyMeteringPointCode, ForecastMWh, GenerationTechnologyCode, SignatureUTC, Model i pozostałe pola bazowego eksportu produkcji |
Gotowe przykłady wszystkich czterech obszarów są w examples/output/.
Powstały z pełnego treningu na danych syntetycznych; run_report.json zawiera
metryki i czasy tego przebiegu. Nie trzeba trenować modelu, żeby obejrzeć format.
output_resolutions: "60", "15" albo "60,15" (domyślnie).
Każda rozdzielczość ma osobny plik. Prognoza jest godzinowa; kwadranse są rozwinięciem:
ceny są powtarzane, energia w MWh dzielona przez 4. To nie jest natywny model kwadransowy.
Godziny wyznaczane są w UTC, więc doby zmiany czasu mają 23/25 godzin i 92/100 kwadransów.
Do jednoznacznego łączenia wyników używaj DatetimeUTC.
W demand pola Actual i WolumenRoczny są puste — nie eksportujemy pomiarów ani
wolumenów kontraktowych. Rzeczywiste wartości walidacji służą tylko metrykom.
W PV/FW Latitude, Longitude, PowerCurveId, PriceHedgingType i IncludeInPortfolio
są puste. Pole lokalizacji reprezentuje anonimowy punkt pomiarowy, nie oryginalny podział
na wiele instalacji. Nie należy sumować plików 15- i 60-minutowych ze sobą.
Te same bazy danych
W .env lub sekretach platformy ustaw:
DATA_SOURCE=sql
DB_HOST=<adres-twojego-serwera>
DB_NAME=<nazwa-twojej-bazy>
DB_AUTH=azure
ANONYMIZATION_KEY=<wlasny-staly-losowy-sekret-minimum-32-znaki>
Nie są kopiowane istniejące .env, magazyny sekretów ani adresy.
Połączenie używa ODBC Driver 18, szyfrowania i weryfikacji certyfikatu.
DB_AUTH=azure obsługuje standardowy łańcuch Azure Identity, a komplet
AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET wybiera service principal.
Alternatywnie DB_AUTH=sql oraz DB_USER i DB_PASSWORD.
Zasobowi przydziel dostęp do odczytu; aplikacja wykonuje wyłącznie zapytania SELECT.
Zapytania znajdują się w forecast_service/sql/. Zostają nazwy tabel i kolumn,
bo mają pasować do tego samego schematu:
| Dane | Domyślne tabele |
|---|---|
| Ceny | dbo.tge_rdn_kontrakty, dbo.ceny_rynek_bilansujacy |
| Zapotrzebowanie | dbo.iris_zuzycie_rozbicie, dbo.pricing_report, dbo.excluded_clients |
| PV i wiatr | dbo.generation_unit, dbo.energy_generation_site, dbo.energy_metering_point_mapping, dbo.iris_zuzycie_rozbicie |
| Pogoda | dbo.meteo_unified_model_4 |
Zmienne TABLE_* z .env.example pozwalają wskazać schemat.tabela lub
baza.schemat.tabela. Wartości dat są parametrami zapytań; identyfikatory tabel
przechodzą walidację. Odczyt poboru używa Kierunek=1, produkcji Kierunek=2,
technologii PV 2, wiatru 1, zgodnie z zastanym kodem.
Wersja minimalna agreguje pogodę i dane do godzin. Wybiera ostatnią prognozę pogody dostępną do D-1 08:30, dodatkowo ograniczoną datą rozpoczęcia predykcji. Brak pogody pozostaje wartością brakującą obsługiwaną przez LightGBM. Metadane jednostek są aktualnym stanem bazy, a nie pełnym historycznym snapshotem.
Tabele źródłowe podają czas lokalny bez offsetu. Zapytania agregują powtarzającą się jesienną godzinę, a adapter przypisuje ten sam agregat do obu godzin UTC. To ograniczenie testowego adaptera: precyzyjny historyczny backtest wymaga źródłowego UTC lub oznaczenia powtórzonej godziny. Eksport prognoz ma jednoznaczny UTC.
Anonimizacja i granice zakresu
- Nazwy organizacji, hosty, ścieżki użytkowników, identyfikatory chmury, dane klientów i istniejące artefakty nie są częścią paczki.
- Punkty PV/FW dostają stabilny pseudonim HMAC-SHA256 z własnym kluczem. Jawne identyfikatory nie są zapisywane do plików ani używane jako cechy.
- Logi błędów nie zawierają treści zapytań, parametrów połączenia ani tracebacków.
- Dane SQL nadal opisują rzeczywisty biznes. Ich prognozy, metryki i wyuczone modele mogą ujawniać zależności lub skalę działalności. To pseudonimizacja i ograniczenie metadanych, nie gwarancja pełnej anonimowości wartości liczbowych. Do publicznej prezentacji służy jawny tryb syntetyczny.
To reprezentatywny workload MLOps, a nie odtworzenie jakości modeli produkcyjnych. Wspólny model drzewiasty zastępuje ensemble biznesowe, modele fizyczne i sieci neuronowe. Nie ma taryfowych wariantów demand, RUS, direction/spread, redispatch, aktywacji jednostek, procesów kolejkowych, zapisów do tabel wynikowych ani publikacji w chmurze. Zachowany jest przepływ trening → wersja → predykcja → CSV dla wszystkich czterech obszarów.
Uruchomienie w kontenerze
Copy-Item .env.example .env
docker compose up --build
Obraz zawiera sterownik ODBC zgodnie z
instrukcją Microsoft.
Podczas instalacji sterownika akceptuje jego licencję.
Kontener działa jako użytkownik 10001; dane zapisuje w /data.
Plik .dockerignore dopuszcza wyłącznie kod serwisu i requirements.
Na platformie buduj z tego folderu, ustaw sekrety, zamontuj zapisywalny wolumen
pod /data i uruchom jeden proces API. Kolejkowanie i skalowanie zadań pozostają
po stronie platformy. Artefakty mają niezmienne wersje i manifest zapisany na końcu,
więc można przenieść je do magazynu platformy. Usługa nie rejestruje ich automatycznie
w zewnętrznym rejestrze. HTTP zakłada prywatny dostęp lub uwierzytelnianie przez reverse proxy.
Weryfikacja
.\.venv\Scripts\python.exe -m pip install -r requirements-dev.txt
.\.venv\Scripts\python.exe -m pytest -q
Testy obejmują rzeczywisty trening wszystkich obszarów, ponowne wczytanie natywnych
modeli, HTTP, CSV, izolację przyszłych wartości, pseudonimy, błędy konfiguracji,
adapter SQL z kontrolowanym źródłem testowym i dni zmiany czasu.
Sprawdzenie na żywej bazie i budowa kontenera wymagają dostępu do bazy oraz działającego Dockera.
Wynik wykonanej weryfikacji opisuje VERIFICATION.md.