Zum Inhalt springen

Driftmanagement und Bias-Korrektur im SolarForecast KI-Prognosesystem

Aus FHEMWiki
Version vom 5. August 2026, 09:24 Uhr von DS Starter (Diskussion | Beiträge) (Die Seite wurde neu angelegt: „= Driftmanagement und Bias-Korrektur im SolarForecast KI-Prognosesystem = Diese Seite erläutert, wie SolarForecast die Qualität der neuronalen Verbrauchs- und Ertragsprognosen (AI::FANN) im laufenden Betrieb überwacht und automatisch nachjustiert. Zwei eng verzahnte Mechanismen sind dafür verantwortlich: * Die '''Bias-Korrektur''' (<code>_aiFannApplyBiasCorrection</code>), die bei '''jeder einzelnen Prognoseabfrage''' (<code>aiFannConInfer</code>) a…“)
(Unterschied) ← Nächstältere Version | Aktuelle Version (Unterschied) | Nächstjüngere Version → (Unterschied)

Driftmanagement und Bias-Korrektur im SolarForecast KI-Prognosesystem

Diese Seite erläutert, wie SolarForecast die Qualität der neuronalen Verbrauchs- und Ertragsprognosen (AI::FANN) im laufenden Betrieb überwacht und automatisch nachjustiert. Zwei eng verzahnte Mechanismen sind dafür verantwortlich:

  • Die Bias-Korrektur (_aiFannApplyBiasCorrection), die bei jeder einzelnen Prognoseabfrage (aiFannConInfer) angewendet wird und den rohen Netzwerk-Output in Echtzeit korrigiert.
  • Das Driftmanagement (aiFannDetectDrift), das einmal pro volle Stunde die Prognosequalität der jüngsten Vergangenheit analysiert, die Korrekturparameter nachführt (Rekalibrierung) und bei Bedarf ein Retraining empfiehlt.

Beide Mechanismen arbeiten zusammen, ersetzen aber nicht das eigentliche Training – sie kompensieren die unvermeidliche Alterung eines einmal trainierten Modells zwischen zwei Trainingsläufen.


Warum überhaupt Drift- und Bias-Management?

Ein neuronales Netz lernt beim Training einen statischen Zusammenhang zwischen Eingangsgrößen (PV-Ertrag, Temperatur, Uhrzeit, Anwesenheit, Lag-Features usw.) und dem Zielwert (Verbrauch). Dieser gelernte Zusammenhang ist jedoch nur eine Momentaufnahme:

  • Saisonale Verschiebungen (Heizverhalten im Winter, Klimatisierung im Sommer)
  • Verhaltensänderungen der Bewohner (neue Geräte, geänderte Homeoffice-Tage, Urlaub)
  • Strukturelle Änderungen am Haushalt (neuer Verbraucher, BEV-Anschaffung, WP-Umbau)
  • Sensor-/API-Störungen (fehlerhafte Wetterdaten, Zählerausfälle)

führen dazu, dass die Vorhersagequalität mit der Zeit sinkt – das reale Verhalten "driftet" vom gelernten Modellzustand weg. In der ML-Literatur unterscheidet man dabei:

  • Concept Drift: Der tatsächliche Zusammenhang zwischen Features und Zielgröße ändert sich (z. B. weil ein E-Auto angeschafft wurde und nun nachts geladen wird).
  • Data Drift / Covariate Shift: Die Verteilung der Eingangsgrößen ändert sich, der zugrunde liegende Zusammenhang bleibt aber gleich.

Ein vollständiges Neu-Training bei jeder kleinen Abweichung ist weder praktikabel (Rechenzeit, Datenmenge) noch für den Anwender komfortabel. SolarForecast verfolgt daher einen zweistufigen Ansatz:

  1. Kurzfristig: Eine leichte additive/multiplikative Korrektur pro Prognosestunde (Bias-Korrektur), die auf zuletzt gemessenen Fehlerkennzahlen basiert.
  2. Mittelfristig: Eine stündliche, statistisch abgesicherte Nachführung der Korrekturreferenzwerte (Drift-Rekalibrierung) sowie eine Retraining-Empfehlung, wenn die Drift strukturell und nicht mehr kompensierbar ist.


Grundbegriffe

