Spaces:
Build error
Build error
| 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__) | |
| def index(): | |
| return render_template("index.html", features=FEATURE_NAMES) | |
| 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 | |
| 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) | |