Навчання моделі класифікації тексту (BERT, RoBERTa, DeBERTa)

При донавчанні BERT ми часто стикаємося з парадоксом: метрики на валідації чудові (F1 > 0.95), а в продакшні — провал. Причина — невідповідність розподілів: train та real‑world дані різняться за довжиною, стилем, формулюваннями. Ми вирішуємо цю проблему системно: від вибору базової моделі до product

Напрямки AI-розробки

Часті запитання

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1441
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1302
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    998
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1267
  • image_logo-advance_0.webp
    Розробка логотипу компанії B2B Advance
    714
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    1006

При донавчанні BERT ми часто стикаємося з парадоксом: метрики на валідації чудові (F1 > 0.95), а в продакшні — провал. Причина — невідповідність розподілів: train та real‑world дані різняться за довжиною, стилем, формулюваннями. Ми вирішуємо цю проблему системно: від вибору базової моделі до production‑оптимізації. Нещодавно до нас звернувся клієнт із задачею класифікації відгуків на маркетплейсі — на валідації F1 досягав 0.97, але в реальному потоці впав до 0.82. Аналіз показав, що більшість хибних передбачень припадало на короткі відгуки (менше 10 токенів) — їх майже не було в навчальній вибірці. Після додавання аугментації та налаштування max_length до 128 метрика повернулась до 0.94.

Вибір базової моделі

BERT (bert-base-uncased, DeepPavlov/rubert-base-cased): класика, добре вивчена, багато туторіалів. Для більшості задач достатньо.

RoBERTa (roberta-base, ai-forever/ruRoBERTa-large): покращене навчання без Next Sentence Prediction, на більшому корпусі. Зазвичай на 1–3% краще BERT.

DeBERTa (microsoft/deberta-v3-base): диcентангльована увага — найкраща якість на benchmarks. Рекомендується, якщо потрібна максимальна точність і є GPU-ресурси.

Для української мови: ukr-models/bert-base-uk (адаптована версія BERT), DeepPavlov/rubert-base-cased (показує добрі результати на україномовних текстах), ai-forever/ruRoBERTa-large. Рекомендуємо тестувати кілька моделей на вашому датасеті.

Як вибрати базову модель для української мови?

Вибір залежить від трьох факторів: розміру датасету, цільової метрики та доступних обчислювальних ресурсів. Для старту ми рекомендуємо DeepPavlov/rubert-base-cased — він збалансований за якістю та швидкістю навчання. Якщо датасет >10К прикладів і є V100/A100, використовуйте ai-forever/ruRoBERTa-large — приріст F1 до 3%. Для максимальної точності (і за наявності часу на тюнінг) — microsoft/deberta-v3-base.

Pipeline навчання

from transformers import ( AutoTokenizer, AutoModelForSequenceClassification, TrainingArguments, Trainer ) from datasets import Dataset import evaluate import numpy as np # Підготовка даних tokenizer = AutoTokenizer.from_pretrained("DeepPavlov/rubert-base-cased") def tokenize_function(examples): return tokenizer( examples["text"], padding="max_length", truncation=True, max_length=256 # 256 достатньо для більшості задач ) dataset = Dataset.from_pandas(df) tokenized = dataset.map(tokenize_function, batched=True) tokenized = tokenized.train_test_split(test_size=0.2) # Ініціалізація моделі model = AutoModelForSequenceClassification.from_pretrained( "DeepPavlov/rubert-base-cased", num_labels=num_classes, id2label=id2label, label2id=label2id ) # Метрики accuracy = evaluate.load("accuracy") f1 = evaluate.load("f1") def compute_metrics(eval_pred): logits, labels = eval_pred predictions = np.argmax(logits, axis=-1) return { "accuracy": accuracy.compute(predictions=predictions, references=labels)["accuracy"], "f1_macro": f1.compute(predictions=predictions, references=labels, average="macro")["f1"], } # Параметри навчання training_args = TrainingArguments( output_dir="./results", num_train_epochs=5, per_device_train_batch_size=16, per_device_eval_batch_size=32, learning_rate=2e-5, weight_decay=0.01, warmup_ratio=0.1, evaluation_strategy="epoch", save_strategy="epoch", load_best_model_at_end=True, metric_for_best_model="f1_macro", fp16=True, # mixed precision для GPU ) trainer = Trainer( model=model, args=training_args, train_dataset=tokenized["train"], eval_dataset=tokenized["test"], compute_metrics=compute_metrics, ) trainer.train() 

