Spaces:
Sleeping
Sleeping
| import sys | |
| import os | |
| sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) | |
| import pandas as pd | |
| import numpy as np | |
| from typing import Dict, Tuple | |
| from sklearn.model_selection import train_test_split, cross_val_score | |
| from sklearn.metrics import classification_report, confusion_matrix, accuracy_score | |
| import xgboost as xgb | |
| import joblib | |
| import json | |
| class FupShopModelTrainer: | |
| def __init__(self, model_path: str = "models/fupshop_model.pkl"): | |
| self.model_path = model_path | |
| self.model = None | |
| self.feature_names = None | |
| self.metrics = {} | |
| self.X_train_ref = None | |
| self.y_train_ref = None | |
| def prepare_data(self, df: pd.DataFrame): | |
| feature_cols = [col for col in df.columns if col not in ['url', 'label']] | |
| self.feature_names = feature_cols | |
| X = df[feature_cols].values | |
| y = df['label'].values | |
| X_train, X_temp, y_train, y_temp = train_test_split( | |
| X, y, test_size=0.3, random_state=42, stratify=y | |
| ) | |
| X_val, X_test, y_val, y_test = train_test_split( | |
| X_temp, y_temp, test_size=0.5, random_state=42, stratify=y_temp | |
| ) | |
| print(f"Train: {len(X_train)} | Val: {len(X_val)} | Test: {len(X_test)}") | |
| return X_train, X_val, X_test, y_train, y_val, y_test | |
| def train(self, X_train, y_train, X_val=None, y_val=None): | |
| print("\nTraining XGBoost...") | |
| self.X_train_ref = X_train | |
| self.y_train_ref = y_train | |
| self.model = xgb.XGBClassifier( | |
| n_estimators=300, | |
| max_depth=4, | |
| learning_rate=0.05, | |
| subsample=0.7, | |
| colsample_bytree=0.7, | |
| reg_alpha=0.1, | |
| reg_lambda=1.0, | |
| random_state=42, | |
| eval_metric='logloss', | |
| early_stopping_rounds=30 | |
| ) | |
| eval_set = [(X_train, y_train)] | |
| if X_val is not None: | |
| eval_set.append((X_val, y_val)) | |
| self.model.fit( | |
| X_train, y_train, | |
| eval_set=eval_set, | |
| verbose=False | |
| ) | |
| print(f"Best iteration: {self.model.best_iteration}") | |
| return self.model | |
| def evaluate(self, X_test, y_test): | |
| y_pred = self.model.predict(X_test) | |
| accuracy = accuracy_score(y_test, y_pred) | |
| print(f"\nTest Accuracy: {accuracy:.4f}") | |
| print(classification_report(y_test, y_pred, target_names=['Legitimate', 'Phishing'])) | |
| train_pred = self.model.predict(self.X_train_ref) | |
| train_acc = accuracy_score(self.y_train_ref, train_pred) | |
| gap = train_acc - accuracy | |
| print(f"\nTrain Accuracy: {train_acc:.4f}") | |
| print(f"Test Accuracy: {accuracy:.4f}") | |
| print(f"Overfitting Gap: {gap:.4f}") | |
| if gap > 0.1: | |
| print("⚠️ WARNING: Model is overfitting!") | |
| else: | |
| print("✅ Model generalizes well") | |
| self.metrics = { | |
| 'accuracy': float(accuracy), | |
| 'train_accuracy': float(train_acc), | |
| 'overfitting_gap': float(gap), | |
| 'feature_importance': dict(zip( | |
| self.feature_names, | |
| self.model.feature_importances_.tolist() | |
| )) | |
| } | |
| return self.metrics | |
| def cross_validate(self, X, y): | |
| print("\nRunning 5-Fold Cross-Validation...") | |
| cv_model = xgb.XGBClassifier(n_estimators=100, max_depth=4, random_state=42) | |
| scores = cross_val_score(cv_model, X, y, cv=5, scoring='accuracy') | |
| print(f"CV Scores: {scores}") | |
| print(f"Mean: {scores.mean():.4f} (+/- {scores.std():.4f})") | |
| return scores | |
| def save(self): | |
| os.makedirs(os.path.dirname(self.model_path), exist_ok=True) | |
| joblib.dump(self.model, self.model_path) | |
| with open(self.model_path.replace('.pkl', '_metrics.json'), 'w') as f: | |
| json.dump(self.metrics, f, indent=2) | |
| with open(self.model_path.replace('.pkl', '_features.json'), 'w') as f: | |
| json.dump(self.feature_names, f, indent=2) | |
| print(f"\nModel saved to {self.model_path}") | |
| def train_pipeline(): | |
| from utils.dataset_builder import DatasetBuilder, SAMPLE_LEGITIMATE_URLS | |
| builder = DatasetBuilder(urlhaus_key="6fe27ca7aa571ad003699cc22fb33160c911773d0c979d8f") | |
| df = builder.build_full_dataset(SAMPLE_LEGITIMATE_URLS) | |
| trainer = FupShopModelTrainer( | |
| model_path="/workspaces/fupshop-detector/src/models/fupshop_model.pkl" | |
| ) | |
| X_train, X_val, X_test, y_train, y_val, y_test = trainer.prepare_data(df) | |
| trainer.train(X_train, y_train, X_val, y_val) | |
| trainer.evaluate(X_test, y_test) | |
| feature_cols = [col for col in df.columns if col not in ['url', 'label']] | |
| X_full = df[feature_cols].values | |
| y_full = df['label'].values | |
| trainer.cross_validate(X_full, y_full) | |
| trainer.save() | |
| return trainer | |
| if __name__ == "__main__": | |
| print("FupShop Detector - Training") | |
| print("=" * 50) | |
| train_pipeline() | |