Bias und Slope

Für ein Modell, das reale Werte <math>y</math> aus Vorhersagen <math>\hat{y}</math> möglichst gut trifft, sind zwei Fehlerarten zu unterscheiden:

  • Bias (additiver Fehler): systematischer Versatz, z. B. das Modell sagt im Mittel 430 Wh zu wenig voraus. Berechnet als bias = mean(y - ŷ).
  • Slope (multiplikativer Fehler): Steigung der linearen Regression ŷ ~ y. Ein Slope < 1 bedeutet, dass das Modell hohe Werte tendenziell unterschätzt und niedrige Werte tendenziell überschätzt (Regression-to-the-mean-Effekt bei stochastischen, schwer vorhersagbaren Haushalten).

Diese beiden Größen werden im Code an mehreren Stellen getrennt geführt:

  • Statische Werte aus dem Training: ModelBias, ModelSlope, Mae, RmseRel (einmalig beim Training bestimmt, siehe Trainingsmodul).
  • Dynamische, laufend nachgeführte Referenzwerte: DriftRefBias, DriftRefSlope (werden bei Rekalibrierung überschrieben und sind dann maßgeblich – _aiFannApplyBiasCorrection bevorzugt sie explizit, sofern definiert).
  • Aktuelle Live-Abweichung zur Referenz: DriftBias, DriftSlope (wie weit ist die aktuelle Stunde von der zuletzt kalibrierten Referenz entfernt).

Der strukturelle Zusammenhang Bias ≈ mean(y) × (1 − Slope) erklärt, warum bei stochastischen Haushalten (schwer vorhersagbares Verbrauchsverhalten) auch ein "gutes" Modell einen strukturellen Bias von mehreren hundert Wh aufweisen kann, ohne dass ein Modellfehler vorliegt.


Baseline vs. Peak

Ein zentrales Entwurfsprinzip beider Mechanismen ist der Peak-Schutz: Additive und multiplikative Korrekturen werden nur auf die Grundlast (Baseline) angewendet, niemals auf Verbrauchsspitzen. Grund: Eine Korrektur, die im Mittel passt, würde bei einem punktuellen Peak (z. B. Backofen, Wallbox-Ladevorgang) den Vorhersagefehler eher vergrößern als verkleinern. Die Baseline-Erkennung erfolgt über das 30 %-Quantil des jeweiligen Prognosetyps (CircularVal ($name, '99', $fanntyp.'_quantile30', 0)): Liegt der (bereits driftkorrigierte) Vorhersagewert nicht mehr als 20 % über diesem Quantil, gilt er als Grundlast.


Ablauf einer Prognose (aiFannConInfer)

der Weg jeder einzelnen Prognosestunde (aiFannConInfer)

aiFannConInfer wird für jede der MAXNEXTHOURS zukünftigen Stunden aufgerufen und durchläuft je Stunde folgende Kette:

  1. Feature-Vektor aus NextHours/History aufbauen (Lag-Features, zyklische Kodierung, BEV-/WP-Aggregate usw. – siehe separate Wiki-Seite zur Feature-Registry).
  2. Netzwerk-Inferenz: _aiFannPredict lässt das FANN-Modell laufen und denormalisiert den Output anhand der beim Training gespeicherten MinVal/MaxVal.
  3. Bias-/Drift-Korrektur: _aiFannApplyBiasCorrection wandelt den rohen Modell-Output $denorm_val in den korrigierten Wert $prediction um (Details siehe unten).
  4. Horizont-Blending: Für weiter in der Zukunft liegende Stunden wird die reine Modellprognose zunehmend mit einem historischen Referenzwert gemischt (_aiFannGetHistoricalReference, Mittelwert derselben Stunde der letzten 7 Tage). Der Blend-Anteil blend_alpha steigt mit der Anzahl der Stunden in die Zukunft auf maximal 80 %, weil die Modellprognose mit wachsendem Horizont zunehmend unsicherer wird und ein saisonaler Mittelwert stabiler ist.
  5. Hybridisierung mit der Legacy-Prognose: confc_final = alpha × prediction + (1 − alpha) × legacyconfc, gesteuert über das Reading aiConAlpha. So kann der Anwender das Vertrauen in das KI-Modell gegenüber der klassischen Prognose stufenlos einstellen.
  6. Speicherung der Zwischenwerte (conaifc, confc, conbiascorr) in Circular- und History-Speicher zur späteren Auswertung durch das Driftmanagement.





