| | """ |
| | Automatic Retraining Service |
| | Background service that retrains model on schedule |
| | """ |
| | import time |
| | import threading |
| | from datetime import datetime, timedelta |
| | from typing import Optional |
| | from .config import CONFIG |
| | from .trainer import ModelTrainer |
| |
|
| |
|
| | class RetrainingService: |
| | """Background service for automatic model retraining""" |
| | |
| | def __init__(self, trainer: Optional[ModelTrainer] = None): |
| | self.trainer = trainer or ModelTrainer() |
| | self.running = False |
| | self.thread = None |
| | self.check_interval_minutes = 60 |
| | |
| | def start(self): |
| | """Start the retraining service""" |
| | if self.running: |
| | print("Retraining service already running") |
| | return |
| | |
| | self.running = True |
| | self.thread = threading.Thread(target=self._run_loop, daemon=True) |
| | self.thread.start() |
| | |
| | print(f"Retraining service started (check interval: {self.check_interval_minutes} min)") |
| | print(f"Will retrain every {CONFIG.RETRAIN_INTERVAL_HOURS} hours") |
| | |
| | def stop(self): |
| | """Stop the retraining service""" |
| | self.running = False |
| | if self.thread: |
| | self.thread.join(timeout=5) |
| | print("Retraining service stopped") |
| | |
| | def _run_loop(self): |
| | """Main loop for retraining service""" |
| | while self.running: |
| | try: |
| | |
| | if self.trainer.should_retrain(): |
| | print(f"\n[{datetime.now()}] Starting automatic retraining...") |
| | result = self.trainer.train() |
| | |
| | if result.get("success"): |
| | summary = result |
| | print(f"✓ Retraining completed successfully") |
| | print(f" - Models trained: {', '.join(summary.get('models_trained', []))}") |
| | print(f" - Best model: {summary.get('best_model', 'N/A')}") |
| | best_metrics = summary.get('best_metrics', {}) |
| | print(f" - Best R²: {best_metrics.get('test_r2', 0):.4f}") |
| | print(f" - Best RMSE: {best_metrics.get('test_rmse', 0):.4f}") |
| | if summary.get('ensemble_weights'): |
| | print(f" - Ensemble models: {len(summary['ensemble_weights'])}") |
| | else: |
| | reason = result.get("reason", result.get("error", "Unknown")) |
| | print(f"✗ Retraining skipped: {reason}") |
| | |
| | except Exception as e: |
| | print(f"Error in retraining loop: {e}") |
| | |
| | |
| | for _ in range(self.check_interval_minutes * 60): |
| | if not self.running: |
| | break |
| | time.sleep(1) |
| | |
| | def force_retrain(self): |
| | """Force immediate retraining""" |
| | print(f"\n[{datetime.now()}] Forcing model retraining...") |
| | result = self.trainer.train(force=True) |
| | return result |
| | |
| | def get_status(self) -> dict: |
| | """Get service status""" |
| | return { |
| | "running": self.running, |
| | "check_interval_minutes": self.check_interval_minutes, |
| | "retrain_interval_hours": CONFIG.RETRAIN_INTERVAL_HOURS, |
| | "model_info": self.trainer.get_model_info() |
| | } |
| |
|
| |
|
| | |
| | _service = None |
| |
|
| |
|
| | def get_retraining_service() -> RetrainingService: |
| | """Get or create global retraining service""" |
| | global _service |
| | if _service is None: |
| | _service = RetrainingService() |
| | return _service |
| |
|
| |
|
| | def start_retraining_service(): |
| | """Start global retraining service""" |
| | service = get_retraining_service() |
| | service.start() |
| | return service |
| |
|
| |
|
| | def stop_retraining_service(): |
| | """Stop global retraining service""" |
| | global _service |
| | if _service: |
| | _service.stop() |
| | _service = None |
| |
|