Когда нужно классифицировать текст, дефолтный ход в 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. Разрыв крошечный, и по одному прогону судить о нем нельзя. Позже, по замечанию из комментариев, мы прогнали оба варианта на пяти сплитах – замер вынесен в апдейт в конце. Если коротко: перевес трансформера стабильный, но небольшой, около полутора пунктов macro-F1.

Где ломается 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 миллионов параметров.

За все это – около полутора пунктов macro-F1 (аккуратный замер на пяти сплитах – в апдейте ниже; без класса «нейтрально» разрыв доходит до 3.5 пунктов). Прирост реальный, но небольшой. На потоке в миллион текстов в сутки разница в инференсе превращается в реальные деньги на железо, и вопрос ровно один: стоит ли остаток этих денег.

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

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

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

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

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

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

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

Честно про границы этого сравнения, чтобы не выдавать частный результат за закон.

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

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

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

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

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

Апдейт: сколько тут шума (замер на пяти сплитах)

В комментариях справедливо заметили: обе цифры, 0.671 и 0.675, сняты на одном разбиении, а разница объявлена шумом без замера самого шума. Замечание правильное, поэтому мы прогнали оба лучших варианта – TF-IDF с биграммами и взвешенный rubert-tiny2 – на пяти разных сплитах (разный seed – своя подвыборка 60k и свое разбиение).

Сплит

TF-IDF

rubert-tiny2

разница

seed 42

0.671

0.674

+0.003

seed 1

0.665

0.674

+0.009

seed 2

0.661

0.682

+0.021

seed 3

0.669

0.696

+0.027

seed 4

0.673

0.686

+0.013

среднее

0.668 ± 0.005

0.682 ± 0.009

+0.015 ± 0.010

Трансформер впереди на всех пяти сплитах, но немного
Трансформер впереди на всех пяти сплитах, но немного

Собственный разброс каждой модели (±0.005–0.009) действительно больше тех 0.004, что были на seed 42 – так что на одном прогоне называть разницу шумом было некорректно. Но если сравнивать попарно, на одном и том же сплите, трансформер впереди на всех пяти прогонах, в среднем на 0.015. Это уже не шум, а маленький, но устойчивый перевес. получается трансформер стабильно выигрывает около полутора пунктов macro-F1.

Второй вопрос из комментариев – держится ли паритет без класса «нейтрально», у которого F1 около 0.30 у всех, а метка из тройки-рейтинга сама по себе слабый прокси. Убрали нейтраль полностью и решили бинарную задачу негатив против позитива (seed 42): TF-IDF 0.869, rubert-tiny2 0.904, разница +0.035. Паритет не держится – наоборот, на чистой задаче трансформер отрывается заметнее.

Что все это не меняет – стоимость. Даже +0.035 берутся ценой примерно 170x по времени обучения и 40x по инференсу на CPU. Так что вывод точнее звучит не как «бейзлайн не хуже», а как «бейзлайн дает вам процентов 96–98 качества за один процент цены, и дальше вопрос, стоит ли остаток этих денег». Спасибо тем, кто в комментариях помог это уточнить.


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