Guardian dialect-fair hate classifier (ModernBERT-large)
Browse files- README.md +85 -0
- config.json +91 -0
- eval_result.json +12 -0
- model.safetensors +3 -0
- tokenizer.json +0 -0
- tokenizer_config.json +21 -0
README.md
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: mit
|
| 3 |
+
language:
|
| 4 |
+
- en
|
| 5 |
+
base_model: answerdotai/ModernBERT-large
|
| 6 |
+
pipeline_tag: text-classification
|
| 7 |
+
tags:
|
| 8 |
+
- hate-speech-detection
|
| 9 |
+
- content-moderation
|
| 10 |
+
- fairness
|
| 11 |
+
- bias-mitigation
|
| 12 |
+
- aae
|
| 13 |
+
- dialect
|
| 14 |
+
metrics:
|
| 15 |
+
- recall
|
| 16 |
+
---
|
| 17 |
+
|
| 18 |
+
# Guardian — Dialect-Fair Hate Classifier (ModernBERT-large)
|
| 19 |
+
|
| 20 |
+
A binary hate-speech classifier fine-tuned from [ModernBERT-large](https://huggingface.co/answerdotai/ModernBERT-large), explicitly debiased against **African-American English (AAE) false positives**.
|
| 21 |
+
|
| 22 |
+
Off-the-shelf toxicity/hate classifiers flag benign AAE text as hateful at **2x+** the rate of benign General-American English (Sap et al. 2019). Guardian targets and measures that gap directly: it catches **93% of hate** while keeping the benign-AAE false-positive rate within **~0.04** of the benign-GAE rate.
|
| 23 |
+
|
| 24 |
+
Author: **Guy Grigsby** (Aeryx-ai). License: MIT.
|
| 25 |
+
|
| 26 |
+
## Labels
|
| 27 |
+
|
| 28 |
+
`0 = not hate`, `1 = hate`. Output the softmax probability of class 1 and threshold it. The policy is **targeted hate** (slur-as-attack, protected-group harassment) — profanity, insults, dark themes, sexual content, and political opinion are deliberately **not** flagged.
|
| 29 |
+
|
| 30 |
+
## Operating frontier
|
| 31 |
+
|
| 32 |
+
Evaluated on HateCheck (recall, FP) and a **held-out dialect-balanced benign set** (FP on high-AAE vs low-AAE text, dialect measured with [TwitterAAE](https://github.com/slanglab/twitteraae)). Pick a threshold from the curve:
|
| 33 |
+
|
| 34 |
+
| threshold | recall | HateCheck FP | high-AAE benign FP | low-AAE benign FP | dialect FP gap |
|
| 35 |
+
|---|---|---|---|---|---|
|
| 36 |
+
| 0.50 | 0.934 | 0.013 | 0.082 | 0.040 | **0.042** |
|
| 37 |
+
| 0.80 | 0.906 | 0.007 | 0.055 | 0.025 | **0.030** |
|
| 38 |
+
| 0.90 | 0.872 | 0.006 | 0.041 | 0.015 | **0.027** |
|
| 39 |
+
|
| 40 |
+
The dialect FP gap is near-parity across the whole frontier (raw bias is ~5x lower than the ModernBERT-large baseline's 0.21 gap). Default to **0.50** for max recall; raise toward **0.80** for extra parity margin (e.g. corpus filtering).
|
| 41 |
+
|
| 42 |
+
## Usage
|
| 43 |
+
|
| 44 |
+
```python
|
| 45 |
+
import torch
|
| 46 |
+
from transformers import AutoModelForSequenceClassification, AutoTokenizer
|
| 47 |
+
|
| 48 |
+
tok = AutoTokenizer.from_pretrained("Aeryx-ai/guardian-dialect-fair-hate")
|
| 49 |
+
model = AutoModelForSequenceClassification.from_pretrained("Aeryx-ai/guardian-dialect-fair-hate").eval()
|
| 50 |
+
|
| 51 |
+
def hate_prob(text):
|
| 52 |
+
enc = tok(text, return_tensors="pt", truncation=True, max_length=256)
|
| 53 |
+
with torch.no_grad():
|
| 54 |
+
return torch.softmax(model(**enc).logits, -1)[0, 1].item()
|
| 55 |
+
|
| 56 |
+
is_hate = hate_prob("...") >= 0.5
|
| 57 |
+
```
|
| 58 |
+
|
| 59 |
+
## How it was debiased
|
| 60 |
+
|
| 61 |
+
1. **Bias-aware training sources** — Measuring Hate Speech (UC Berkeley), ToxiGen, DynaHate, Civil Comments (using `identity_attack` to separate group-hate from mere toxicity). The canonically AAE-biased Davidson/Founta sets are **excluded** as label sources.
|
| 62 |
+
2. **High-AAE-safe augmentation** — ~14k benign high-AAE examples (mined from social text, TwitterAAE-filtered) so the model cannot use dialect as a hate cue.
|
| 63 |
+
3. **FP-parity gate** — release was conditioned on benign high-AAE FP ≈ benign low-AAE FP on a held-out set (not just overall F1). The eval set is held out from the augmentation distribution.
|
| 64 |
+
|
| 65 |
+
Loss reweighting and DANN-style adversarial debiasing were tried; **data volume of high-AAE-safe text** was what actually closed the gap.
|
| 66 |
+
|
| 67 |
+
## Limitations
|
| 68 |
+
|
| 69 |
+
- **English only.** TwitterAAE is a Twitter-domain distant-supervision proxy for dialect, not ground truth, and is noisier on long-form/other-domain text.
|
| 70 |
+
- **Recall/parity tradeoff** is explicit in the frontier — higher thresholds trade recall for margin.
|
| 71 |
+
- **Not for high-stakes automated decisions** about individuals. Intended for content moderation triage and training-corpus filtering, with humans in the loop.
|
| 72 |
+
- Trained on a "targeted hate" policy; it will not flag profanity or controversy by design.
|
| 73 |
+
|
| 74 |
+
## Citation
|
| 75 |
+
|
| 76 |
+
```
|
| 77 |
+
@misc{grigsby2026guardian,
|
| 78 |
+
title = {Guardian: A Dialect-Fair Hate Classifier},
|
| 79 |
+
author = {Grigsby, Guy},
|
| 80 |
+
year = {2026},
|
| 81 |
+
howpublished = {\url{https://huggingface.co/Aeryx-ai/guardian-dialect-fair-hate}}
|
| 82 |
+
}
|
| 83 |
+
```
|
| 84 |
+
|
| 85 |
+
References: Sap et al. 2019 (Risk of Racial Bias in Hate Speech Detection); Blodgett et al. 2016 (TwitterAAE); Kennedy et al. (Measuring Hate Speech); Hartvigsen et al. (ToxiGen); Vidgen et al. 2021 (DynaHate).
|
config.json
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"architectures": [
|
| 3 |
+
"ModernBertForSequenceClassification"
|
| 4 |
+
],
|
| 5 |
+
"attention_bias": false,
|
| 6 |
+
"attention_dropout": 0.0,
|
| 7 |
+
"bos_token_id": 50281,
|
| 8 |
+
"classifier_activation": "gelu",
|
| 9 |
+
"classifier_bias": false,
|
| 10 |
+
"classifier_dropout": 0.0,
|
| 11 |
+
"classifier_pooling": "mean",
|
| 12 |
+
"cls_token_id": 50281,
|
| 13 |
+
"decoder_bias": true,
|
| 14 |
+
"deterministic_flash_attn": false,
|
| 15 |
+
"dtype": "float32",
|
| 16 |
+
"embedding_dropout": 0.0,
|
| 17 |
+
"eos_token_id": 50282,
|
| 18 |
+
"global_attn_every_n_layers": 3,
|
| 19 |
+
"gradient_checkpointing": false,
|
| 20 |
+
"hidden_activation": "gelu",
|
| 21 |
+
"hidden_size": 1024,
|
| 22 |
+
"id2label": {
|
| 23 |
+
"0": "not_hate",
|
| 24 |
+
"1": "hate"
|
| 25 |
+
},
|
| 26 |
+
"initializer_cutoff_factor": 2.0,
|
| 27 |
+
"initializer_range": 0.02,
|
| 28 |
+
"intermediate_size": 2624,
|
| 29 |
+
"label2id": {
|
| 30 |
+
"hate": 1,
|
| 31 |
+
"not_hate": 0
|
| 32 |
+
},
|
| 33 |
+
"layer_norm_eps": 1e-05,
|
| 34 |
+
"layer_types": [
|
| 35 |
+
"full_attention",
|
| 36 |
+
"sliding_attention",
|
| 37 |
+
"sliding_attention",
|
| 38 |
+
"full_attention",
|
| 39 |
+
"sliding_attention",
|
| 40 |
+
"sliding_attention",
|
| 41 |
+
"full_attention",
|
| 42 |
+
"sliding_attention",
|
| 43 |
+
"sliding_attention",
|
| 44 |
+
"full_attention",
|
| 45 |
+
"sliding_attention",
|
| 46 |
+
"sliding_attention",
|
| 47 |
+
"full_attention",
|
| 48 |
+
"sliding_attention",
|
| 49 |
+
"sliding_attention",
|
| 50 |
+
"full_attention",
|
| 51 |
+
"sliding_attention",
|
| 52 |
+
"sliding_attention",
|
| 53 |
+
"full_attention",
|
| 54 |
+
"sliding_attention",
|
| 55 |
+
"sliding_attention",
|
| 56 |
+
"full_attention",
|
| 57 |
+
"sliding_attention",
|
| 58 |
+
"sliding_attention",
|
| 59 |
+
"full_attention",
|
| 60 |
+
"sliding_attention",
|
| 61 |
+
"sliding_attention",
|
| 62 |
+
"full_attention"
|
| 63 |
+
],
|
| 64 |
+
"local_attention": 128,
|
| 65 |
+
"max_position_embeddings": 8192,
|
| 66 |
+
"mlp_bias": false,
|
| 67 |
+
"mlp_dropout": 0.0,
|
| 68 |
+
"model_type": "modernbert",
|
| 69 |
+
"norm_bias": false,
|
| 70 |
+
"norm_eps": 1e-05,
|
| 71 |
+
"num_attention_heads": 16,
|
| 72 |
+
"num_hidden_layers": 28,
|
| 73 |
+
"pad_token_id": 50283,
|
| 74 |
+
"position_embedding_type": "absolute",
|
| 75 |
+
"rope_parameters": {
|
| 76 |
+
"full_attention": {
|
| 77 |
+
"rope_theta": 160000.0,
|
| 78 |
+
"rope_type": "default"
|
| 79 |
+
},
|
| 80 |
+
"sliding_attention": {
|
| 81 |
+
"rope_theta": 10000.0,
|
| 82 |
+
"rope_type": "default"
|
| 83 |
+
}
|
| 84 |
+
},
|
| 85 |
+
"sep_token_id": 50282,
|
| 86 |
+
"sparse_pred_ignore_index": -100,
|
| 87 |
+
"sparse_prediction": false,
|
| 88 |
+
"tie_word_embeddings": true,
|
| 89 |
+
"transformers_version": "5.9.0",
|
| 90 |
+
"vocab_size": 50368
|
| 91 |
+
}
|
eval_result.json
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"model": "runs/large_v3",
|
| 3 |
+
"threshold": 0.5,
|
| 4 |
+
"hatecheck_recall": 0.9345,
|
| 5 |
+
"hatecheck_fp": 0.0129,
|
| 6 |
+
"dialect_fp_high_aae": 0.082,
|
| 7 |
+
"dialect_fp_low_aae": 0.04,
|
| 8 |
+
"dialect_fp_gap": 0.042,
|
| 9 |
+
"n_high": 1500,
|
| 10 |
+
"n_low": 1500,
|
| 11 |
+
"GATE_PASSED": true
|
| 12 |
+
}
|
model.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:8941ce68af1092f1a0a1448db4824641f4b9c5debf2580c4000370306645bb5e
|
| 3 |
+
size 1583351632
|
tokenizer.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
tokenizer_config.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"backend": "tokenizers",
|
| 3 |
+
"clean_up_tokenization_spaces": true,
|
| 4 |
+
"cls_token": "[CLS]",
|
| 5 |
+
"is_local": true,
|
| 6 |
+
"local_files_only": false,
|
| 7 |
+
"mask_token": "[MASK]",
|
| 8 |
+
"max_length": 256,
|
| 9 |
+
"model_input_names": [
|
| 10 |
+
"input_ids",
|
| 11 |
+
"attention_mask"
|
| 12 |
+
],
|
| 13 |
+
"model_max_length": 8192,
|
| 14 |
+
"pad_token": "[PAD]",
|
| 15 |
+
"sep_token": "[SEP]",
|
| 16 |
+
"stride": 0,
|
| 17 |
+
"tokenizer_class": "TokenizersBackend",
|
| 18 |
+
"truncation_side": "right",
|
| 19 |
+
"truncation_strategy": "longest_first",
|
| 20 |
+
"unk_token": "[UNK]"
|
| 21 |
+
}
|