hackathon-ENGIN/engin/model.py

82 lines
2.7 KiB
Python

"""Prediction model adapters used through a small dependency-injection boundary."""
from __future__ import annotations
from dataclasses import dataclass
from typing import Protocol
import pandas as pd
from final_pipeline import DiagnosticModels, predict_test, train_models, validate_submission
from .errors import InferenceError
@dataclass(frozen=True)
class PredictionBundle:
submission: pd.DataFrame
diagnostics: pd.DataFrame
class PredictionModel(Protocol):
def predict(self, frame: pd.DataFrame) -> PredictionBundle: ...
class SklearnPredictionModel:
"""Thin adapter around the frozen, validated competition pipeline."""
def __init__(self, models: DiagnosticModels) -> None:
self._models = models
@classmethod
def train(
cls,
reference_frame: pd.DataFrame,
*,
random_state: int = 42,
n_jobs: int = -1,
) -> "SklearnPredictionModel":
try:
models = train_models(
reference_frame,
random_state=random_state,
n_jobs=n_jobs,
)
except Exception as exc:
raise InferenceError(
"Nie udało się przygotować modelu referencyjnego.",
hint="Sprawdź kompletność val.csv oraz zgodność wersji zależności.",
) from exc
return cls(models)
def predict(self, frame: pd.DataFrame) -> PredictionBundle:
try:
submission, diagnostics = predict_test(self._models, frame)
validate_submission(submission, frame)
except Exception as exc:
raise InferenceError(
"Model odrzucił pomiary podczas predykcji.",
hint="Zweryfikuj kompletność silników oraz brak nietypowych wartości w widmie.",
) from exc
return PredictionBundle(submission, diagnostics)
class PrecomputedPredictionModel:
"""Read-only demo fallback; accepts only the exact precomputed key set."""
def __init__(self, submission: pd.DataFrame, diagnostics: pd.DataFrame) -> None:
self._submission = submission.reset_index(drop=True).copy()
self._diagnostics = diagnostics.reset_index(drop=True).copy()
def predict(self, frame: pd.DataFrame) -> PredictionBundle:
keys = ["engine_id", "cylinder"]
if not frame[keys].reset_index(drop=True).equals(self._submission[keys]):
raise InferenceError(
"Tryb awaryjny obsługuje wyłącznie dołączony zestaw demonstracyjny.",
hint="Przywróć val.csv i uruchom ponownie aplikację, aby diagnozować własne pliki.",
)
return PredictionBundle(
self._submission.copy(),
self._diagnostics.copy(),
)