@@ -4,6 +4,7 @@ __all__ = [
|
||||
"FeatureStore",
|
||||
"FeatureVector",
|
||||
"FeatureModel",
|
||||
"FeatureExplanation",
|
||||
"PredictionResult",
|
||||
"Predictor",
|
||||
"RetrainingResult",
|
||||
@@ -13,6 +14,7 @@ __all__ = [
|
||||
"retrain_model",
|
||||
]
|
||||
from app.ml.feature_store import FeatureStore, FeatureVector
|
||||
from app.ml.explanation import FeatureExplanation
|
||||
from app.ml.predictor import PredictionResult, Predictor
|
||||
from app.ml.retraining import RetrainingResult, RetrainingService, retrain_model
|
||||
from app.ml.training import FeatureModel, TrainedArtifact, TrainingPipeline
|
||||
|
||||
57
app/ml/explanation.py
Normal file
57
app/ml/explanation.py
Normal file
@@ -0,0 +1,57 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
from app.ml.training import FeatureModel
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class FeatureExplanation:
|
||||
feature: str
|
||||
current_value: float
|
||||
predicted_value: float
|
||||
change: float
|
||||
direction: str
|
||||
sample_count: int
|
||||
historical_mean: float
|
||||
historical_range: tuple[float, float]
|
||||
standard_deviation: float
|
||||
trend_per_step: float
|
||||
confidence: float
|
||||
summary: str
|
||||
|
||||
|
||||
def explain_feature(
|
||||
feature_name: str,
|
||||
current_value: float,
|
||||
predicted_value: float,
|
||||
model: FeatureModel,
|
||||
) -> FeatureExplanation:
|
||||
change = predicted_value - current_value
|
||||
direction = _direction(change)
|
||||
summary = (
|
||||
f"{feature_name}: {direction}; Prognose {predicted_value:.3f} "
|
||||
f"aus aktuellem Wert {current_value:.3f} und Trend {model.slope:+.3f}. "
|
||||
f"Basis: {model.sample_count} Messwerte, Mittelwert {model.mean:.3f}, "
|
||||
f"Confidence {model.confidence:.0%}."
|
||||
)
|
||||
return FeatureExplanation(
|
||||
feature=feature_name,
|
||||
current_value=current_value,
|
||||
predicted_value=predicted_value,
|
||||
change=change,
|
||||
direction=direction,
|
||||
sample_count=model.sample_count,
|
||||
historical_mean=model.mean,
|
||||
historical_range=(model.minimum, model.maximum),
|
||||
standard_deviation=model.standard_deviation,
|
||||
trend_per_step=model.slope,
|
||||
confidence=model.confidence,
|
||||
summary=summary,
|
||||
)
|
||||
|
||||
|
||||
def _direction(change: float) -> str:
|
||||
if abs(change) < 1e-12:
|
||||
return "stabil"
|
||||
return "steigend" if change > 0 else "fallend"
|
||||
@@ -5,6 +5,7 @@ import math
|
||||
from dataclasses import dataclass
|
||||
from typing import Sequence
|
||||
|
||||
from app.ml.explanation import FeatureExplanation, explain_feature
|
||||
from app.ml.feature_store import FeatureVector
|
||||
from app.ml.registry.model_registry import ModelRegistry
|
||||
from app.ml.training import TrainedArtifact, TrainingPipeline
|
||||
@@ -19,6 +20,7 @@ class PredictionResult:
|
||||
predictions: dict[str, float]
|
||||
confidence: float
|
||||
model_type: str
|
||||
explanations: dict[str, FeatureExplanation]
|
||||
|
||||
|
||||
class Predictor:
|
||||
@@ -52,13 +54,21 @@ class Predictor:
|
||||
)
|
||||
|
||||
predictions: dict[str, float] = {}
|
||||
explanations: dict[str, FeatureExplanation] = {}
|
||||
confidences: list[float] = []
|
||||
for feature_name in feature_names:
|
||||
model = sensor_models[feature_name]
|
||||
current_value = float(entity.values[feature_name])
|
||||
if not math.isfinite(current_value):
|
||||
raise ValueError("Vorhersagewerte müssen endlich sein.")
|
||||
predictions[feature_name] = model.forecast(current_value)
|
||||
predicted_value = model.forecast(current_value)
|
||||
predictions[feature_name] = predicted_value
|
||||
explanations[feature_name] = explain_feature(
|
||||
feature_name,
|
||||
current_value,
|
||||
predicted_value,
|
||||
model,
|
||||
)
|
||||
confidences.append(model.confidence)
|
||||
|
||||
return PredictionResult(
|
||||
@@ -67,6 +77,7 @@ class Predictor:
|
||||
predictions=predictions,
|
||||
confidence=sum(confidences) / len(confidences),
|
||||
model_type=artifact.model_type,
|
||||
explanations=explanations,
|
||||
)
|
||||
|
||||
def predict_batch(
|
||||
|
||||
Reference in New Issue
Block a user