| """Classifiers under evaluation. |
| |
| ToyClassifier -- a deliberately brittle keyword model used by the offline |
| smoke test. It fails the way real lexical models fail: it |
| has a language blind spot (it only knows English toxic |
| tokens) and it shatters under character-level evasions. That |
| makes the smoke run produce honest, real-looking findings |
| with zero network access. |
| |
| DetoxifyModel -- thin wrapper over the `detoxify` library (unbiased |
| checkpoint, trained on Civil Comments) for real runs. Lazily |
| imported so the smoke path never needs it installed. |
| """ |
| from __future__ import annotations |
|
|
| from typing import List |
|
|
| import numpy as np |
|
|
| |
| TOXIC_WORDS_EN = {"idiot", "stupid", "loser", "worthless", "hate", "trash", "pathetic"} |
|
|
|
|
| class ToyClassifier: |
| """Surface-token keyword scorer. Score 0.9 if any known toxic token is |
| present (post simple lowercase/split), else 0.1, with light noise so PR |
| curves are non-degenerate. Knows English tokens only -> language blind spot. |
| """ |
|
|
| def __init__(self, vocab=None, seed: int = 0): |
| self.vocab = set(vocab) if vocab else set(TOXIC_WORDS_EN) |
| self._rng = np.random.default_rng(seed) |
|
|
| def predict_proba(self, texts: List[str]) -> np.ndarray: |
| scores = [] |
| for t in texts: |
| toks = t.lower().split() |
| hit = any(tok.strip(".,!?") in self.vocab for tok in toks) |
| base = 0.9 if hit else 0.1 |
| scores.append(np.clip(base + self._rng.normal(0, 0.03), 0, 1)) |
| return np.array(scores) |
|
|
|
|
| class DetoxifyModel: |
| """Wrapper over detoxify for real datasets. Requires `pip install detoxify`.""" |
|
|
| def __init__(self, variant: str = "unbiased", batch_size: int = 32): |
| from detoxify import Detoxify |
| self.model = Detoxify(variant) |
| self.batch_size = batch_size |
|
|
| def predict_proba(self, texts: List[str]) -> np.ndarray: |
| |
| |
| |
| texts = list(texts) |
| scores: List[float] = [] |
| for i in range(0, len(texts), self.batch_size): |
| chunk = texts[i:i + self.batch_size] |
| preds = self.model.predict(chunk) |
| scores.extend(preds["toxicity"]) |
| return np.array(scores) |
|
|