kozy9 commited on
Commit
3d62172
·
verified ·
1 Parent(s): 3cbdeda

Upload LSTM — test RMSE 2.9386 m R² 0.5505

Browse files
.gitattributes CHANGED
@@ -33,3 +33,7 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ fig_lstm_baseline_forecast.png filter=lfs diff=lfs merge=lfs -text
37
+ fig_lstm_comparison.png filter=lfs diff=lfs merge=lfs -text
38
+ fig_lstm_forecast.png filter=lfs diff=lfs merge=lfs -text
39
+ lstm_model.keras filter=lfs diff=lfs merge=lfs -text
README.md ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: mit
3
+ tags:
4
+ - time-series
5
+ - forecasting
6
+ - lstm
7
+ - hydrology
8
+ - groundwater
9
+ ---
10
+
11
+ # LSTM Groundwater Level Forecasting — UK
12
+
13
+ A tuned LSTM model for single-step monthly groundwater level forecasting
14
+ using meteorological variables as exogenous inputs.
15
+
16
+ ## Model Details
17
+
18
+ | Parameter | Value |
19
+ |---|---|
20
+ | Architecture | LSTM(128) → Dropout(0.1) → Dense(32) → Dense(1) |
21
+ | Framework | TensorFlow / Keras |
22
+ | Task | Single-step monthly forecasting |
23
+ | Lookback window | 24 months |
24
+ | Input features | water_level, temperature, precipitation, wind_speed |
25
+ | Tuning method | Bayesian Optimisation (Keras Tuner, 20 trials) |
26
+
27
+ ## Data Splits
28
+
29
+ | Split | Period | Months |
30
+ |---|---|---|
31
+ | Training | 1944-01-01 → 2007-10-01 | 766 |
32
+ | Validation | 2007-11-01 → 2015-10-01 | 96 |
33
+ | Test | 2015-11-01 → 2023-10-01 | 96 |
34
+
35
+ ## Best Hyperparameters
36
+
37
+ | Parameter | Value |
38
+ |---|---|
39
+ | LSTM layers | 2 |
40
+ | Units | 64 |
41
+ | Dropout | 0.1 |
42
+ | Learning rate | 0.000773 |
43
+ | Batch size | 32 |
44
+
45
+ ## Test Set Performance
46
+
47
+ | Metric | Value |
48
+ |---|---|
49
+ | RMSE | 2.9386 m |
50
+ | MAE | 2.397 m |
51
+ | MAPE | 3.671% |
52
+ | R² | 0.5505 |
53
+ | NSE | 0.5505 |
54
+
55
+ > This model is part of a benchmark study comparing SARIMAX, LSTM, and TCN
56
+ > for UK groundwater level forecasting.
57
+
58
+ ## Important Note
59
+
60
+ Contemporaneous meteorological variables are used as inputs at forecast time
61
+ (oracle assumption). Future met values are treated as known — consistent with
62
+ the experimental setup used across all models in this study.
63
+
64
+ ## Repository Contents
65
+
66
+ ```
67
+ ├── lstm_model.keras # Trained Keras model
68
+ ├── scaler_X.pkl # Feature scaler (MinMaxScaler)
69
+ ├── scaler_y.pkl # Target scaler (MinMaxScaler)
70
+ ├── model_config.json # Config, hyperparameters & metrics
71
+ ├── inference.py # Load model & generate forecasts
72
+ └── README.md # This file
73
+ ```
74
+
75
+ ## Quick Start
76
+
77
+ ```python
78
+ from huggingface_hub import hf_hub_download
79
+ from tensorflow.keras.models import load_model
80
+ import joblib, pandas as pd, numpy as np
81
+
82
+ model = load_model(hf_hub_download('kozy9/GWLSTM', 'lstm_model.keras'))
83
+ scaler_X = joblib.load(hf_hub_download('kozy9/GWLSTM', 'scaler_X.pkl'))
84
+ scaler_y = joblib.load(hf_hub_download('kozy9/GWLSTM', 'scaler_y.pkl'))
85
+
86
+ # Provide a 24-month window of features
87
+ X_window = pd.DataFrame({
88
+ 'water_level' : [...], # 24 values
89
+ 'temperature' : [...],
90
+ 'precipitation': [...],
91
+ 'wind_speed' : [...],
92
+ })
93
+
94
+ X_scaled = scaler_X.transform(X_window)
95
+ X_input = X_scaled.reshape(1, 24, 4)
96
+ y_scaled = model.predict(X_input)
97
+ pred = scaler_y.inverse_transform(y_scaled)[0][0]
98
+ print(f'Next month forecast: {pred:.2f} m')
99
+ ```
fig_lstm_baseline_forecast.png ADDED