Гіперпараметри та їх вплив

Параметр Рекомендований діапазон Вплив
learning_rate 1e-5 – 5e-5 Найкритичніший. 2e-5 — хороший старт
num_epochs 3–10 Перетренування при > 10
batch_size 8–32 Більше = стабільніше, але потрібно більше VRAM
max_length 64–512 Залежить від довжини текстів
warmup_ratio 0.06–0.1 Запобігає нестабільному старту

Порівняння часу інференсу моделей

Модель FP32 latency (CPU) INT8 latency (CPU) F1 drop (vs FP32)
ruBERT-base 120ms 18ms -0.3%
ruRoBERTa-large 340ms 52ms -0.5%
DeBERTa-v3-base 190ms 30ms -0.4%

Чому важливо використовувати weighted loss при дисбалансі класів?

Якщо класи розподілені нерівномірно (наприклад, 95% нормальних звернень і 5% скарг), стандартний CrossEntropyLoss «проігнорує» рідкісний клас. Ми додаємо class_weight у функцію втрат:

from torch import nn import torch # Обчислюємо ваги класів class_weights = compute_class_weight("balanced", classes=np.unique(labels), y=labels) weights_tensor = torch.FloatTensor(class_weights).to(device) class WeightedTrainer(Trainer): def compute_loss(self, model, inputs, return_outputs=False): labels = inputs.pop("labels") outputs = model(**inputs) logits = outputs.get("logits") loss_fn = nn.CrossEntropyLoss(weight=weights_tensor) loss = loss_fn(logits, labels) return (loss, outputs) if return_outputs else loss 

Цей прийом піднімає F1 по рідкісному класу на 10–15% без втрати якості на частих.

Оцінка та аналіз помилок

Після навчання обов'язково:

  • Confusion matrix за всіма класами
  • Приклади помилок для кожної пари (істинний клас, передбачений клас)
  • Calibration plot: наскільки достовірні ймовірності моделі
  • Error analysis: чи є патерн у помилках? (певні слова, довжина тексту, авторський стиль)
Детальний чек-лист посттренувальної валідації
  1. Перевірити розподіл передбачень на реальних даних (відмінних від train/test).
  2. Побудувати ROC-криву для кожного класу, переконатись в AUC >0.95.
  3. Виконати A/B-тест: порівняти нову модель з поточною на випадковій вибірці.
  4. Заміряти latency p99 та throughput на цільовій конфігурації.
  5. Підготувати карту моделі з обмеженнями та відомими edge-case.

Оптимізація для продакшену

Після fine-tuning експортуємо в ONNX:

from optimum.onnxruntime import ORTModelForSequenceClassification ort_model = ORTModelForSequenceClassification.from_pretrained("./results", export=True) ort_model.save_pretrained("./onnx_model") 

Benchmark: ruBERT fine-tuned → ONNX INT8: 120ms → 18ms на CPU при точності -0.3%. Завдяки квантизації вартість інференсу знижується на 40% порівняно з FP32. На одному з проєктів ми скоротили витрати на GPU з $2000 до $1200 на місяць.

Що входить у нашу роботу

Ми надаємо не просто модель, а готове рішення під ключ:

  • Аналіз датасету та визначення мінімального достатнього розміру вибірки
  • Вибір оптимальної архітектури (BERT/RoBERTa/DeBERTa) під ваші дані
  • Донавчання з підбором гіперпараметрів (learning rate, batch size, epochs)
  • Пост-тренувальний аналіз: confusion matrix, calibration curve, error analysis
  • Експорт в ONNX/TensorRT для інференсу на CPU
  • Інтеграція в ваш пайплайн (REST API, gRPC, бэтч)
  • Документація моделі та метрик, звіт про якість
  • Підтримка після впровадження — гарантуємо стабільну роботу

З нами працюють компанії з 7+ річним досвідом в NLP, ми виконали понад 30 проєктів з класифікації текстів. Зв'яжіться з нами для оцінки вашого завдання — ми підберемо оптимальний стек та режим навчання. Замовте консультацію, і ми проаналізуємо ваш датасет.

Типові результати

На задачах класифікації новин: 92–96% F1 macro. Класифікація звернень клієнтів: 88–94%. Мультиміткова класифікація: 78–86% Micro F1. Всі результати фіксуються у звіті та підтверджуються на відкладеній вибірці.