SaiBon99's picture
Add Gradio app and inference pipeline for phishing detection
45dfcaa
Raw
History Blame Contribute Delete
5.03 kB
"""
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}")