File size: 2,859 Bytes
3d62172
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
"""
inference.py — LSTM Groundwater Level Forecasting
==================================================
Usage
-----
    from inference import load_model, forecast
    model, scaler_X, scaler_y = load_model()
    prediction = forecast(model, scaler_X, scaler_y, X_window)
"""

import json
import joblib
import numpy as np
import pandas as pd
from pathlib import Path
from tensorflow.keras.models import load_model as keras_load

MODEL_PATH    = Path(__file__).parent / "lstm_model.keras"
SCALER_X_PATH = Path(__file__).parent / "scaler_X.pkl"
SCALER_Y_PATH = Path(__file__).parent / "scaler_y.pkl"
CONFIG_PATH   = Path(__file__).parent / "model_config.json"


def load_model():
    """Load the LSTM model and both scalers."""
    model    = keras_load(MODEL_PATH)
    scaler_X = joblib.load(SCALER_X_PATH)
    scaler_y = joblib.load(SCALER_Y_PATH)
    print("LSTM model and scalers loaded.")
    return model, scaler_X, scaler_y


def load_config():
    with open(CONFIG_PATH) as f:
        return json.load(f)


def forecast(model, scaler_X, scaler_y, X_window: pd.DataFrame):
    """
    Predict the next month's groundwater level.

    Parameters
    ----------
    model     : loaded Keras model
    scaler_X  : fitted MinMaxScaler for features
    scaler_y  : fitted MinMaxScaler for target
    X_window  : DataFrame with columns [water_level, temperature,
                precipitation, wind_speed] and exactly 24 rows
                (the lookback window)

    Returns
    -------
    prediction : float — forecasted water level in original units (m)
    """
    cfg      = load_config()
    required = cfg['features']
    lookback = cfg['lookback_months']

    missing = [c for c in required if c not in X_window.columns]
    if missing:
        raise ValueError(f"X_window is missing columns: {missing}")
    if len(X_window) != lookback:
        raise ValueError(f"X_window must have {lookback} rows, got {len(X_window)}")

    X_scaled = scaler_X.transform(X_window[required])
    X_input  = X_scaled.reshape(1, lookback, len(required))

    y_scaled    = model.predict(X_input, verbose=0).flatten()
    prediction  = scaler_y.inverse_transform(y_scaled.reshape(-1, 1)).flatten()[0]

    return float(prediction)


if __name__ == "__main__":
    model, scaler_X, scaler_y = load_model()
    cfg = load_config()
    print(f"Model: {cfg['architecture']}")
    print(f"Test RMSE: {cfg['test_metrics']['RMSE']} m")

    # Dummy window — replace with real data
    import numpy as np
    dummy = pd.DataFrame({
        'water_level'  : np.random.uniform(60, 75, 24),
        'temperature'  : np.random.uniform(3, 15, 24),
        'precipitation': np.random.uniform(20, 120, 24),
        'wind_speed'   : np.random.uniform(10, 25, 24),
    })
    pred = forecast(model, scaler_X, scaler_y, dummy)
    print(f"\nForecast (next month): {pred:.4f} m")