maxafanasiev's picture
Document archive download
f58ecf3 verified
|
Raw History Blame Contribute Delete
5.06 kB
---
license: other
base_model: intfloat/multilingual-e5-base
pipeline_tag: text-classification
tags: [multi-label, news, multilingual]
---
# News topic classifier (e5-base)
Fine-tuned `intfloat/multilingual-e5-base` for multi-label classification of news articles into 13 topics:
Business And Finance, Market And Exchange, Demographics, Scientific Exploration, Labour Dispute, Disaster,
Conflicts And War, Politics, Crime, Theft, Assault, Corporate Crime, Fraud. No label means "Other".
## Usage
- Input: `"passage: " + title + "\n" + content`, truncated to 512 tokens.
- Output: sigmoid per category, compared with the per-category threshold in `thresholds.json` (not 0.5).
- If Theft or Assault is predicted, add Crime as well.
```python
import json
import torch
from huggingface_hub import hf_hub_download
from transformers import AutoModelForSequenceClassification, AutoTokenizer
REPO = "maxafanasiev/news-topic-classifier-e5-base" # private: run `hf auth login` first
PREFIX = "passage: "
PARENTS = {"Theft": "Crime", "Assault": "Crime"}
tokenizer = AutoTokenizer.from_pretrained(REPO)
model = AutoModelForSequenceClassification.from_pretrained(REPO).eval()
thresholds = json.load(open(hf_hub_download(REPO, "thresholds.json")))
categories = list(thresholds) # same order as the model outputs
@torch.no_grad()
def classify(articles: list[dict], max_len: int = 512) -> list[list[str]]:
texts = [f"{PREFIX}{a['title']}\n{a['content']}" for a in articles]
batch = tokenizer(texts, truncation=True, max_length=max_len, padding=True, return_tensors="pt")
probs = torch.sigmoid(model(**batch).logits)
results = []
for row in probs:
labels = [c for c, p in zip(categories, row.tolist()) if p >= thresholds[c]]
labels += [PARENTS[c] for c in labels if c in PARENTS and PARENTS[c] not in labels]
results.append(labels or ["Other"])
return results
print(classify([
{"title": "Police arrest two men over jewellery store robbery", "content": "Officers detained the suspects after ..."},
{"title": "El banco central sube los tipos de interés", "content": "La decisión responde a la inflación ..."},
]))
# e.g. [['Crime', 'Theft'], ['Business And Finance']]
```
Classify in batches of 16–32 articles; `max_len=256` is about twice as fast at a small quality cost (see Results).
## Download once, use without Hugging Face
1. Download the files (needs access to this private repo once), with any of:
- CLI: `hf auth login`, then `hf download maxafanasiev/news-topic-classifier-e5-base --local-dir ./news-topic-classifier-e5-base`
- git: `git lfs install && git clone https://huggingface.co/maxafanasiev/news-topic-classifier-e5-base` (use a token as the password)
- browser: the "Files and versions" tab, download every file into one folder
- one archive: download `news-topic-classifier-e5-base.tar.gz` from "Files and versions" (or `hf download maxafanasiev/news-topic-classifier-e5-base news-topic-classifier-e5-base.tar.gz --local-dir .`), then `tar xzf news-topic-classifier-e5-base.tar.gz`; it unpacks into `./news-topic-classifier-e5-base/`, ready for the code below. Windows 10+ has `tar` built in.
2. Keep these files together: `model.safetensors`, `config.json`, `tokenizer.json`, `tokenizer_config.json`, `thresholds.json`.
3. Load from the folder; `HF_HUB_OFFLINE=1` makes sure nothing is fetched from the internet:
```python
import json
import os
from pathlib import Path
os.environ["HF_HUB_OFFLINE"] = "1"
import torch
from transformers import AutoModelForSequenceClassification, AutoTokenizer
MODEL_DIR = Path("./news-topic-classifier-e5-base")
PREFIX = "passage: "
PARENTS = {"Theft": "Crime", "Assault": "Crime"}
tokenizer = AutoTokenizer.from_pretrained(MODEL_DIR)
model = AutoModelForSequenceClassification.from_pretrained(MODEL_DIR).eval()
thresholds = json.loads((MODEL_DIR / "thresholds.json").read_text())
categories = list(thresholds)
@torch.no_grad()
def classify(articles: list[dict], max_len: int = 512) -> list[list[str]]:
texts = [f"{PREFIX}{a['title']}\n{a['content']}" for a in articles]
batch = tokenizer(texts, truncation=True, max_length=max_len, padding=True, return_tensors="pt")
probs = torch.sigmoid(model(**batch).logits)
results = []
for row in probs:
labels = [c for c, p in zip(categories, row.tolist()) if p >= thresholds[c]]
labels += [PARENTS[c] for c in labels if c in PARENTS and PARENTS[c] not in labels]
results.append(labels or ["Other"])
return results
```
Requirements: `torch` and `transformers` (no `huggingface_hub` login needed at this point).
## Results (gold set, CPU Apple M4 Pro, fp32)
| Max tokens | micro-F1 (random gold, 163) | exact set match | CPU latency |
| --- | --- | --- | --- |
| 512 | 0.860 | 0.804 | 42 ms/article |
| 256 | 0.836 | 0.785 | 20 ms/article |
Training data: ~8k news articles labeled by an LLM teacher (Sonnet 5.5); gold built from three blind LLM labelers plus a judge, not hand-checked. Training data is private and not included.