SentinelAI / src /ai /classification /xgboost_classifier.py
ACA050's picture
Upload 114 files
5a3b9db verified
Raw
History Blame Contribute Delete
4.89 kB
import pandas as pd
import xgboost as xgb
import logging
import json
from pathlib import Path
from datetime import datetime
from typing import Dict, Any
logger = logging.getLogger(__name__)
class XGBoostClassifier:
"""
Module 3: Attack Classification.
Supervised XGBoost model to classify authentication events into specific attack categories.
"""
def __init__(self, model_dir: str, config: Dict[str, Any]):
self.model_dir = Path(model_dir)
self.model = None
self.config = config
self.version = "1.0.0"
self.labels = self.config.get("classification_labels", [
"Normal Authentication",
"Credential Stuffing",
"Password Spray",
"Brute Force",
"Impossible Travel",
"Insider Threat",
"Privilege Abuse",
"Suspicious Device"
])
self.features_used = config.get("classifier_features", [
"hour_of_day", "day_of_week", "is_weekend", "is_working_hour",
"is_failure", "country_encoded", "is_mfa", "time_since_last_login",
"rolling_failures_24h"
])
def train(self, X: pd.DataFrame, y: pd.Series):
logger.info(f"Training Attack Classification model on {len(X)} samples...")
missing_cols = [c for c in self.features_used if c not in X.columns]
for c in missing_cols:
X[c] = 0
X_train = X[self.features_used].fillna(0)
self.model = xgb.XGBClassifier(
objective="multi:softprob",
num_class=len(self.labels),
eval_metric="mlogloss",
random_state=42
)
self.model.fit(X_train, y)
self.persist()
def persist(self):
import hashlib
self.model_dir.mkdir(parents=True, exist_ok=True)
version_id = datetime.now().strftime("%Y%m%d_%H%M%S")
model_path = self.model_dir / f"xgboost_attack_classifier_{version_id}.json"
meta_path = self.model_dir / f"classifier_metadata_{version_id}.json"
if self.model is not None:
self.model.save_model(model_path)
with open(model_path, "rb") as f:
checksum = hashlib.sha256(f.read()).hexdigest()
metadata = {
"model_version": version_id,
"training_dataset_version": "1.0",
"feature_schema_version": "1.0",
"training_date": datetime.now().isoformat(),
"feature_schema": self.features_used,
"labels": self.labels,
"hyperparameters": self.model.get_params() if self.model else {},
"performance_metrics": {"mlogloss": "computed_during_eval"},
"model_checksum": checksum,
"model_file": model_path.name
}
with open(meta_path, "w") as f:
json.dump(metadata, f)
self.version = version_id
logger.info(f"Classifier model {version_id} and metadata persisted to {self.model_dir}")
def load(self) -> bool:
meta_files = sorted(self.model_dir.glob("classifier_metadata_*.json"))
if not meta_files:
return False
latest_meta = meta_files[-1]
with open(latest_meta, "r") as f:
meta = json.load(f)
self.labels = meta.get("labels", self.labels)
self.version = meta.get("model_version", "1.0.0")
model_file = meta.get("model_file")
model_path = self.model_dir / model_file
if model_path.exists():
self.model = xgb.XGBClassifier()
self.model.load_model(model_path)
logger.info(f"Pre-trained XGBoost classifier {self.version} loaded successfully.")
return True
return False
def predict(self, features: pd.DataFrame) -> pd.DataFrame:
if self.model is None:
if not self.load():
raise RuntimeError("Classifier model is not loaded. Train or load first.")
logger.info("Executing XGBoost Attack Classification inference...")
# Prepare inference data
missing_cols = [c for c in self.features_used if c not in features.columns]
for c in missing_cols:
features[c] = 0
X = features[self.features_used].fillna(0)
probs = self.model.predict_proba(X)
preds = probs.argmax(axis=1)
confidences = probs.max(axis=1)
predicted_labels = [self.labels[p] if p < len(self.labels) else "Unknown" for p in preds]
return pd.DataFrame({
"event_id": features["event_id"],
"attack_category": predicted_labels,
"prediction_confidence": confidences,
"classification_model_version": self.version
})