Spaces:
Running
Running
| """Linear SVM classifier - single-stage binary cyberbullying detection. | |
| Uses ``LinearSVC`` wrapped in ``CalibratedClassifierCV(method='sigmoid', cv=5)`` so | |
| that ``predict_proba`` returns proper probabilities (Platt scaling). RBF kernel | |
| is deferred - see README.md § Models. | |
| """ | |
| from __future__ import annotations | |
| import logging | |
| from pathlib import Path | |
| import joblib | |
| import numpy as np | |
| from sklearn.calibration import CalibratedClassifierCV | |
| from sklearn.svm import LinearSVC | |
| logger = logging.getLogger(__name__) | |
| class SVMModel: | |
| """Linear SVM with Platt-calibrated probabilities.""" | |
| # Target hyperparams to sweep during tuning: | |
| # C ∈ [0.1, 1, 10] (RBF + gamma sweep deferred) | |
| def __init__( | |
| self, | |
| C: float = 1.0, | |
| class_weight: str = "balanced", | |
| random_state: int = 42, | |
| max_iter: int = 10000, | |
| calibration_cv: int = 5, | |
| ) -> None: | |
| base = LinearSVC( | |
| C=C, | |
| class_weight=class_weight, | |
| random_state=random_state, | |
| max_iter=max_iter, | |
| ) | |
| self.model = CalibratedClassifierCV(base, method="sigmoid", cv=calibration_cv) | |
| def fit(self, X_train, y_train, X_val=None, y_val=None) -> None: | |
| """Fit on TF-IDF features. ``X_val``/``y_val`` accepted for API parity but unused.""" | |
| self.model.fit(X_train, y_train) | |
| logger.info("LinearSVC (calibrated) fitted on %d samples", X_train.shape[0]) | |
| def predict(self, X) -> np.ndarray: | |
| """Predict 0/1 labels.""" | |
| return self.model.predict(X) | |
| def predict_proba(self, X) -> np.ndarray: | |
| """Predict Platt-scaled class probabilities; shape ``(n, 2)``.""" | |
| return self.model.predict_proba(X) | |
| def save(self, path: Path) -> None: | |
| """Persist the fitted estimator to ``path`` via joblib.""" | |
| joblib.dump(self.model, str(path)) | |
| def load(cls, path: Path) -> "SVMModel": | |
| """Load a previously saved estimator and return a fresh wrapper.""" | |
| instance = cls.__new__(cls) | |
| instance.model = joblib.load(str(path)) | |
| return instance | |