Die Bias-Korrektur im Detail (_aiFannApplyBiasCorrection)

Diese Funktion wird pro Prognosestunde aufgerufen und führt in dieser Reihenfolge vier Korrekturschritte aus: Drift-Level-Korrektur → Drift-Bias-Korrektur → statische Trainingskalibrierung → additive Bias-Zonenkorrektur.


Schritt 1: Drift-Slope-Korrektur (Level-Skalierung)

Der laufend aktualisierte DriftSlope (Verhältnis der aktuellen Live-Regressionssteigung zur Referenzsteigung, siehe unten) wird zunächst auf den Bereich [0.80, 1.10] geklemmt ($ds_min/$ds_max). Anschließend wird eine adaptive Gewichtung berechnet, damit kleine, verrauschte Abweichungen nicht voll durchschlagen:

slope_dev    = |1.0 − DriftSlope|
rmse_dev     = ln(1 + max(0, DriftRmseRelRatio − 1))
mae_scale    = min(1.0, Mae / 200)
drift_weight = min(1.0, 0.4·slope_dev + 0.1·rmse_dev + 0.1·mae_scale)
ds_adapted   = 1.0 + (ds − 1.0) · drift_weight

Der so gedämpfte Faktor ds_adapted wird danach nochmals zonenabhängig skaliert – je nachdem, wie stark und wie zuverlässig der aktuelle Drift ist:

Drift-Zone Bedingung Wirkung auf ds_adapted
1 (stabil) DriftSlope ∈ [0.85, 1.15] und DriftRmseRelRatio < 1.5 nur 10 % der Abweichung wirksam
2 (leichte Drift) DriftSlope ∈ [0.70, 1.30] und DriftRmseRelRatio < 2.5 40 % der Abweichung wirksam
3 (starke Drift) sonst 70 % der Abweichung wirksam

Nach der Zonenskalierung erfolgt ein finales Clamping auf [0.70, 1.10]. Ist die Drift-Korrektur aktuell deaktiviert (siehe nächster Absatz), wird ds_adapted = 1.0 gesetzt und keine Korrektur angewendet. Der rohe Vorhersagewert wird dann multiplikativ skaliert: res = val_predict × ds_adapted.

Deaktivierungsbedingungen ($drift_enabled = 0):

  • Modellalter ModelAgeHours < AIMODELMINAGE – ein frisches Modell hat noch keine belastbare Live-Fehlerstatistik.
  • Nachtstunden hod < 7 – nachts ist die Grundlast so niedrig und variabel (Standby-Verbräuche), dass eine Driftkorrektur hier eher schadet als nützt.


Schritt 2: Drift-Bias (additiver Versatz)

Der aktuell gemessene DriftBias (Differenz zwischen Live-Bias und Referenz-Bias, siehe Driftanalyse) wird auf maximal ±1.0 × Mae geklemmt und – nur wenn die Vorhersage als Grundlast (is_baseline) erkannt wurde – additiv aufaddiert: res += clamped_drift_bias. Ist die Driftkorrektur deaktiviert, wird auch dieser Anteil auf 0 gesetzt.


Schritt 3: Statische Trainingskalibrierung (OLS, Peak-geschützt)

Falls beim letzten Training eine strukturelle Kalibrierung ermittelt wurde (CalSlope/CalBias im Modell-Datensatz, per Ordinary-Least-Squares-Regression bestimmt), wird sie – wiederum ausschließlich auf Grundlast-Werte – angewendet:

cal_val       = CalSlope · res + CalBias
osl_delta     = cal_val − res
max_osl_delta = 0.15 · Mae            # Korrektur auf max. 15 % des MAE begrenzt
res           = clamp(res + osl_delta, 0, MaxVal)

