
Когда нужно классифицировать текст, дефолтный ход в 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, дообученный трансформер – чемпион. Стоит посмотреть на честную метрику, и оказывается, что он собрал высокий процент, сдав самый трудный класс.
Возникает справедливый вопрос: но бейзлайнам-то мы дали балансировку, а трансформеру нет. Нечестно. Согласны – поэтому дали.
Дообученный трансформер: честный бой
Тот же rubert-tiny2, но теперь с весами классов в функции потерь и с третьей эпохой – ровно те же условия, что у бейзлайна, плюс запас времени.
macro‑F1 0.675;
класс «нейтрально» ожил: F1 поднялся с нуля до 0.30.
Балансировка вернула редкий класс, и модель обошла TF-IDF – на этом сплите на 0.004. Разрыв крошечный, и по одному прогону судить о нем нельзя. Позже, по замечанию из комментариев, мы прогнали оба варианта на пяти сплитах – замер вынесен в апдейт в конце. Если коротко: перевес трансформера стабильный, но небольшой, около полутора пунктов macro-F1.

Вот теперь можно свести все в одну таблицу.
Сводная таблица
Модель | 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.