| import os |
| import sys |
| import joblib |
| import numpy as np |
| import pandas as pd |
| import matplotlib.pyplot as plt |
| import matplotlib.gridspec as gridspec |
| from sklearn.metrics import ( |
| roc_auc_score, f1_score, precision_score, |
| recall_score, accuracy_score, roc_curve, |
| ) |
|
|
| sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))) |
|
|
| PROCESSED_DIR = os.path.join('data', 'processed') |
| MODELS_DIR = os.path.join('models') |
| CHARTS_DIR = os.path.join('data', 'processed', 'charts') |
|
|
| MODEL_FILES = { |
| "Logistic Regression" : "logistic_regression.pkl", |
| "Decision Tree" : "decision_tree.pkl", |
| "Random Forest" : "random_forest.pkl", |
| "Extra Trees" : "extra_trees.pkl", |
| "AdaBoost" : "adaboost.pkl", |
| "Gradient Boosting" : "gradient_boosting.pkl", |
| "XGBoost" : "xgboost.pkl", |
| "LightGBM" : "lightgbm.pkl", |
| "CatBoost" : "catboost.pkl", |
| } |
|
|
|
|
| def load_test_data(): |
| X_test = joblib.load(os.path.join(PROCESSED_DIR, 'X_test.pkl')) |
| y_test = joblib.load(os.path.join(PROCESSED_DIR, 'y_test.pkl')) |
| return X_test, y_test |
|
|
|
|
| def score_all_models(X_test, y_test): |
| results = [] |
| for name, fname in MODEL_FILES.items(): |
| path = os.path.join(MODELS_DIR, fname) |
| model = joblib.load(path) |
| y_pred = model.predict(X_test) |
| y_proba = model.predict_proba(X_test)[:, 1] |
| results.append({ |
| "Model" : name, |
| "AUC-ROC" : round(roc_auc_score(y_test, y_proba), 4), |
| "F1" : round(f1_score(y_test, y_pred), 4), |
| "Precision" : round(precision_score(y_test, y_pred), 4), |
| "Recall" : round(recall_score(y_test, y_pred), 4), |
| "Accuracy" : round(accuracy_score(y_test, y_pred), 4), |
| }) |
| print(f" Scored : {name}") |
| df = pd.DataFrame(results).sort_values("AUC-ROC", ascending=False).reset_index(drop=True) |
| df.index += 1 |
| return df |
|
|
|
|
| def plot_bar_comparison(df): |
| os.makedirs(CHARTS_DIR, exist_ok=True) |
| metrics = ["AUC-ROC", "F1", "Precision", "Recall"] |
| fig, axes = plt.subplots(2, 2, figsize=(16, 10)) |
| axes = axes.flatten() |
| colors = plt.cm.RdYlGn(np.linspace(0.3, 0.9, len(df))) |
|
|
| for i, metric in enumerate(metrics): |
| vals = df.sort_values(metric, ascending=True) |
| bars = axes[i].barh(vals["Model"], vals[metric], color=colors) |
| axes[i].set_title(metric, fontsize=13, fontweight='bold') |
| axes[i].set_xlim(max(0, vals[metric].min() - 0.05), 1.0) |
| for bar, val in zip(bars, vals[metric]): |
| axes[i].text(val + 0.002, bar.get_y() + bar.get_height() / 2, |
| f'{val:.4f}', va='center', fontsize=8) |
|
|
| plt.suptitle('Model Benchmark — All Metrics', fontsize=15, fontweight='bold') |
| plt.tight_layout() |
| path = os.path.join(CHARTS_DIR, 'metric_comparison.png') |
| plt.savefig(path, bbox_inches='tight', dpi=130) |
| plt.close() |
| print(f" Saved : {path}") |
|
|
|
|
| def plot_roc_curves(X_test, y_test): |
| os.makedirs(CHARTS_DIR, exist_ok=True) |
| plt.figure(figsize=(10, 8)) |
| colors = plt.cm.tab10(np.linspace(0, 1, len(MODEL_FILES))) |
|
|
| for (name, fname), color in zip(MODEL_FILES.items(), colors): |
| model = joblib.load(os.path.join(MODELS_DIR, fname)) |
| y_proba = model.predict_proba(X_test)[:, 1] |
| fpr, tpr, _ = roc_curve(y_test, y_proba) |
| auc = roc_auc_score(y_test, y_proba) |
| plt.plot(fpr, tpr, label=f"{name} (AUC={auc:.4f})", color=color, linewidth=1.8) |
|
|
| plt.plot([0, 1], [0, 1], 'k--', linewidth=1, label='Random (AUC=0.5)') |
| plt.xlabel('False Positive Rate', fontsize=12) |
| plt.ylabel('True Positive Rate', fontsize=12) |
| plt.title('ROC Curves — All Models', fontsize=14, fontweight='bold') |
| plt.legend(loc='lower right', fontsize=9) |
| plt.tight_layout() |
| path = os.path.join(CHARTS_DIR, 'roc_curves.png') |
| plt.savefig(path, bbox_inches='tight', dpi=130) |
| plt.close() |
| print(f" Saved : {path}") |
|
|
|
|
| def save_best_model(df): |
| best_name = df.iloc[0]["Model"] |
| best_file = MODEL_FILES[best_name] |
| best_model = joblib.load(os.path.join(MODELS_DIR, best_file)) |
| out_path = os.path.join(MODELS_DIR, 'best_model.pkl') |
| joblib.dump(best_model, out_path) |
| print(f" Best : {best_name} (AUC-ROC={df.iloc[0]['AUC-ROC']})") |
| print(f" Saved : {out_path}") |
| print(f" Note : Run tune_catboost.py to get the tuned version → catboost_final.pkl") |
| return best_name, best_model |
|
|
|
|
| def run(): |
| print("=" * 55) |
| print(" Model Comparison & Selection") |
| print("=" * 55) |
|
|
| X_test, y_test = load_test_data() |
|
|
| print("\n[1] Scoring all models ...") |
| df = score_all_models(X_test, y_test) |
|
|
| print("\n[2] Generating comparison bar charts ...") |
| plot_bar_comparison(df) |
|
|
| print("\n[3] Generating ROC curves ...") |
| plot_roc_curves(X_test, y_test) |
|
|
| print("\n[4] Saving best model ...") |
| best_name, _ = save_best_model(df) |
|
|
| print("\n") |
| print("=" * 55) |
| print(" Benchmark Results (ranked by AUC-ROC)") |
| print("=" * 55) |
| print(df.to_string()) |
| print("=" * 55) |
|
|
| return df |
|
|
|
|
| if __name__ == '__main__': |
| run() |
|
|