Files
Lotto-Tip-Generator/scripts/utils/model_evaluator.py
T
cbazzaandClaude Sonnet 4.5 f6106b8333 Initial commit: Lotto number generator project
This project includes multiple AI/ML-based lottery number generators for
German Lotto 6aus49, including pattern analysis, weighted predictions,
and hybrid approaches. Features automated weekly tip generation,
performance tracking, and Telegram bot integration.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-16 14:47:59 +01:00

242 lines
8.5 KiB
Python

#!/usr/bin/env python3
"""
Model Evaluation & Reporting Module
Für umfassende ML-Model-Evaluation
"""
import numpy as np
import json
import os
from datetime import datetime
from collections import defaultdict
try:
from sklearn.model_selection import cross_val_score
from sklearn.metrics import r2_score, mean_absolute_error, mean_squared_error
SKLEARN_AVAILABLE = True
except ImportError:
SKLEARN_AVAILABLE = False
class ModelEvaluator:
"""Evaluiert und dokumentiert ML-Modelle."""
def __init__(self, cache_path=None):
self.cache_path = cache_path
self.evaluation_results = {}
self.report_file = os.path.join(cache_path, 'model_evaluation.json') if cache_path else None
def evaluate_model(self, model, X_train, X_test, y_train, y_test, model_name, number):
"""
Umfassende Model-Evaluation.
Returns:
dict with metrics
"""
if not SKLEARN_AVAILABLE:
return {'error': 'scikit-learn not available'}
results = {
'model_name': model_name,
'number': number,
'timestamp': datetime.now().isoformat(),
'data_size': {
'train': len(X_train),
'test': len(X_test)
}
}
try:
# 1. Training Score
y_train_pred = model.predict(X_train)
results['train_r2'] = r2_score(y_train, y_train_pred)
results['train_mae'] = mean_absolute_error(y_train, y_train_pred)
results['train_rmse'] = np.sqrt(mean_squared_error(y_train, y_train_pred))
# 2. Test Score
y_test_pred = model.predict(X_test)
results['test_r2'] = r2_score(y_test, y_test_pred)
results['test_mae'] = mean_absolute_error(y_test, y_test_pred)
results['test_rmse'] = np.sqrt(mean_squared_error(y_test, y_test_pred))
# 3. Overfit Detection
results['overfitting'] = results['train_r2'] - results['test_r2']
results['is_overfit'] = results['overfitting'] > 0.2
# 4. Cross-Validation (3-fold for speed)
try:
cv_scores = cross_val_score(model, X_train, y_train, cv=3, scoring='r2')
results['cv_mean'] = float(np.mean(cv_scores))
results['cv_std'] = float(np.std(cv_scores))
results['cv_scores'] = [float(s) for s in cv_scores]
except Exception as e:
results['cv_error'] = str(e)
# 5. Feature Importance (if available)
if hasattr(model, 'feature_importances_'):
importances = model.feature_importances_
results['top_features'] = {
f'feature_{i}': float(imp)
for i, imp in enumerate(importances[:10]) # Top 10
}
results['feature_importance_sum'] = float(np.sum(importances))
# 6. Prediction Distribution
results['pred_distribution'] = {
'min': float(np.min(y_test_pred)),
'max': float(np.max(y_test_pred)),
'mean': float(np.mean(y_test_pred)),
'std': float(np.std(y_test_pred))
}
# 7. Quality Rating
test_r2 = results['test_r2']
if test_r2 > 0.7:
results['quality'] = 'Excellent'
elif test_r2 > 0.5:
results['quality'] = 'Good'
elif test_r2 > 0.3:
results['quality'] = 'Fair'
elif test_r2 > 0.1:
results['quality'] = 'Poor'
else:
results['quality'] = 'Very Poor'
except Exception as e:
results['error'] = str(e)
return results
def add_evaluation(self, number, model_name, results):
"""Fügt Evaluation-Result hinzu."""
key = f"{number}_{model_name}"
self.evaluation_results[key] = results
def generate_summary_report(self):
"""Generiert Zusammenfassungs-Report."""
if not self.evaluation_results:
return "Keine Evaluation-Daten vorhanden"
report = []
report.append("=" * 80)
report.append("MODEL EVALUATION SUMMARY")
report.append("=" * 80)
report.append(f"Timestamp: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
report.append(f"Total Evaluations: {len(self.evaluation_results)}")
report.append("")
# Aggregate stats
all_test_r2 = []
all_cv_mean = []
overfit_count = 0
quality_dist = defaultdict(int)
for key, results in self.evaluation_results.items():
if 'test_r2' in results:
all_test_r2.append(results['test_r2'])
if 'cv_mean' in results:
all_cv_mean.append(results['cv_mean'])
if results.get('is_overfit'):
overfit_count += 1
if 'quality' in results:
quality_dist[results['quality']] += 1
# Overall Stats
report.append("OVERALL STATISTICS")
report.append("-" * 80)
if all_test_r2:
report.append(f"Test R² Score:")
report.append(f" Mean: {np.mean(all_test_r2):.4f}")
report.append(f" Median: {np.median(all_test_r2):.4f}")
report.append(f" Std: {np.std(all_test_r2):.4f}")
report.append(f" Min: {np.min(all_test_r2):.4f}")
report.append(f" Max: {np.max(all_test_r2):.4f}")
report.append("")
if all_cv_mean:
report.append(f"Cross-Validation R² Score:")
report.append(f" Mean: {np.mean(all_cv_mean):.4f}")
report.append(f" Std: {np.std(all_cv_mean):.4f}")
report.append("")
report.append(f"Overfitting Detection:")
report.append(f" Overfit Models: {overfit_count}/{len(self.evaluation_results)}")
report.append("")
report.append(f"Quality Distribution:")
for quality in ['Excellent', 'Good', 'Fair', 'Poor', 'Very Poor']:
count = quality_dist.get(quality, 0)
pct = (count / len(self.evaluation_results)) * 100 if self.evaluation_results else 0
report.append(f" {quality:12}: {count:3} ({pct:5.1f}%)")
report.append("")
# Top 10 Best Models
sorted_results = sorted(
[(k, v) for k, v in self.evaluation_results.items() if 'test_r2' in v],
key=lambda x: x[1]['test_r2'],
reverse=True
)[:10]
report.append("TOP 10 MODELS (by Test R²)")
report.append("-" * 80)
report.append(f"{'Number':<10} {'Model':<20} {'Test R²':<12} {'CV Mean':<12} {'Quality':<15}")
report.append("-" * 80)
for key, results in sorted_results:
number = results.get('number', 'N/A')
model = results.get('model_name', 'N/A')
test_r2 = results.get('test_r2', 0)
cv_mean = results.get('cv_mean', 0)
quality = results.get('quality', 'N/A')
report.append(f"{number:<10} {model:<20} {test_r2:<12.4f} {cv_mean:<12.4f} {quality:<15}")
report.append("")
report.append("=" * 80)
return "\n".join(report)
def save_evaluation(self):
"""Speichert Evaluation persistent."""
if not self.report_file:
return
try:
os.makedirs(os.path.dirname(self.report_file), exist_ok=True)
data = {
'timestamp': datetime.now().isoformat(),
'total_evaluations': len(self.evaluation_results),
'results': self.evaluation_results
}
with open(self.report_file, 'w') as f:
json.dump(data, f, indent=2)
print(f" 💾 Evaluation saved: {self.report_file}")
except Exception as e:
print(f" ⚠️ Could not save evaluation: {e}")
def load_evaluation(self):
"""Lädt gespeicherte Evaluation."""
if not self.report_file or not os.path.exists(self.report_file):
return
try:
with open(self.report_file, 'r') as f:
data = json.load(f)
self.evaluation_results = data.get('results', {})
timestamp = data.get('timestamp', 'Unknown')
print(f" 📂 Evaluation loaded: {len(self.evaluation_results)} results (from {timestamp[:10]})")
except Exception as e:
print(f" ⚠️ Could not load evaluation: {e}")
def print_summary(self):
"""Druckt Summary auf Console."""
summary = self.generate_summary_report()
print("\n" + summary)