AtesiT's picture
Upload README.md with huggingface_hub
0fbf965 verified
|
Raw
History Blame Contribute Delete
5.31 kB
---
language:
- ru
license: mit
tags:
- text-classification
- toxicity
- multi-label-classification
- multi-task-learning
- russian
- bert
base_model: cointegrated/rubert-tiny2
---
# RU Multi-Task Toxicity Encoder
Лёгкая multi-task модель для классификации токсичности русскоязычных текстов
по трём независимым категориям одновременно.
## Описание
Модель построена на базе [cointegrated/rubert-tiny2](https://huggingface.co/cointegrated/rubert-tiny2)
с тремя независимыми классификационными головами поверх эмбеддинга `[CLS]` токена.
Каждая голова предсказывает один бинарный класс:
- **profanity** — ненормативная лексика
- **threat** — угрозы в адрес пользователя
- **illegal** — запросы, связанные с нарушением закона
## Архитектура
```
Encoder (rubert-tiny2)
└── [CLS] embedding (312-dim)
├── Dropout (p=0.2)
├── Linear(312 → 1) → profanity_logit
├── Linear(312 → 1) → threat_logit
└── Linear(312 → 1) → illegal_logit
```
## Метрики (валидационная выборка)
| Класс | Порог | Precision | Recall | F1 |
|------------|----------|-------------|---------|---------|
| profanity | 0.60 | 0.9454 | 0.9256 | 0.9354 |
| threat | 0.75 | 1.0000 | 0.9286 | 0.9630 |
| illegal | 0.16 | 1.0000 | 1.0000 | 1.0000 |
> Пороги подобраны индивидуально для каждого класса по максимуму F1-score
> на валидационной выборке. Классы сильно несбалансированы — это учтено
> через `pos_weight` в `BCEWithLogitsLoss` при обучении.
## Пример использования
```python
import json
import torch
import torch.nn as nn
from transformers import AutoModel, AutoTokenizer
# 1. Загрузка токенизатора и конфига
tokenizer = AutoTokenizer.from_pretrained("AtesiT/ru-multitask-toxicity-encoder")
with open("toxicity_config.json") as f:
config = json.load(f)
# 2. Определение архитектуры
class MultiTaskToxicityEncoder(nn.Module):
def __init__(self, model_name, hidden_size, dropout=0.2):
super().__init__()
self.encoder = AutoModel.from_pretrained(model_name)
self.dropout = nn.Dropout(dropout)
self.profanity_head = nn.Linear(hidden_size, 1)
self.threat_head = nn.Linear(hidden_size, 1)
self.illegal_head = nn.Linear(hidden_size, 1)
def forward(self, input_ids, attention_mask):
out = self.encoder(input_ids=input_ids, attention_mask=attention_mask)
cls = self.dropout(out.last_hidden_state[:, 0, :])
return (
self.profanity_head(cls),
self.threat_head(cls),
self.illegal_head(cls),
)
# 3. Загрузка весов
model = MultiTaskToxicityEncoder(
model_name=config["base_model"],
hidden_size=config["hidden_size"],
)
state_dict = torch.load("model_weights.pt", map_location="cpu")
model.load_state_dict(state_dict)
model.eval()
# 4. Инференс
thresholds = config["thresholds"]
def predict(text):
enc = tokenizer(
text, return_tensors="pt",
padding="max_length", truncation=True,
max_length=config["max_length"],
)
with torch.no_grad():
p_logit, t_logit, i_logit = model(
enc["input_ids"], enc["attention_mask"]
)
probs = {
"profanity": torch.sigmoid(p_logit).item(),
"threat": torch.sigmoid(t_logit).item(),
"illegal": torch.sigmoid(i_logit).item(),
}
labels = {k: int(v >= thresholds[k]) for k, v in probs.items()}
return {"probs": probs, "labels": labels}
print(predict("Ты полный идиот, заткнись!"))
```
## Обучение
- **Датасет**: [AtesiT/ru-multitask-toxicity](https://huggingface.co/datasets/AtesiT/ru-multitask-toxicity)
- **Функция потерь**: `BCEWithLogitsLoss` с `pos_weight` для каждого класса
- **Оптимизатор**: AdamW (lr=2e-5, weight_decay=0.01)
- **Early Stopping**: patience=3 по валидационному лоссу
- **Макс. длина последовательности**: 64 токена
## Ограничения
- Модель обучена на синтетически аугментированных данных для редких классов
(`threat`, `illegal`) — качество на реальных данных может отличаться.
- Короткие тексты (< 5 слов) могут обрабатываться менее точно.
- Модель не учитывает контекст диалога — только отдельное сообщение.
## Лицензия
MIT. Создано в учебных целях для курса по трансформерным энкодерам.