Text Classification
Transformers
Safetensors
multilingual
snt_classifier
feature-extraction
news
topic-classification
multi-label
xlm-roberta
custom_code
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
| """SNT news classifier — HF-native wrapper (uploaded to the HF repo as-is). | |
| Usage: | |
| 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("Title. Body...", return_tensors="pt", truncation=True, max_length=512) | |
| labels = model.predict_labels(**enc) | |
| """ | |
| from __future__ import annotations | |
| import torch | |
| import torch.nn as nn | |
| from transformers import AutoConfig, AutoModel, PretrainedConfig, PreTrainedModel | |
| class SNTConfig(PretrainedConfig): | |
| model_type = "snt_classifier" | |
| def __init__( | |
| self, | |
| encoder_name: str = "xlm-roberta-large", | |
| l1_keys: list[str] | None = None, | |
| l2_keys: list[str] | None = None, | |
| l2_parent: dict[str, str] | None = None, | |
| l1_thresholds: dict[str, float] | None = None, | |
| l2_thresholds: dict[str, float] | None = None, | |
| snt_version: str = "v0.5.1", | |
| dropout: float = 0.1, | |
| **kwargs, | |
| ): | |
| self.encoder_name = encoder_name | |
| self.l1_keys = l1_keys or [] | |
| self.l2_keys = l2_keys or [] | |
| self.l2_parent = l2_parent or {} | |
| self.l1_thresholds = l1_thresholds or {} | |
| self.l2_thresholds = l2_thresholds or {} | |
| self.snt_version = snt_version | |
| self.dropout = dropout | |
| super().__init__(**kwargs) | |
| def n_l1(self) -> int: | |
| return len(self.l1_keys) | |
| def n_l2(self) -> int: | |
| return len(self.l2_keys) | |
| class SNTForNewsClassification(PreTrainedModel): | |
| config_class = SNTConfig | |
| def __init__(self, config: SNTConfig): | |
| super().__init__(config) | |
| # Attribute names MUST match DualHeadModel so state_dicts load 1:1. | |
| # from_config (not from_pretrained): weights come from this repo's | |
| # safetensors; from_pretrained breaks under HF's meta-device loading. | |
| self.encoder = AutoModel.from_config(AutoConfig.from_pretrained(config.encoder_name)) | |
| hidden = self.encoder.config.hidden_size | |
| self.dropout = nn.Dropout(config.dropout) | |
| self.head_top = nn.Linear(hidden, config.n_l1) | |
| self.head_sub = nn.Linear(hidden, config.n_l2) | |
| self.post_init() | |
| def forward(self, input_ids, attention_mask, **kwargs): | |
| out = self.encoder(input_ids=input_ids, attention_mask=attention_mask) | |
| pooled = self.dropout(out.last_hidden_state[:, 0, :]) | |
| return {"l1_logits": self.head_top(pooled), "l2_logits": self.head_sub(pooled)} | |
| def predict_labels(self, input_ids, attention_mask, **kwargs) -> list[dict]: | |
| """Thresholded multi-label prediction, one dict per batch row. | |
| Logits are upcast to fp32 before sigmoid — bf16 sigmoid saturates to | |
| exactly 1.0 above logit ~6.2, collapsing co-confident categories. | |
| """ | |
| out = self.forward(input_ids, attention_mask) | |
| l1_probs = torch.sigmoid(out["l1_logits"].float()) | |
| l2_probs = torch.sigmoid(out["l2_logits"].float()) | |
| results = [] | |
| for row in range(l1_probs.shape[0]): | |
| l1 = sorted( | |
| ( | |
| {"key": k, "p": round(float(p), 4)} | |
| for k, p in zip(self.config.l1_keys, l1_probs[row].tolist()) | |
| if p >= self.config.l1_thresholds.get(k, 0.5) | |
| ), | |
| key=lambda hit: -hit["p"], | |
| ) | |
| if not l1: # argmax fallback — never return unlabeled | |
| idx = int(l1_probs[row].argmax()) | |
| l1 = [{"key": self.config.l1_keys[idx], "p": round(float(l1_probs[row][idx]), 4)}] | |
| l2 = sorted( | |
| ( | |
| {"key": k, "p": round(float(p), 4)} | |
| for k, p in zip(self.config.l2_keys, l2_probs[row].tolist()) | |
| if p >= self.config.l2_thresholds.get(k, 0.5) | |
| ), | |
| key=lambda hit: -hit["p"], | |
| ) | |
| results.append({"l1": l1, "primary_l1": l1[0]["key"], "l2": l2}) | |
| return results | |