Когда нужно классифицировать текст, дефолтный ход в 2026 — взять предобученный трансформер и дообучить. TF‑IDF воспринимается как музейный экспонат: работал в эпоху до BERT, сейчас не всерьез. Обычно этот выбор никто не проверяет на своих данных, потому что и так понятно, кто победит.

Мы решили проверить. Взяли одну задачу, один корпус, одну метрику и прогнали три подхода подряд: счетный бейзлайн на TF‑IDF, сверточную сеть TextCNN и дообученный русский энкодер. Считали не только качество, но и стоимость — время обучения и скорость предсказания на обычном процессоре.

Короткий вывод: на этой задаче счетный бейзлайн обучается за три секунды и по честной метрике держится вровень с трансформером, который дообучался восемь минут. А трансформер, взятый по стандартному рецепту, сначала вообще проиграл — и проиграл поучительно.

Дальше — как так вышло и что из этого забрать себе.

Данные и методика

Корпус — отзывы об организациях с Яндекс Карт (d0rj/geo-reviews-dataset-2023, лицензия MIT, 500 тысяч записей). Из него собран воспроизводимый учебный срез: чистка по длине текста и валидности рейтинга, дедупликация по нормализованному тексту до разбиения (иначе один отзыв утечет и в train, и в test), стратифицированный сплит с фиксированным seed.

Задача — тональность в три класса. Метка берется из рейтинга: 1–2 негатив, 3 нейтрально, 4–5 позитив. Это дешевая разметка из готового поля, и у нее есть цена, к которой мы еще вернемся.

Вот важное свойство данных, которое определит весь результат: классы сильно несбалансированы.

Выборка

Всего

Позитив

Негатив

Нейтрально

train

41 998

36 199 (86%)

3 963 (9%)

1 836 (4%)

test

9 002

7 758

850

394

Позитива — 86 процентов. Нейтральных отзывов — четыре. Это типичная картина для отзывов: люди чаще пишут, когда все хорошо, а тройка встречается редко и звучит невнятно.

Из‑за перекоса метрику надо выбирать аккуратно. Accuracy тут почти бесполезна: модель, которая всегда отвечает «позитив», получит 86 процентов и ничему не научится. Поэтому основная метрика — macro‑F1: она усредняет F1 по трем классам с равным весом, и провал на редком классе сразу виден. Она же меняться не должна между моделями, иначе сравнение нечестное.

Чтобы бой был честным, всем моделям, где это возможно, включили балансировку классов: линейной модели через class_weight, сверточной и трансформеру — через веса в функции потерь. Разбиение, seed и тестовая выборка у всех одни и те же.

Все прогоны — на процессоре (12 ядер, без GPU). Это осознанно: если задача решается на CPU, стоимость решения становится частью ответа.

Бейзлайн: TF‑IDF и линейная модель

Начинаем с нижней границы. TF‑IDF превращает текст в разреженный вектор частот слов, поверх — логистическая регрессия с балансировкой классов. Векторизатор обучается только на train, иначе тестовые тексты влияли бы на признаки — тихая утечка.

Две конфигурации: только униграммы и униграммы вместе с биграммами.

  • TF‑IDF на униграммах: macro‑F1 0.664, обучение 1.5 секунды.

  • TF‑IDF с биграммами: macro‑F1 0.671, обучение 2.8 секунды, 107 тысяч признаков.

Биграммы добавляют совсем немного и стоят почти столько же. Инференс — сотые доли секунды на тысячу текстов. Линейная модель еще и объяснима: можно посмотреть слова с наибольшим весом по каждому классу и поймать, что модель выучила названия конкретных сетей вместо тональности.

TextCNN: обучаемые векторы

Следующий шаг сложности — своя нейросеть. TextCNN учит эмбеддинги слов с нуля и собирает из них признаки сверткой по n‑граммам. Модель на 3.8 миллиона параметров, три эпохи, тоже с балансировкой классов.

Результат неожиданный: macro‑F1 0.624 — ниже обоих вариантов TF‑IDF. При этом обучение заняло 166 секунд, в шестьдесят раз дольше бейзлайна.

Причина простая. Обучаемым эмбеддингам нужно много данных, чтобы обогнать честный счет частот. Сорок тысяч отзывов — мало: словарь получается разреженным, и на редких классах свертка выучивает меньше, чем линейная модель на явных признаках. Сложнее не значит лучше.

Дообученный трансформер: сначала ловушка

Теперь тяжелая артиллерия. Берем cointegrated/rubert-tiny2 — компактный русский энкодер на 29 миллионов параметров, который дообучается на процессоре за разумное время. Стандартный рецепт: две эпохи, обычная кросс‑энтропия, без ручной балансировки.

И вот тут первый сюрприз:

  • accuracy 0.923 — выше всех в статье;

  • macro‑F1 0.574 — ниже всех, кроме константы.

Как одна модель может быть одновременно лучшей и худшей? Смотрим на F1 по классам и все становится ясно: по классу «нейтрально» F1 равен нулю. Трансформер ни разу не предсказал нейтральную тональность. Он выучил негатив и позитив, а редкий класс просто выбросил — и почти ничего не потерял в accuracy, потому что нейтральных всего четыре процента.