Der Grund für die enge Deckelung: CalSlope korrigiert einen strukturellen Steigungsfehler aus dem Training pauschal für den gesamten Wertebereich. Ohne Deckelung würde diese Korrektur bei Peaks (die per Definition außerhalb der Baseline-Prüfung liegen dürften, aber am Übergang) überkorrigieren. Kommt diese Kalibrierung zur Anwendung, wird die nachfolgende additive Bias-Zonenkorrektur (Schritt 4) übersprungen (bias_zone = 3, Kennzeichnung +OSL im Reading), um eine Doppelkorrektur zu vermeiden.


Schritt 4: Additive Bias-Zonenkorrektur

Nur falls Grundlast und keine OLS-Kalibrierung gegriffen hat, wird der geklemmte statische Bias (clamped_bias, max. 50 % des Mae) je nach Modellgüte anteilig aufaddiert. Die Modellgüte wird über drei Kennzahlen bewertet: ModelSlope, bias_ratio = |bias| / max(Mae, 0.1) und RmseRel.

Zone Farbe Bedingung Korrekturanteil
1 grün Slope ∈ [0.9, 1.1], bias_ratio ≤ 1.0, RmseRel ≤ 25 % 70 % des Bias
2 gelb Slope ∈ [0.7, 0.9), bias_ratio ≤ 2.0, RmseRel ≤ 40 % 50 % des Bias
2b (profilabhängig) Slope ≥ z2_slope_min, bias_ratio ≤ z2_bias_max, RmseRel ≤ z2_rmse_max (Schwellen aus %profileweights{$profile}) 25 % des Bias
3 rot keine der obigen Bedingungen erfüllt keine Korrektur

Die Zone-2b-Schwellen sind bewusst pro Profil konfigurierbar (%profileweights), da stochastische Haushalte (viele Einzelverbraucher, unregelmäßiges Verhalten) strukturell schlechtere Slope-/RMSE-Werte aufweisen als reguläre Haushalte, ohne dass dies ein Qualitätsmangel des Modells ist (siehe Bias-≈-mean×(1−Slope)-Zusammenhang oben). Ohne die Zone-2b-Ausnahme würden solche Profile dauerhaft in der roten Zone landen und nie eine Korrektur erhalten.

Die Funktion liefert am Ende ($res, $corr_val, $bias_zone, $drift_zone) zurück, wobei $corr_val = $res − $val_predict die insgesamt angewendete Korrektur in Wh ist. Diese Werte werden pro Stunde als conbiascorr, conaifc und confc historisiert und stehen damit der nachfolgenden Driftanalyse als Trainings-/Prüfdaten zur Verfügung.


Die stündliche Driftanalyse (aiFannDetectDrift)

Diese Funktion wird einmal pro volle Stunde und pro Prognosetyp (fanntyp, z. B. con) aufgerufen und darf nicht häufiger laufen, da sie zustandsbehaftete EMA-Glättungen (siehe unten) fortschreibt, die bei Mehrfachaufruf verfälscht würden.

stündliche Driftanalyse und Rekalibrierung

1. Adaptive Fenstergröße (_aiFannSelectWindow)

Bevor irgendwelche Kennzahlen gelöscht werden, wird die Fenstergröße für die nachfolgende Analyse bestimmt – sie muss vor dem Löschen ermittelt werden, weil sie selbst auf zuvor gespeicherten Kennzahlen (ModelAgeHours, DriftScore, DriftSemRatio) aufbaut. Grundprinzip: Peaks bzw. seltene Ausreißer sollen das Fenster vergrößern (verwässern), während dauerhafte, breit gestreute Drift das Fenster verkleinern soll (schnellere Reaktion auf neue Verhältnisse):

Regel Bedingung Fenstergröße
Peak, breit gestreut DriftScore > 2.5 und SemRatio > 0.7 120 h
Seltene extreme Peaks DriftScore > 2.0 und SemRatio < 0.4 120 h
Dauerhafte Drift DriftScore > 2.0, SemRatio ≥ 0.4, Alter > 72 h 48 h
Schleichende Drift 1.5 < DriftScore ≤ 2.0, SemRatio < 0.4, Alter > 48 h 72 h
Stabiles Modell DriftScore < 1.2, Alter > 72 h 144 h
(keine Regel greift / fehlende Werte) 96 h (Default)


2. Modellalter und Fresh-Model-Reset

