Instructions to use sweenk/snt-classifier with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use sweenk/snt-classifier with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-classification", model="sweenk/snt-classifier", trust_remote_code=True)# Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("sweenk/snt-classifier", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
license: mit
base_model: FacebookAI/xlm-roberta-large
pipeline_tag: text-classification
language:
- multilingual
tags:
- news
- topic-classification
- multi-label
- xlm-roberta
library_name: transformers
SNT News Classifier v0.6
Multi-label news topic classifier: 12 top-level (L1) and 71 sub-level (L2)
categories, built on xlm-roberta-large with two independent sigmoid heads. Both levels are
genuinely multi-label — an article about a trade deal can be world + money_and_business +
politics at the same time. Per-class decision thresholds (tuned on a held-out validation split)
ship inside config.json; predict_labels() applies them and falls back to argmax so no article
is ever left unlabeled.
Built by Sweenk to categorize its news feed; released so others can use and scrutinize it.
Links: GitHub — model & training code · GitHub — data pipeline
At a glance
- Multi-label at both levels — a story can be
world+money_and_business+politicsat once, each above its own per-class tuned threshold. - 0.847 L1 macro-F1 on 26,412 held-out articles; all 12 top-level categories clear a 0.65 per-class floor.
- Multilingual encoder —
xlm-roberta-large(100 languages), fine-tuned on ~264K news articles. - Honest about its labels — the gold is LLM-teacher-generated, not human-annotated, and the evaluation section says exactly what that means.
Quick start
from transformers import AutoModel, AutoTokenizer
model = AutoModel.from_pretrained("sweenk/snt-classifier", trust_remote_code=True)
tok = AutoTokenizer.from_pretrained("sweenk/snt-classifier")
enc = tok("OpenAI raises $6.6B. The startup announced its latest funding round...",
return_tensors="pt", truncation=True, max_length=512)
print(model.predict_labels(**enc))
# [{'l1': [{'key': 'money_and_business', 'p': 0.99}, {'key': 'tech_and_ai', 'p': 0.98}],
# 'primary_l1': 'money_and_business',
# 'l2': [{'key': 'companies_and_industries', 'p': 0.92}, ...]}]
Input convention: "{title}\n\n{body}", truncated at 512 tokens. The classifier was trained
on title+body; titles alone work but body text improves routing (the training prompt explicitly
prioritizes body over headline).
Taxonomy — 12 L1 / 71 L2
| L1 category | # L2 | L2 sub-categories |
|---|---|---|
sports |
12 | american_football, baseball, basketball, college_sports, combat_sports, golf, hockey, motorsports, olympics, other_sports, soccer, tennis |
politics |
6 | elections_and_campaigns, government_and_policy, immigration_and_borders, political_figures_and_scandals, social_issues_and_activism, state_and_local_politics |
world |
4 | geopolitics_and_diplomacy, humanitarian_crises, terrorism_and_security, war_and_conflict |
entertainment_and_pop_culture |
7 | books_and_arts, celebrities_and_gossip, gaming, internet_culture_and_creators, media_and_journalism, movies_and_tv, music |
money_and_business |
8 | companies_and_industries, cost_of_living, crypto_and_fintech, housing_and_real_estate, macro_economy_and_rates, markets_and_investing, personal_finance, work_and_careers |
crime_and_justice |
4 | courts_and_trials, crime_and_policing, scams_and_fraud, true_crime |
tech_and_ai |
5 | artificial_intelligence, big_tech_and_startups, cybersecurity_and_privacy, gadgets_and_apps, screen_time_and_digital_life |
science_and_space |
4 | archaeology_and_history, psychology_and_behavior, scientific_discoveries, space_and_astronomy |
health_and_wellness |
5 | fitness_and_exercise, medical_and_public_health, mental_health, nutrition_and_diet, sleep_and_longevity |
lifestyle |
8 | education_and_schools, faith_and_spirituality, fashion_and_beauty, food_and_drink, home_and_garden, parenting_and_family, relationships_and_dating, travel_and_places |
weather_and_environment |
5 | climate_change, disasters_and_accidents, energy_and_climate_solutions, nature_and_wildlife, severe_weather |
human_stories |
3 | animals_and_pets, good_news_and_kindness, offbeat_and_unusual |
The full machine-readable taxonomy (l1_keys, l2_keys, l2_parent, per-class thresholds) is in
config.json.
Evaluation — our own numbers, stated plainly
Held-out test split: 26,412 articles (a 10% slice of the labeled corpus, stratified by primary L1). "Tuned" = per-class thresholds optimized on the validation split, then applied unchanged to test.
| Metric | @0.5 threshold | tuned thresholds |
|---|---|---|
| L1 macro F1 | 0.817 | 0.847 |
| L1 micro F1 | 0.831 | 0.857 |
| L1 primary accuracy (argmax) | 0.833 | — |
| L2 macro F1 | 0.647 | 0.685 |
| L2 micro F1 | 0.753 | 0.754 |
Per-class L1 F1 (test)
| L1 | F1 @0.5 | F1 tuned | threshold |
|---|---|---|---|
sports |
0.941 | 0.954 | 0.850 |
politics |
0.845 | 0.859 | 0.750 |
world |
0.783 | 0.827 | 0.850 |
entertainment_and_pop_culture |
0.887 | 0.898 | 0.850 |
money_and_business |
0.786 | 0.825 | 0.950 |
crime_and_justice |
0.823 | 0.857 | 0.950 |
tech_and_ai |
0.802 | 0.857 | 0.950 |
science_and_space |
0.802 | 0.833 | 0.950 |
health_and_wellness |
0.821 | 0.860 | 0.900 |
lifestyle |
0.865 | 0.867 | 0.700 |
weather_and_environment |
0.816 | 0.857 | 0.950 |
human_stories |
0.632 | 0.671 | 0.900 |
What you should know before trusting these numbers
Read this section — it is the honest part.
- The gold labels are model-assisted, not human-annotated. The corpus (~278K articles from HuffPost archives, CommonCrawl News, daily.dev, and Sweenk production) was labeled by a mechanical migration from an earlier taxonomy plus multiple passes of a Claude Sonnet teacher with a rule-based prompt, spot-audited by humans (QA gates at 70–87% agreement on sampled batches). Test F1 therefore measures agreement with an LLM teacher, not with human ground truth.
- Class imbalance is real (~21x).
politics/lifestyle/entertainmenthave ~44-47K training rows;science_and_space~2.2K andtech_and_ai~3.7K. Training compensates with per-classpos_weight(clamped at 10) and caps the majority classes at 25K primary-label train rows so they don't swamp the rare ones. The weakest class here ishuman_stories(F1 0.671) — the fuzziest category by construction. - Labels were corrected over time, so F1 is not comparable across releases. v0.5.1 re-labeled ~1,500 systematically mislabeled rows with route-by-cause rules (accidents by cause, terror → crime, pharma earnings → money); on those rows agreement with the corrected gold went 25.9% → 75.3%. Because the gold labels themselves changed between releases, aggregate F1 deltas are not like-for-like — treat each release's numbers as self-referential.
- Multilingual ability is inherited, not measured. The encoder is XLM-R, but nearly all training articles are English. Expect degraded (unquantified) quality on non-English news.
- L3 (named topics / entities) is not part of this model — Sweenk handles that downstream with a separate extraction step.
Architecture
xlm-roberta-large encoder → CLS pooling → dropout(0.1) → two parallel linear heads
(L1: 12 logits, L2: 71 logits), both sigmoid. Trained 3 epochs, BCE loss with
per-class pos_weight (L1) and loss weights L1:1.0 / L2:2.0, bf16 autocast, gradient checkpointing.
Inference upcasts logits to fp32 before sigmoid (bf16 sigmoid saturates above logit ~6.2, which
collapses co-confident multi-label pairs).
Versions
| Version | What changed |
|---|---|
| v0.5 | First multi-label release (12 L1 / 71 L2, dual sigmoid heads) |
| v0.5.1 | Corrective retrain: route-accidents-by-cause, terror→crime, earnings→money, govt-personnel→politics, wildlife→weather, body-over-headline; ~1,500 corrected labels; re-tuned thresholds |
| v0.6 | Retrain on the corrected corpus + ~23K newly teacher-labeled articles (Sweenk production + daily.dev science/tech); majority-class capping (25K/class) on top of pos_weight; re-tuned thresholds |
License & attribution
Model weights: MIT. Base model: FacebookAI/xlm-roberta-large (MIT). The training corpus contains article text from public news sources and is not redistributed with this model.