Accuracy хвалит модель, которая жмет в мажоритарный класс, а macro-F1 показывает правду
Accuracy хвалит модель, которая жмет в мажоритарный класс, а macro‑F1 показывает правду

Это и есть ловушка. Если смотреть только на accuracy, дообученный трансформер — чемпион. Стоит посмотреть на честную метрику, и оказывается, что он собрал высокий процент, сдав самый трудный класс.

Возникает справедливый вопрос: но бейзлайнам‑то мы дали балансировку, а трансформеру нет. Нечестно. Согласны — поэтому дали.

Дообученный трансформер: честный бой

Тот же rubert‑tiny2, но теперь с весами классов в функции потерь и с третьей эпохой — ровно те же условия, что у бейзлайна, плюс запас времени.

  • macro‑F1 0.675;

  • класс «нейтрально» ожил: F1 поднялся с нуля до 0.30.

Балансировка вернула редкий класс, и модель наконец обошла TF‑IDF. На 0.004. То есть в пределах шума.

Где ломается accuracy: редкий класс «нейтрально» дообученный трансформер без весов не предсказывает ни разу
Где ломается accuracy: редкий класс «нейтрально» дообученный трансформер без весов не предсказывает ни разу

Вот теперь можно свести все в одну таблицу.

Сводная таблица

Модель

macro‑F1

accuracy

F1 “нейтр.”

Обучение

Инференс, с/1000

Параметры

Константа (частый класс)

0.309

0.862

0.00

0

TF‑IDF 1-gram + logreg

0.664

0.879

0.32

1.5 с

0.01

~0.1M

TF‑IDF 1–2-gram + logreg

0.671

0.891

0.32

2.8 с

0.03

~0.3M

TextCNN

0.624

0.848

0.28

166 с

0.82

3.8M

rubert‑tiny2 (без весов, 2 эп.)

0.574

0.923

0.00

293 с

0.92

29M

rubert‑tiny2 (с весами, 3 эп.)

0.675

0.912

0.30

478 с

1.18

29M

Два лучших результата почти совпали: 0.671 у бейзлайна и 0.675 у трансформера. Разница между ними — цена.

Качество против стоимости

Это главный график для нас. По горизонтали — время обучения на процессоре в логарифмическом масштабе, по вертикали — качество.

Счетный бейзлайн: столько же качества за доли секунды
Счетный бейзлайн: столько же качества за доли секунды

Левый верхний угол — мечта: дешево и хорошо. Там сидит TF‑IDF. Трансформер того же качества стоит правее на два порядка.

Цифры ребром:

  • обучение: 2.8 секунды против 478, разница примерно в 170 раз;

  • предсказание: 0.03 против 1.18 секунды на тысячу текстов, около 40 раз;

  • размер: 0.3 против 29 миллионов параметров.

За все это — плюс 0.004 macro‑F1. На потоке в миллион текстов в сутки разница в инференсе превращается в реальные деньги на железо, а прирост качества по‑прежнему теряется в шуме.

Как это читать на своей задаче

Вывод не в том, что трансформеры не нужны. Вывод в том, что порядок действий обратный привычному.

Начинать с бейзлайна. TF‑IDF с логистической регрессией собирается за минуты и сразу дает честную планку. Без нее непонятно, улучшает ли тяжелая модель хоть что‑то или просто дороже.

Смотреть на правильную метрику. На несбалансированных данных accuracy убаюкивает. macro‑F1, F1 по редкому классу и матрица ошибок показывают, что происходит на самом деле.

Считать стоимость как часть результата. Строка сравнения без времени обучения и инференса не дает принять решение о внедрении.

Тянуться за трансформером, когда бейзлайн уперся. Если планка взята и ее не хватает — тогда есть смысл в дообучении, длинном контексте, переносе знаний из претрейна. Но это осознанный следующий шаг, а не первый рефлекторный.

Что осталось за кадром

Честно опишу границы этого сравнения

Модель маленькая. rubert‑tiny2 — компактный энкодер под CPU. Модель побольше (rubert‑base, многоязычные e5) скорее всего оторвется от бейзлайна заметнее. Но и стоимость вырастет еще, а тезис про «сначала померь цену» станет только крепче.

Задача простая. Тональность отзывов — короткие тексты и явные сигналы, где счетные признаки сильны. На NER, разрешении кореференции или семантическом поиске расклад другой, и там трансформер отрывается по‑настоящему.

Разметка дешевая. Тональность из рейтинга — это прокси. Человек с рейтингом 3 мог написать вполне позитивный отзыв. Часть путаницы на классе «нейтрально» — не вина моделей, а свойство меток.

Мы не выжимали максимум. Ни у одной модели не крутили гиперпараметры до предела. Цель была не рекорд, а честное сравнение при равных условиях.

Если вы гоняете классификацию текста в проде — соберите свой бейзлайн и свой график «качество против стоимости». Вполне возможно, что скучная линейная модель уже дает вам девяносто процентов результата за один процент цены.


Весь пайплайн из этой статьи — от сборки среза до дообученного энкодера и сводной таблицы — собирается руками с нуля в нашем курсе «NLP на практике: от токенов до трансформеров и эмбеддингов». Разборы того, где инженерные решения ломаются и что реально работает в проде, выходят в канале @torch_lab.