Der Zeitstempel des letzten Trainingsabschlusses wird aus CircularVal(..., 99, $fanntyp.'NNTrainLastFinishTs') gelesen; die Differenz zum jüngsten verfügbaren Rohdatensatz ergibt ModelAgeHours. Ist das Modell jünger als AIMODELMINAGE Stunden, greift ein harter Reset: Die Drift-Historie wird geleert, DriftBias = 0, DriftSlope = 1 gesetzt, und die Referenzwerte DriftRefBias/DriftRefSlope werden auf den Modellzustand aus dem Training gesetzt (ModelBias/ModelSlope) – außer es ist bereits eine statische Kalibrierung (CalSlope/CalBias) aktiv; in diesem Fall werden die Referenzen auf 0/1 gesetzt, um die in Schritt 3 der Bias-Korrektur bereits erfolgende OLS-Korrektur nicht doppelt zu berücksichtigen. Die Funktion kehrt danach sofort mit dem Flag fresh_model zurück – es findet noch keine Drift-Bewertung statt.


3. Leckage-Schutz: nur Daten des aktuellen Modells

Aus der Rohhistorie ($data{$name}{aidectree}{airaw}) werden ausschließlich Datensätze berücksichtigt, deren Zeitstempel nach dem Trainingsabschluss liegen (post_train_idx). Damit wird verhindert, dass die Drift-Bewertung durch Prognosen des vorherigen Modells verfälscht wird. Sind weniger als AIMODELMINAGE solcher Datensätze vorhanden, bricht die Analyse mit insufficient_data ab. Aus den verbleibenden Datensätzen wird das jüngste Fenster der Größe $window (tail_idx) für die eigentliche Analyse verwendet.


4. Stündliche Fehlerberechnung und Glättung

Für jede Stunde im Analysefenster werden realer Wert ($fanntyp, z. B. con) und Prognosewert (${fanntyp}aifc) gelesen, auf Gültigkeit geprüft (definiert, numerisch, ≥ 0) und daraus:

  • slope_list: Verhältnis p/a je Stunde (für spätere Varianzberechnung)
  • bias_hour = a − p, geklemmt auf ±3 × DriftRefMae (Ausreißerschutz), anschließend exponentiell geglättet (EMA, alpha = 0.3): bias_smooth = 0.3·bias_hour + 0.7·bias_smooth_prev

gebildet. Für die Bias-Varianz wird nur das letzte 24-h-Fenster verwendet (bias_last24), damit einzelne Peaks über ein 96-h-Fenster die Varianzschätzung nicht dominieren.


5. Fehlermetriken (_aiFannErrorMetrics)

Auf Basis von @targets/@preds im Analysefenster werden auf Originalskala (Wh) berechnet:

  • MAE (Mean Absolute Error): mittlerer absoluter Fehler
  • RMSE und RMSErel (RMSE relativ zum Median der Zielwerte, robuster als Mittelwert-Bezug bei schiefen Verteilungen)
  • MedAE (Median Absolute Error, ausreißerrobust)
  • MAPE / MdAPE (mittlerer/medianer prozentualer Fehler)
  • Bias-Kennzahlen (signed): bias_mean, bias_median, bias_abs_mean, bias_abs_median

Daraus: drift_score = mae_live / DriftRefMae – das zentrale Verhältnis "wie viel schlechter ist die aktuelle Fehlerlage gegenüber dem Referenzzustand".


6. Regression Live (_aiFannSlopeBias, OLS)

Mittels gewöhnlicher kleinster Quadrate (Ordinary Least Squares) wird die lineare Regression Prognose ~ Realwert über das Analysefenster berechnet:

slope = (n·Σxy − Σx·Σy) / (n·Σx² − (Σx)²)
bias  = (Σy − slope·Σx) / n

mit x = Realwerte, y = Prognosewerte. Sonderfälle werden explizit behandelt: zu wenige Datenpunkte (n < 2), unterschiedliche Array-Längen (Truncation) sowie Null-Varianz in den Zielwerten (alle x identisch – dann ist die Steigung nicht bestimmbar, slope = 0, bias = ȳ als Fallback). Der Nenner wird gegen einen skalierten Epsilon-Wert statt gegen exakt 0 geprüft, um numerische Instabilität bei sehr kleinen, aber nicht exakt verschwindenden Nennern zu vermeiden.

