Skip to content

FEAT: add ViolenceClassifierScorer, a CPU violence classifier with an abstain band - #2626

Open
WatchTree-19 wants to merge 1 commit into
microsoft:mainfrom
WatchTree-19:violence-classifier-scorer
Open

WatchTree-19 wants to merge 1 commit into
microsoft:mainfrom
WatchTree-19:violence-classifier-scorer

Conversation

@WatchTree-19

Copy link
Copy Markdown
Contributor

Description

Adds a scorer for the violence harm category that runs a small CPU classifier instead of an LLM judge, following the direction discussed with Roman Lutz (@romanlutz): traditional ML scoring with the classifier carrying the bulk and an LLM judge taking the uncertain tail.

The scorer embeds the objective/response pair with a frozen bge-small-en-v1.5 encoder (pinned revision, same pattern as RobloxPiiScorer) and applies a single-hidden-layer MLP trained from PyRIT's own human-labeled violence datasets in pyrit/datasets/scorer_evals/harm. The head trains on first use in seconds, so no model binary ships in the PR and the whole model reproduces from the repository. The dataset bytes are SHA-256 pinned: if the gold set changes, the scorer refuses to train rather than silently returning numbers validated against different data.

Probabilities are temperature-calibrated on out-of-fold predictions. Scores inside the abstain band (default 0.3 to 0.7) are returned with ScoreStatus.UNDETERMINED so callers can route them to an LLM judge. Out-of-fold cross-validation on the training rows: AUC 0.83 to 0.86 across seeds, ECE about 0.04 after calibration, and with the default band about 0.70 coverage at 0.87 accuracy versus 0.76 at full coverage. Through HarmScorerEvaluator out of sample (trained on the multi-score rows only, evaluated on the disjoint violence.csv), MAE is 0.235 against 0.244 for AzureContentFilterScorer on the same 96 rows; the gpt-4o Likert judge is at 0.161, partly because this scorer outputs probability of harmful rather than a severity estimate.

On operating points: the score is a calibrated probability, so callers can tune for their own FNR/FPR preference with FloatScaleThresholdScorer or the abstain band. From out-of-fold cross-validation: threshold 0.50 gives FNR 0.27 at FPR 0.21, 0.30 gives 0.11 at 0.36, 0.20 gives 0.05 at 0.53. For a low-false-negative configuration, lowering the abstain band's bottom edge (e.g. 0.15 to 0.7) sends borderline responses to the judge rather than marking them safe.

The scorer is restricted to violence deliberately. On these same gold sets, small classifiers are near chance for several other harm categories, so a general head would return confident numbers it cannot support. Extending to other categories, and a composite scorer that automatically routes abstentions to a judge, are natural follow-ups where the data supports them.

No new dependencies; torch and transformers come from the existing huggingface extra.

Tests and Documentation

15 unit tests: score construction, abstain behaviour, determinism of the train step, the dataset-hash guard (including that it passes when re-pinned, so the guard is shown to discriminate), and validator config. A section is added to doc/code/scoring/2_float_scale_scorers.py and the paired notebook (cells added without outputs; happy to regenerate if preferred).

Written with AI assistance.

… abstain band

Scores the violence harm category with a frozen bge-small-en-v1.5 encoder
and a small MLP head trained from PyRIT's own human-labeled violence
datasets, instead of an LLM judge. The head trains on first use from the
in-package CSVs, whose bytes are hash-pinned so a changed dataset fails
loudly rather than scoring with an unvalidated model. Probabilities are
temperature-calibrated on out-of-fold predictions; scores inside the
abstain band are returned as UNDETERMINED so callers can route the
uncertain tail to an LLM judge.

Out-of-fold cross-validation on the training rows: AUC 0.83-0.86 across
seeds, ECE about 0.04 after calibration, and with the default band about
0.70 coverage at 0.87 accuracy versus 0.76 at full coverage.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MFydrdwX6iPvUuGiqKDybJ
@WatchTree-19 WatchTree-19 changed the title FEAT: add ViolenceClassifierScorer, a CPU violence classifier with an… FEAT: add ViolenceClassifierScorer, a CPU violence classifier with an abstain band Sep 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant