gridops-ai / scripts /verify_finetuned_model.py
Aadithya
feat: finetuned Chronos-T5-Base integrated and verified — WAPE improvement over SARIMA
730cea7
Raw
History Blame Contribute Delete
7.21 kB
import os
import sys
import numpy as np
from loguru import logger
# Add project root to Python path
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
from autogluon.timeseries import TimeSeriesPredictor
from worker.chronos_client import get_chronos_client, LocalChronosClient
from worker.data_pipeline import EnergyDataPipeline
from agents.graph import build_gridops_graph
def calculate_wape(y_true, y_pred):
return np.sum(np.abs(y_true - y_pred)) / np.sum(np.abs(y_true))
def main():
checks_passed = 0
total_checks = 6
try:
# ---------------------------------------------------------
# Check 1: Model loads
# ---------------------------------------------------------
logger.info("Running Check 1: Model loads...")
predictor = TimeSeriesPredictor.load("models/chronos-pjm-finetuned")
logger.info(f"prediction_length: {predictor.prediction_length}")
logger.info(f"eval_metric: {predictor.eval_metric}")
logger.success("PASS - Check 1\n")
checks_passed += 1
# ---------------------------------------------------------
# Check 2: Client initializes
# ---------------------------------------------------------
logger.info("Running Check 2: Client initializes...")
client = get_chronos_client()
assert isinstance(client, LocalChronosClient), f"Expected LocalChronosClient, got {type(client)}"
logger.info(f"Client type: {type(client)}")
logger.success("PASS - Check 2\n")
checks_passed += 1
# ---------------------------------------------------------
# Check 3: Forecast shape is correct
# ---------------------------------------------------------
logger.info("Running Check 3: Forecast shape is correct...")
pipeline = EnergyDataPipeline('data_store/pjm_hourly_est.csv')
pipeline.load_and_preprocess()
pipeline.split_holdout(n_days=30)
assert pipeline.train is not None, "pipeline.train was not initialized"
assert pipeline.holdout is not None, "pipeline.holdout was not initialized"
forecast_result = client.forecast(
pipeline.train.to_numpy(), # type: ignore
prediction_length=30,
num_samples=20
)
chronos_p10 = np.array(forecast_result["p10"])
chronos_p50 = np.array(forecast_result["p50"])
chronos_p90 = np.array(forecast_result["p90"])
assert chronos_p10.shape == (30,), f"Expected shape (30,), got {chronos_p10.shape}"
assert chronos_p50.shape == (30,), f"Expected shape (30,), got {chronos_p50.shape}"
assert chronos_p90.shape == (30,), f"Expected shape (30,), got {chronos_p90.shape}"
assert (chronos_p10 <= chronos_p50).all(), "Expected p10 <= p50 element-wise"
assert (chronos_p50 <= chronos_p90).all(), "Expected p50 <= p90 element-wise"
logger.success("PASS - Check 3\n")
checks_passed += 1
# ---------------------------------------------------------
# Check 4: WAPE is better than SARIMA
# ---------------------------------------------------------
logger.info("Running Check 4: WAPE is better than SARIMA...")
pipeline.fit_sarima()
sarima_forecast = pipeline.forecast_sarima(steps=30)
sarima_wape = calculate_wape(pipeline.holdout.values, sarima_forecast)
chronos_wape = calculate_wape(pipeline.holdout.values, chronos_p50)
improvement = sarima_wape - chronos_wape
logger.info(f"SARIMA WAPE: {sarima_wape:.4f}")
logger.info(f"Chronos WAPE: {chronos_wape:.4f}")
logger.info(f"Improvement Delta: {improvement:.4f}")
assert chronos_wape < sarima_wape, "Chronos WAPE should be better (lower) than SARIMA WAPE"
logger.success("PASS - Check 4\n")
checks_passed += 1
# ---------------------------------------------------------
# Check 5: Full LangGraph pipeline runs with finetuned model
# ---------------------------------------------------------
logger.info("Running Check 5: Full LangGraph pipeline runs...")
graph = build_gridops_graph()
initial_state = {
"dataset_path": "data_store/pjm_hourly_est.csv",
"forecast_horizon": 30,
"severity_threshold": 0.40,
"data_stats": pipeline.data_stats,
"sarima_forecast": sarima_forecast.tolist(),
"sarima_wape": sarima_wape,
"sarima_backtest_wape": 0.0,
"backtest_wape": 0.0,
"chronos_p10": chronos_p10.tolist(),
"chronos_p50": chronos_p50.tolist(),
"chronos_p90": chronos_p90.tolist(),
"chronos_wape": chronos_wape,
"interval_sharpness": EnergyDataPipeline.calculate_interval_sharpness(chronos_p10, chronos_p90),
"seasonality_regime": pipeline.detect_seasonality_regime(),
"historical_data": pipeline.train.to_numpy()[-90:].tolist(),
"holdout_data": pipeline.holdout.to_numpy().tolist(),
"holdout_dates": [d.date().isoformat() for d in pipeline.holdout.index],
"forecast_dates": [],
"analysis_findings": [],
"graph_execution_trace": [],
"pipeline_start_ts": "",
"pipeline_end_ts": "",
}
result = graph.invoke(initial_state)
mandate = result.get('trading_mandate', {})
assert isinstance(mandate, dict), "trading_mandate should be a dictionary"
valid_recs = ['BUY', 'SELL', 'HOLD', 'MAINTAIN OPS', 'INCREASE GENERATION', 'DEPLOY RESERVES']
rec = mandate.get('recommendation', '')
assert rec in valid_recs, f"recommendation '{rec}' not in expected valid values"
trace = result.get('graph_execution_trace', [])
assert len(trace) >= 6, f"Expected at least 6 nodes in execution trace, got {len(trace)}"
logger.success("PASS - Check 5\n")
checks_passed += 1
# ---------------------------------------------------------
# Check 6: Finetuned vs base comparison
# ---------------------------------------------------------
logger.info("Running Check 6: Finetuned vs base comparison...")
improvement_pct = (improvement / sarima_wape) * 100
print("\nModel WAPE Improvement")
print("-" * 50)
print(f"SARIMA baseline {sarima_wape:.4f} —")
print(f"Chronos-T5-Base (FT) {chronos_wape:.4f} +{improvement_pct:.1f}%\n")
logger.success("PASS - Check 6\n")
checks_passed += 1
except AssertionError as e:
logger.error(f"Assertion failed: {str(e)}")
except Exception as e:
logger.exception(f"Unexpected error: {str(e)}")
finally:
if checks_passed == total_checks:
print("✅ All 6 checks passed — finetuned model verified")
else:
print(f"❌ {total_checks - checks_passed} checks failed — see details above")
if __name__ == '__main__':
main()