Der resultierende bias_live wird nochmals per EMA mit dem vorherigen Wert geglättet (0.7 × neu + 0.3 × alt), bevor er als DriftBiasLive gespeichert wird – zusätzlich zur stundenweisen EMA aus Schritt 4. Diese doppelte Glättung dient dazu, sowohl kurzfristige (stundenweise) als auch mittelfristige (fensterweise) Sprünge zu dämpfen.


7. Drift-Kennzahlen relativ zur Referenz

slope_drift     = slope_live / DriftRefSlope
bias_drift      = bias_live  − DriftRefBias
rmse_rel_ratio  = rmse_rel_live / DriftRefRmse
bias_drift_norm = |bias_drift| / DriftRefMae
slope_rel_drift = |slope_drift − 1|


8. Semantik- und Peak-Anteil

Zusätzlich zu den globalen Fehlermaßen wird geprüft, wie verbreitet der Fehler im Fenster ist:

  • sem_ratio: Anteil der Stunden, bei denen der absolute Fehler die Schwelle 0.5 × Mae_model überschreitet. Hoch (>0.7) deutet auf flächendeckende, dauerhafte Drift hin; niedrig (<0.4) auf vereinzelte Ausreißer.
  • peak_ratio: Anteil der Stunden mit Realwert oberhalb Median + 2×Mae_model.

Diese beiden Kennzahlen fließen sowohl in die Fenstergrößen-Heuristik (siehe oben) als auch in die Sicherheitslogik (siehe unten) ein.


9. Der Drift-Index und die Ampel-Flags

Aus den bisherigen Größen wird ein gewichteter Gesamtindex gebildet:

slope_penalty = slope_rel_drift < 0.3
              ? min(2.0, slope_rel_drift)                              # linear bei kleiner Abweichung
              : min(2.0, slope_rel_drift + slope_rel_drift²)           # quadratisch bei großer Abweichung

slope_boost   = slope_rel_drift > 0.4 ? 0.25 : 0.0                     # zusätzlicher Booster bei starkem Dynamikverlust

drift_index = 0.40 · min(3.0, drift_score)
            + 0.30 · min(3.0, rmse_rel_ratio)
            + 0.20 · min(2.0, slope_penalty)
            + 0.10 · min(2.0, bias_drift_norm)
            + slope_boost

Die Gewichtung spiegelt die praktische Relevanz wider: der reine Fehleranstieg (drift_score) und die RMSE-Relation dominieren mit zusammen 70 %, echte Dynamikverluste (Slope) mit 20 % plus Booster, reine additive Verschiebung (Bias) nur mit 10 %, da Bias durch die separate additive Korrektur ohnehin laufend adressiert wird.

Drift-Index Flag
> 3.0 severe
> 2.0 moderate
> 1.5 mild
> 1.1 low
> 1.02 very_low
≤ 1.02 stable


10. Rekalibrierung

Das Flag der jeweiligen Stunde wird in DriftZoneHistory protokolliert (auf die letzten 20 Einträge begrenzt). Ein Zähler DriftZone3Hours erfasst, wie viele Stunden in Folge der Drift-Index über der "mild"-Schwelle (1.5) lag; er wird zurückgesetzt, sobald der Index wieder darunter fällt.

Bevor eine Rekalibrierung stattfinden darf, prüft _aiFannDriftSafetyBlocked eine Reihe von Plausibilitätsbedingungen (siehe nächster Abschnitt). Ist keine geblockt, und hat DriftZone3Hours die Schwelle erreicht (4 Stunden bei severe, sonst DRIFTHZN3TH = 8 Stunden), wird rekalibriert:

bias_drift_effective = clamp(0.5·bias_drift + 0.5·(mae_live − DriftRefMae), ±2·DriftRefMae)
new_bias              = DriftRefBias + 0.4 · bias_drift_effective        # sanfte 40%-Anpassung

slope_error            = slope_live − DriftRefSlope
slope_drift_effective  = 0.6·slope_error + 0.4·(rmse_rel_ratio − 1.0)·0.1
new_slope               = clamp(DriftRefSlope + slope_drift_effective, 0.85, 1.15)