Git LFS Details

  • SHA256: f9a420a830dc21834083889fc161c97712f8085bdf5d0b47359b43b44011d1be
  • Pointer size: 131 Bytes
  • Size of remote file: 151 kB
fig_lstm_comparison.png ADDED

Git LFS Details

  • SHA256: d356d87c6c571810fbadd5354c3f083e4c585a4e25f1edb272ebf763510e189d
  • Pointer size: 131 Bytes
  • Size of remote file: 251 kB
fig_lstm_errors.png ADDED
fig_lstm_forecast.png ADDED

Git LFS Details

  • SHA256: 90d892049706ee82e7c3a7a36a6b8fba12fb1382abf38cecca71a09ba41dc0b7
  • Pointer size: 131 Bytes
  • Size of remote file: 151 kB
fig_lstm_training.png ADDED
fig_lstm_tuned_training.png ADDED
inference.py ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ inference.py — LSTM Groundwater Level Forecasting
3
+ ==================================================
4
+ Usage
5
+ -----
6
+ from inference import load_model, forecast
7
+ model, scaler_X, scaler_y = load_model()
8
+ prediction = forecast(model, scaler_X, scaler_y, X_window)
9
+ """
10
+
11
+ import json
12
+ import joblib
13
+ import numpy as np
14
+ import pandas as pd
15
+ from pathlib import Path
16
+ from tensorflow.keras.models import load_model as keras_load
17
+
18
+ MODEL_PATH = Path(__file__).parent / "lstm_model.keras"
19
+ SCALER_X_PATH = Path(__file__).parent / "scaler_X.pkl"
20
+ SCALER_Y_PATH = Path(__file__).parent / "scaler_y.pkl"
21
+ CONFIG_PATH = Path(__file__).parent / "model_config.json"
22
+
23
+
24
+ def load_model():
25
+ """Load the LSTM model and both scalers."""
26
+ model = keras_load(MODEL_PATH)
27
+ scaler_X = joblib.load(SCALER_X_PATH)
28
+ scaler_y = joblib.load(SCALER_Y_PATH)
29
+ print("LSTM model and scalers loaded.")
30
+ return model, scaler_X, scaler_y
31
+
32
+
33
+ def load_config():
34
+ with open(CONFIG_PATH) as f:
35
+ return json.load(f)
36
+
37
+
38
+ def forecast(model, scaler_X, scaler_y, X_window: pd.DataFrame):
39
+ """
40
+ Predict the next month's groundwater level.
41
+
42
+ Parameters
43
+ ----------
44
+ model : loaded Keras model
45
+ scaler_X : fitted MinMaxScaler for features
46
+ scaler_y : fitted MinMaxScaler for target
47
+ X_window : DataFrame with columns [water_level, temperature,
48
+ precipitation, wind_speed] and exactly 24 rows
49
+ (the lookback window)
50
+
51
+ Returns
52
+ -------
53
+ prediction : float — forecasted water level in original units (m)
54
+ """
55
+ cfg = load_config()
56
+ required = cfg['features']
57
+ lookback = cfg['lookback_months']
58
+
59
+ missing = [c for c in required if c not in X_window.columns]
60
+ if missing:
61
+ raise ValueError(f"X_window is missing columns: {missing}")
62
+ if len(X_window) != lookback:
63
+ raise ValueError(f"X_window must have {lookback} rows, got {len(X_window)}")
64
+
65
+ X_scaled = scaler_X.transform(X_window[required])
66
+ X_input = X_scaled.reshape(1, lookback, len(required))
67
+
68
+ y_scaled = model.predict(X_input, verbose=0).flatten()
69
+ prediction = scaler_y.inverse_transform(y_scaled.reshape(-1, 1)).flatten()[0]
70
+
71
+ return float(prediction)
72
+
73
+
74
+ if __name__ == "__main__":
75
+ model, scaler_X, scaler_y = load_model()
76
+ cfg = load_config()
77
+ print(f"Model: {cfg['architecture']}")
78
+ print(f"Test RMSE: {cfg['test_metrics']['RMSE']} m")
79
+
80
+ # Dummy window — replace with real data
81
+ import numpy as np
82
+ dummy = pd.DataFrame({
83
+ 'water_level' : np.random.uniform(60, 75, 24),
84
+ 'temperature' : np.random.uniform(3, 15, 24),
85
+ 'precipitation': np.random.uniform(20, 120, 24),
86
+ 'wind_speed' : np.random.uniform(10, 25, 24),
87
+ })
88
+ pred = forecast(model, scaler_X, scaler_y, dummy)
89
+ print(f"\nForecast (next month): {pred:.4f} m")
lstm_model.keras ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:c19fbb8e942ce8360eb95361163ed979b76f071792ab1d726c05de62a35c25d0
3
+ size 675521
model_config.json ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model_type": "LSTM",
3
+ "architecture": "LSTM(128) \u2192 Dropout(0.1) \u2192 Dense(32) \u2192 Dense(1)",
4
+ "framework": "TensorFlow/Keras",
5
+ "task": "Single-step monthly groundwater level forecasting",
6
+ "features": [
7
+ "water_level",
8
+ "temperature",
9
+ "precipitation",
10
+ "wind_speed"
11
+ ],
12
+ "target": "water_level",
13
+ "lookback_months": 24,
14
+ "horizon_months": 1,
15
+ "tuning": {
16
+ "method": "Bayesian Optimisation (Keras Tuner)",
17
+ "n_trials": 20,
18
+ "best_config": {
19
+ "n_layers": 2,
20
+ "units_1": 64,
21
+ "dropout": 0.1,
22
+ "lr": 0.0007731576839806804,
23
+ "batch_size": 32
24
+ }
25
+ },
26
+ "data_splits": {
27
+ "train": {
28
+ "start": "1944-01-01",
29
+ "end": "2007-10-01",
30
+ "n_months": 766
31
+ },
32
+ "validation": {
33
+ "start": "2007-11-01",
34
+ "end": "2015-10-01",
35
+ "n_months": 96
36
+ },
37
+ "test": {
38
+ "start": "2015-11-01",
39
+ "end": "2023-10-01",
40
+ "n_months": 96
41
+ }
42
+ },
43
+ "test_metrics": {
44
+ "RMSE": 2.9386,
45
+ "MAE": 2.397,
46
+ "MAPE_pct": 3.671,
47
+ "R2": 0.5505,
48
+ "NSE": 0.5505
49
+ },
50
+ "notes": "Scaler fitted on train only. Oracle exog assumption \u2014 contemporaneous met vars used at forecast time."
51
+ }
scaler_X.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3c0d11dcc6e97c52246a4bc0a08b926e75edaa58a268400446e4eafdd81c199a
3
+ size 1127
scaler_y.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:865d8113514357d4b75338166ebba5771c914f7cd10b5892fc2187265ae4515c
3
+ size 975