Text Classification
Transformers
PyTorch
toxic_comment_xlmr
feature-extraction
toxicity
content-moderation
multilingual
multi-label
xlm-roberta
custom_code
Eval Results (legacy)
Instructions to use Deeptanshuu/mill-screen with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use Deeptanshuu/mill-screen with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-classification", model="Deeptanshuu/mill-screen", trust_remote_code=True)# pip install -U transformers accelerate # Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("Deeptanshuu/mill-screen", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
File size: 5,985 Bytes
7fb84fb 9bec9b1 7fb84fb 1e37e3c 7fb84fb | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | #!/usr/bin/env python3
"""Runnable example: batch toxicity prediction with Mill, using tuned per-class thresholds.
python inference_example.py # pull from the Hub
python inference_example.py /path/to/local/repo # use a local checkout
python inference_example.py --device cuda
Only needs `transformers`, `torch` and `huggingface_hub`. Nothing from the
training repo is imported; `modeling_toxic_xlmr.py` ships alongside this file
and is loaded for you by `trust_remote_code=True`.
The three things people get wrong with this model:
1. It needs `lang_ids`. Omit it and every row is scored as English (id 0),
silently and with no error.
2. The six labels are independent, not one choice out of six. Any number of
them can fire on the same comment.
3. 0.5 is the wrong cut. Use `thresholds.json`; the rare classes need cuts
near 0.37.
"""
import argparse
import json
import os
import sys
import torch
from transformers import AutoModel, AutoTokenizer
REPO_ID = "Deeptanshuu/mill-screen"
# Baked into the trained language embedding table. Do not renumber.
LANGUAGE_IDS = {"en": 0, "ru": 1, "tr": 2, "es": 3, "fr": 4, "it": 5, "pt": 6}
# Classifier output order. Index 1 is severe_toxic and index 2 is obscene,
# which is neither alphabetical nor the order most people would guess.
LABEL_NAMES = ["toxic", "severe_toxic", "obscene", "threat", "insult", "identity_hate"]
def load_thresholds(source: str) -> dict:
"""Read thresholds.json from a local directory or from the Hub."""
if os.path.isdir(source):
path = os.path.join(source, "thresholds.json")
else:
from huggingface_hub import hf_hub_download
path = hf_hub_download(repo_id=source, filename="thresholds.json")
with open(path) as f:
return json.load(f)["thresholds"]
@torch.no_grad()
def predict(model, tokenizer, texts, langs, thresholds, batch_size=8, max_length=512):
"""Score a list of texts and apply the per-class thresholds.
Args:
texts: list of strings.
langs: list of language codes, one per text, from LANGUAGE_IDS.
thresholds: {label_name: float}.
Returns:
list of dicts, one per input text.
"""
if len(texts) != len(langs):
raise ValueError(f"{len(texts)} texts but {len(langs)} language codes")
unknown = sorted({l for l in langs if l not in LANGUAGE_IDS})
if unknown:
raise ValueError(
f"Unsupported language codes {unknown}. This model knows "
f"{sorted(LANGUAGE_IDS)}. Pick the closest supported language rather "
"than letting it default silently."
)
device = next(model.parameters()).device
cut = torch.tensor([thresholds[name] for name in LABEL_NAMES], device=device)
results = []
for start in range(0, len(texts), batch_size):
chunk = texts[start : start + batch_size]
chunk_langs = langs[start : start + batch_size]
enc = tokenizer(
chunk,
padding=True,
truncation=True,
max_length=max_length, # must be 512; training used 512
return_tensors="pt",
).to(device)
lang_ids = torch.tensor(
[LANGUAGE_IDS[l] for l in chunk_langs], dtype=torch.long, device=device
)
out = model(
input_ids=enc["input_ids"],
attention_mask=enc["attention_mask"],
lang_ids=lang_ids,
)
probs = out["probabilities"]
fired = probs >= cut
for text, lang, prob_row, fired_row in zip(chunk, chunk_langs, probs, fired):
labels = [n for n, f in zip(LABEL_NAMES, fired_row.tolist()) if f]
results.append(
{
"text": text,
"language": lang,
"probabilities": {
n: round(float(p), 4) for n, p in zip(LABEL_NAMES, prob_row.tolist())
},
"labels": labels,
"any_toxic": bool(labels),
}
)
return results
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__.split("\n")[0])
parser.add_argument(
"source",
nargs="?",
default=REPO_ID,
help="Hub repo id or a local directory holding config.json and pytorch_model.bin",
)
parser.add_argument("--device", default="cuda" if torch.cuda.is_available() else "cpu")
parser.add_argument("--batch-size", type=int, default=8)
args = parser.parse_args()
print(f"loading {args.source} on {args.device}", file=sys.stderr)
model = AutoModel.from_pretrained(args.source, trust_remote_code=True)
model.to(args.device).eval()
# The tokenizer is stock xlm-roberta-large, unmodified. It ships in this
# repo so the whole thing is self-contained, but the line below works
# equally well against "xlm-roberta-large".
tokenizer = AutoTokenizer.from_pretrained(args.source)
thresholds = load_thresholds(args.source)
samples = [
("You are an absolute idiot and everyone here hates you.", "en"),
("Thanks for catching that typo, much appreciated.", "en"),
("Sei un cretino, vattene via da qui.", "it"),
("Merci beaucoup pour ton aide, c'etait tres clair.", "fr"),
("Defol git buradan, seni asagilik herif.", "tr"),
]
texts = [t for t, _ in samples]
langs = [l for _, l in samples]
for row in predict(model, tokenizer, texts, langs, thresholds, batch_size=args.batch_size):
verdict = ", ".join(row["labels"]) if row["labels"] else "clean"
print(f"\n[{row['language']}] {row['text']}")
print(f" fires: {verdict}")
print(
" probs: "
+ " ".join(f"{n}={row['probabilities'][n]:.3f}" for n in LABEL_NAMES)
)
print("\nthresholds used: " + json.dumps(thresholds))
return 0
if __name__ == "__main__":
raise SystemExit(main())
|