File size: 3,459 Bytes
df5a768
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
90
91
92
93
94
95
96
97
98
99
100
import os
import json
import numpy as np
import torch
import torch.nn as nn
import joblib
from flask import Flask, request, jsonify, render_template

# ─────────────────────────────────────────
#  Model definition  (must match training)
# ─────────────────────────────────────────
class LSTMModel(nn.Module):
    def __init__(self, input_size=10, hidden_size=64, num_layers=2, output_size=1):
        super().__init__()
        self.lstm = nn.LSTM(input_size, hidden_size, num_layers, batch_first=True)
        self.fc   = nn.Linear(hidden_size, output_size)

    def forward(self, x):
        out, _ = self.lstm(x)          # out: (batch, seq_len, hidden)
        out    = self.fc(out[:, -1, :])  # last timestep → (batch, 1)
        return out

# ─────────────────────────────────────────
#  Load model & scaler once at startup
# ─────────────────────────────────────────
BASE = os.path.dirname(__file__)

model = LSTMModel()
state = torch.load(os.path.join(BASE, "model_LSTM.pth"), map_location="cpu")
model.load_state_dict(state)
model.eval()

scaler = joblib.load(os.path.join(BASE, "scaler.joblib"))

FEATURE_NAMES = [
    "Active_Energy_Delivered_Received",
    "Current_Phase_Average",
    "Active_Power",
    "Wind_Speed",
    "Weather_Temperature_Celsius",
    "Weather_Relative_Humidity",
    "Global_Horizontal_Radiation",
    "Diffuse_Horizontal_Radiation",
    "Wind_Direction",
    "Weather_Daily_Rainfall",
]

# ─────────────────────────────────────────
#  Flask app
# ─────────────────────────────────────────
app = Flask(__name__)


@app.route("/")
def index():
    return render_template("index.html", features=FEATURE_NAMES)


@app.route("/predict", methods=["POST"])
def predict():
    try:
        data = request.get_json(force=True)

        # Expect: { "rows": [[f1,f2,...,f10], [f1,f2,...,f10], ...] }
        rows = data.get("rows", [])
        if not rows:
            return jsonify({"error": "No input rows provided."}), 400

        # Validate shape
        for i, row in enumerate(rows):
            if len(row) != 10:
                return jsonify({"error": f"Row {i+1} must have exactly 10 values."}), 400

        X = np.array(rows, dtype=np.float32)          # (seq_len, 10)
        X_scaled = scaler.transform(X)                 # scale each timestep
        tensor = torch.tensor(X_scaled).unsqueeze(0)  # (1, seq_len, 10)

        with torch.no_grad():
            pred = model(tensor).item()

        return jsonify({
            "prediction": round(pred, 6),
            "unit": "Active Energy (scaled output)",
            "seq_len": len(rows),
        })

    except Exception as exc:
        return jsonify({"error": str(exc)}), 500


@app.route("/health")
def health():
    return jsonify({"status": "ok", "model": "LSTM Solar Predictor"})


if __name__ == "__main__":
    port = int(os.environ.get("PORT", 5000))
    debug = os.environ.get("FLASK_DEBUG", "0") == "1"
    app.run(host="0.0.0.0", port=port, debug=debug)