Spaces:
Sleeping
Sleeping
| """ | |
| Model selection function for phishing detection. | |
| Simple interface to select the best model using the modular components. | |
| """ | |
| import logging | |
| from typing import Tuple, Any | |
| import pandas as pd | |
| from sklearn.preprocessing import StandardScaler | |
| from phising_detection.models import data_prep, model_configs, evaluation | |
| from phising_detection.utils.hopsworks_utils import connect_to_hopsworks | |
| logger = logging.getLogger(__name__) | |
| def select_model( | |
| feature_group_name: str = "urlscan_features", | |
| feature_group_version: int = 1, | |
| val_size: float = 0.15, | |
| test_size: float = 0.15 | |
| ) -> Tuple[Any, str, dict, StandardScaler]: | |
| """ | |
| Select the best model for phishing detection. | |
| This function: | |
| 1. Loads data from Hopsworks | |
| 2. Prepares and splits data | |
| 3. Trains multiple models | |
| 4. Compares them on validation set | |
| 5. Returns the best performing model | |
| Args: | |
| feature_group_name: Name of Hopsworks feature group | |
| feature_group_version: Version of feature group | |
| val_size: Fraction for validation | |
| test_size: Fraction for testing | |
| Returns: | |
| Tuple of (best_model, model_name, test_metrics, scaler) | |
| """ | |
| logger.info("=" * 80) | |
| logger.info("Starting Model Selection") | |
| logger.info("=" * 80) | |
| # 1. Load data | |
| project = connect_to_hopsworks() | |
| raw_data = data_prep.load_data(project, feature_group_name, feature_group_version) | |
| # 2. Prepare data (keep unnormalized data for later re-normalization) | |
| logger.info("\n2. Preparing data...") | |
| # Split WITHOUT normalization first | |
| X, y = data_prep.prepare_features(raw_data) | |
| X_train_raw, X_val_raw, X_test_raw, y_train, y_val, y_test = data_prep.split_data( | |
| X, y, val_size=val_size, test_size=test_size | |
| ) | |
| # Normalize the splits | |
| X_train, X_val, X_test, scaler = data_prep.normalize_features( | |
| X_train_raw, X_val_raw, X_test_raw | |
| ) | |
| # 3. Train models | |
| logger.info("\n3. Training models...") | |
| models = model_configs.train_models(X_train, y_train) | |
| # 4. Compare models | |
| logger.info("\n4. Comparing models...") | |
| results_df = evaluation.compare_models(models, X_train, X_val, y_train, y_val) | |
| # Display results | |
| logger.info("\n" + "=" * 80) | |
| logger.info("MODEL COMPARISON RESULTS") | |
| logger.info("=" * 80) | |
| logger.info("\n" + results_df.to_string(index=False)) | |
| # 5. Tune top 3 models with hyperparameter search | |
| logger.info("\n5. Tuning hyperparameters for top 3 models...") | |
| top_3_names = results_df.head(3)['Model'].tolist() | |
| logger.info(f"Top 3 models: {top_3_names}") | |
| tuned_models = {} | |
| for model_name in top_3_names: | |
| tuned_models[model_name] = model_configs.tune_hyperparameters( | |
| model_name, X_train, y_train, cv_folds=5 | |
| ) | |
| # Compare tuned models on validation set | |
| logger.info("\n6. Comparing tuned models...") | |
| tuned_results = evaluation.compare_models(tuned_models, X_train, X_val, y_train, y_val) | |
| logger.info("\n" + tuned_results.to_string(index=False)) | |
| # Select best tuned model | |
| best_model_name, best_model = evaluation.select_best_model(tuned_results, tuned_models) | |
| # 7. Combine train+val and retrain with re-normalization | |
| logger.info(f"\n7. Retraining {best_model_name} on combined train+val set...") | |
| # Combine RAW (unnormalized) data | |
| X_train_val_raw = pd.concat([X_train_raw, X_val_raw]) | |
| y_train_val = pd.concat([y_train, y_val]) | |
| # Re-normalize with combined statistics for better estimates | |
| logger.info("Re-normalizing with combined train+val statistics...") | |
| final_scaler = StandardScaler() | |
| X_train_val = X_train_val_raw.copy() | |
| X_test_final = X_test_raw.copy() | |
| # Normalize | |
| continuous_features = data_prep.CONTINUOUS_FEATURES | |
| X_train_val[continuous_features] = final_scaler.fit_transform(X_train_val_raw[continuous_features]) | |
| X_test_final[continuous_features] = final_scaler.transform(X_test_raw[continuous_features]) | |
| # Update scaler and test set | |
| scaler = final_scaler | |
| X_test = X_test_final | |
| # Train on properly re-normalized combined data | |
| best_model = model_configs.train_model(best_model, X_train_val, y_train_val, best_model_name) | |
| # 8. Evaluate on test set | |
| logger.info("\n8. Evaluating on test set...") | |
| test_metrics = evaluation.evaluate_model_detailed( | |
| best_model, X_test, y_test, best_model_name, "Test" | |
| ) | |
| logger.info("\n" + "=" * 80) | |
| logger.info("Model Selection Complete!") | |
| logger.info(f"Selected: {best_model_name}") | |
| logger.info(f"Test Accuracy: {test_metrics['Accuracy']:.4f}") | |
| logger.info(f"Test F1 Score: {test_metrics['F1 Score']:.4f}") | |
| logger.info("=" * 80) | |
| return best_model, best_model_name, test_metrics, scaler | |
| if __name__ == "__main__": | |
| # Example usage | |
| logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') | |
| model, name, metrics, scaler = select_model() | |
| print(f"\nBest Model: {name}") | |
| print(f"Metrics: {metrics}") | |