DriftRefBias und DriftRefSlope werden mit den neuen Werten überschrieben, DriftBias/DriftSlope relativ dazu neu berechnet (sollten danach nahe 0 bzw. 1 liegen), DriftZone3Hours auf 0 zurückgesetzt und DriftRefMae/DriftRefRmse auf die aktuellen Live-Werte gesetzt – das neue Referenzniveau für die nächste Analyseperiode. Das Flag wird auf recalibrated gesetzt.

Wichtig: Die Rekalibrierung passiert bewusst nur in kleinen Schritten (40 % bzw. 60/40-Mischung), nicht als vollständiger Sprung auf die Live-Werte, um Überreaktionen auf kurzfristiges Rauschen zu vermeiden.


11. Retrain-Empfehlung (_aiFannRetrainRecommended)

Wurde in diesem Durchlauf rekalibriert, wird die Empfehlung auf none (Grund just_recalibrated) gesetzt, da die Rekalibrierung das Problem bereits adressiert hat. Andernfalls wird anhand von vier unabhängigen Signalen aus den letzten 12 protokollierten Flags (DriftZoneHistory) ein Score gebildet:

Signal Bedingung
structural_block Blockgrund ist slope_critical, negative_slope_drift oder model_bad_but_stable und DriftIndex > 1.8
persistent_drift DriftIndex > 2.0 und mindestens 8 der letzten 12 Flags moderate/severe
severe_drift DriftIndex > 2.5 und DriftScore > 2.5
context_block Blockgrund ist slope_implausible/rmse_anomaly/unstable_slope/unstable_bias und persistent (≥10 von 12) und DriftIndex > 2.0

Bewusst nicht gewertet werden reine Datenfehler-Blocks wie bias_implausible oder low_load_phase – diese deuten auf Sensor-/Datenprobleme statt auf Modellprobleme hin und sollen kein Retraining auslösen. Score ≥ 2 → urgent, Score = 1 → advised, sonst none.


Sicherheitslogik gegen Fehlrekalibrierung (_aiFannDriftSafetyBlocked)

Da eine fehlerhafte Rekalibrierung das Modell dauerhaft "verbiegen" würde, prüft diese Funktion vor jeder Rekalibrierung eine Kette von Plausibilitätskriterien und liefert bei Verdacht einen sprechenden Blockgrund statt 0:

Blockgrund Erkennt …
no_data zu wenige Datenpunkte im Fenster (≤ 6)
negative_slope_drift invertierte Dynamik (Slope-Drift oder Live-Slope negativ) – klares Datenproblem
slope_critical slope_live| < 0.25) bei gleichzeitig weit verbreitetem Fehler (sem_ratio > 0.75)
low_load_phase dynamischer Nacht-Detektor: mindestens 5 der letzten 6 Stunden liegen nahe am 30 %-Quantil, kaum Peaks, kaum Semantik-Fehler → typische Grundlast-/Nachtphase, keine Rekalibrierung nötig
slope_implausible extreme Slope-Werte kombiniert mit hoher Varianz oder breitem Fehleranteil, aber wenig Peaks → deutet auf API-/Sensorfehler statt echter Drift
rmse_anomaly RMSE-Verhältnis überschreitet eine dynamische Grenze, die sich aus Peak-Anteil, Semantik-Anteil und Slope-Varianz ableitet (Grundlast → niedrigere Grenze, viele Peaks/Varianz → höhere Toleranz)
bias_implausible Live-Bias überschreitet eine aus Quantil30/Quantil90/Medianlast abgeleitete Plausibilitätsgrenze bei gleichzeitig wenig Peaks → typisches Zeichen für Sensor-/API-Ausfälle
model_bad_but_stable Modell ist zwar schlecht, aber nicht driftend (moderate, aber nicht wachsende Abweichungen) → Rekalibrierung würde nichts verbessern, eher Retraining nötig
unstable_slope Slope-Varianz über dem dynamischen, lastabhängigen Grenzwert → zu instabil für eine belastbare Rekalibrierung
unstable_bias hohes RMSE-Verhältnis und hohe normierte Bias-Varianz gleichzeitig

Erst wenn keine dieser Bedingungen zutrifft, wird die Rekalibrierung freigegeben.


