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
Download inference_example.py from Deeptanshuu/mill-screen: direct link, hf CLI and curl.
- Browser
- Download file 5.99 kB
-
https://huggingface.co/Deeptanshuu/mill-screen/resolve/main/inference_example.py
- Command line
-
hf download hf://Deeptanshuu/mill-screen/inference_example.py
-
curl -L -o inference_example.py https://huggingface.co/Deeptanshuu/mill-screen/resolve/main/inference_example.py
5.99 kB
| #!/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"] | |
| 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()) | |