AtesiT's picture
Upload README.md with huggingface_hub
0fbf965 verified
|
Raw
History Blame Contribute Delete
5.31 kB
metadata
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 с тремя независимыми классификационными головами поверх эмбеддинга [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 при обучении.

Пример использования

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
  • Функция потерь: BCEWithLogitsLoss с pos_weight для каждого класса
  • Оптимизатор: AdamW (lr=2e-5, weight_decay=0.01)
  • Early Stopping: patience=3 по валидационному лоссу
  • Макс. длина последовательности: 64 токена

Ограничения

  • Модель обучена на синтетически аугментированных данных для редких классов (threat, illegal) — качество на реальных данных может отличаться.
  • Короткие тексты (< 5 слов) могут обрабатываться менее точно.
  • Модель не учитывает контекст диалога — только отдельное сообщение.

Лицензия

MIT. Создано в учебных целях для курса по трансформерным энкодерам.