guygrigsby commited on
Commit
4eca5f3
·
verified ·
1 Parent(s): 52615f3

Guardian dialect-fair hate classifier (ModernBERT-large)

Browse files
Files changed (6) hide show
  1. README.md +85 -0
  2. config.json +91 -0
  3. eval_result.json +12 -0
  4. model.safetensors +3 -0
  5. tokenizer.json +0 -0
  6. 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
+ }