Zusammenspiel: Beispielablauf

  1. Jede Prognosestunde (aiFannConInfer): Rohprognose → _aiFannApplyBiasCorrection wendet die zuletzt bekannten Referenzwerte (DriftRefBias/DriftRefSlope bzw. Trainingswerte, falls noch keine Rekalibrierung stattfand) an → korrigierter Wert wird gespeichert und historisiert.
  2. Einmal pro volle Stunde (aiFannDetectDrift): Die zuletzt historisierten Prognose-/Realwertpaare werden ausgewertet, ein Drift-Index gebildet, ein Ampel-Flag gesetzt.
  3. Bei anhaltender, plausibler Drift über mehrere Stunden (Zone3Hours-Schwelle erreicht, keine Sicherheitsblockade): DriftRefBias/DriftRefSlope werden sanft nachgeführt – ab der nächsten Prognosestunde wirkt die neue Referenz in _aiFannApplyBiasCorrection.
  4. Bei struktureller, nicht kompensierbarer Drift (persistente moderate/severe-Flags über 12 Stunden, oder strukturelle Blockgründe): Die Retrain-Empfehlung wird auf advised bzw. urgent gesetzt – dies ist ein Hinweis an den Anwender, ein neues Training anzustoßen, da die laufende Korrektur an ihre Grenzen stößt.

Damit ergibt sich eine Kaskade dreier Zeitskalen: Prognose (jede Stunde neu berechnet, sofort korrigiert) → Drift-Rekalibrierung (Stunden bis Tage, sanfte Nachführung) → Retraining (Wochen bis Monate, vollständige Neuanpassung).


Wichtigste Kennziffern im Überblick

Reading Bedeutung
ModelBias / ModelSlope statische Werte aus dem letzten Training
DriftRefBias / DriftRefSlope aktuell gültige, ggf. rekalibrierte Referenzwerte (haben Vorrang vor den Modellwerten)
DriftBias / DriftSlope aktuelle Abweichung der Live-Fehlerlage von der Referenz
DriftBiasLive / DriftSlopeLive geglättete Live-Regressionskennzahlen des Analysefensters
DriftScore Verhältnis Live-MAE zu Referenz-MAE
DriftIndex gewichteter Gesamt-Driftindikator (Basis der Ampel-Flags)
DriftRmseRelRatio Verhältnis Live-RMSErel zu Referenz-RMSErel
DriftSemRatio Anteil der Stunden mit signifikantem Fehler (Verbreitungsgrad der Drift)
DriftFlag aktuelle Ampel/Status (stablesevere, recalibrated, fresh_model, oder recalibration blocked: …)
DriftZoneHistory Protokoll der letzten 20 Flags
DriftZone3Hours Anzahl aufeinanderfolgender Stunden über der "mild"-Schwelle
DriftLastRecalTime Zeitpunkt der letzten Rekalibrierung
ModelAgeHours Alter des aktuellen Modells seit Trainingsabschluss
RetrainRecommendation / DriftRetrainReason Empfehlung none/advised/urgent mit Begründung
conbiascorr pro Stunde tatsächlich angewendete Bias-/Drift-Korrektur in Wh (corr_val aus _aiFannApplyBiasCorrection)


Praxishinweise für Anwender

  • DriftFlag = stable/very_low/low: Kein Handlungsbedarf, das System korrigiert im Rahmen der normalen Bias-Korrektur.
  • DriftFlag = mild/moderate über längere Zeit: Das System rekalibriert automatisch, sobald die Sicherheitsprüfung dies zulässt – meist kein Eingreifen nötig, kann aber beobachtet werden.
  • DriftFlag = recalibration blocked: … über viele Stunden: Der Blockgrund gibt einen konkreten Hinweis (z. B. low_load_phase ist unkritisch, slope_implausible/bias_implausible deuten dagegen auf Sensor- oder API-Probleme hin, die geprüft werden sollten).
  • RetrainRecommendation = advised oder urgent: Ein neues Training des betreffenden Modells (fanntyp) sollte zeitnah angestoßen werden, da die laufende Korrektur die strukturelle Abweichung nicht mehr ausgleichen kann.
  • DriftFlag = fresh_model: Normal direkt nach einem Training – die Driftanalyse startet erst nach AIMODELMINAGE Stunden mit belastbaren Werten.