Все помешались на трансформерах. А Карпати - нет
2026 год. Кажется, любая задача по обработке текста требует как минимум 7-миллиардную модель, три A100 и молитву на удачную квантизацию. BERT, RoBERTa, DeBERTa - алфавитный суп из архитектур, где каждая следующая буква обещает +0.5% accuracy ценой удвоения вычислительных затрат.
А потом ты заходишь на arXiv Sanity - сервис Андрея Карпати для поиска научных статей, которым пользуются десятки тысяч исследователей. И обнаруживаешь, что классификация по темам там работает на... SVM с TF-IDF. Да, на той самой технологии, которую все считали устаревшей ещё в 2018-м.
Вот ирония: один из главных проповедников глубокого обучения в мире использует для своего рабочего инструмента «старомодные» методы. И они работают. Отлично работают.
Зачем это вам? Потому что деньги - не фантики
Представьте два сценария:
- Сценарий BERT: Разворачиваете модель на GPU-сервере ($200/месяц). Ждёте 30 секунд на инференс одной статьи. Обучение на вашем датасете требует отдельного инженера и неделю экспериментов.
- Сценарий SVM+TF-IDF: Запускаете на самом дешёвом VPS за $5. Классификация занимает миллисекунды. Весь код умещается в 50 строк Python.
Разница в стоимости - 40x. Разница во времени инференса - 1000x. А в accuracy? Часто - 1-3% в пользу BERT на идеальных данных. На реальных, зашумленных - иногда SVM даже выигрывает.
Как работает классификатор arXiv Sanity (без магии)
Карпати не публиковал точный код, но по косвенным признакам и стандартным практикам можно восстановить архитектуру:
- Сбор текстов: Берутся заголовки и аннотации статей с arXiv. Только текст, без формул и специальной разметки.
- Предобработка: Токенизация, лемматизация (приведение слов к начальной форме), удаление стоп-слов. Для научных текстов часто оставляют специальные термины.
- TF-IDF векторизация: Каждый документ превращается в вектор из 10-50 тысяч измерений (в зависимости от размера словаря).
- SVM с линейным ядром: Обучается классификатор, который ищет оптимальную разделяющую гиперплоскость между классами.
Вся прелесть в том, что SVM с линейным ядром идеально сочетается с TF-IDF. Высокая размерность? Не проблема. Разреженные данные? Именно то, что нужно. Линейные SVM масштабируются на миллионы примеров и работают в режиме реального времени.
1 Собираем данные: не только arXiv
Первая ошибка - думать, что метод работает только для научных статей. Я применял ту же связку для:
- Классификации обращений в техподдержку (20 категорий, 95% точность)
- Автоматической тегизации постов в блоге
- Определения тональности отзывов на товары
Главное - достаточное количество размеченных данных. SVM любит 1000+ примеров на класс, но может работать и с меньшим, если признаки хорошие.
2 Пишем код: 50 строк, которые заменяют нейросеть
Вот минимальный рабочий пример. Не нужно устанавливать torch, transformers или что-то тяжёлое:
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.svm import LinearSVC
from sklearn.pipeline import Pipeline
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
# 1. Загружаем данные
# Предполагаем CSV с колонками 'text' и 'label'
df = pd.read_csv('articles.csv')
# 2. Создаём pipeline: TF-IDF -> SVM
# ngram_range=(1, 2) означает униграммы и биграммы
# max_features ограничивает размер словаря (ускоряет работу)
tfidf = TfidfVectorizer(
max_features=10000,
ngram_range=(1, 2),
stop_words='english',
min_df=2 # слово должно встречаться минимум в 2 документах
)
# LinearSVC быстрее SVC с linear kernel и часто точнее
svm = LinearSVC(
C=1.0,
class_weight='balanced', # важно при несбалансированных классах
max_iter=2000
)
pipeline = Pipeline([
('tfidf', tfidf),
('svm', svm)
])
# 3. Делим на train/test
X_train, X_test, y_train, y_test = train_test_split(
df['text'], df['label'], test_size=0.2, random_state=42
)
# 4. Обучаем (это займёт секунды, даже на 10к документов)
pipeline.fit(X_train, y_train)
# 5. Предсказываем и оцениваем
y_pred = pipeline.predict(X_test)
print(classification_report(y_test, y_pred))
# 6. Сохраняем модель для продакшена
import joblib
joblib.dump(pipeline, 'tfidf_svm_model.joblib')
# Для инференса:
# loaded_model = joblib.load('tfidf_svm_model.joblib')
# prediction = loaded_model.predict(["Новый метод квантового машинного обучения"])
Видите? Никаких GPU, никаких сложных архитектур. Весь файл с моделью весит мегабайты, а не гигабайты.
Параметр C в SVM - это регуляризация. Меньше C = больше регуляризация, модель проще. Начинайте с C=1.0 и меняйте на 0.1 или 10, если видите переобучение или недообучение.
Почему это всё ещё работает в 2026 году?
Три причины, которые все игнорируют:
| Фактор | SVM+TF-IDF | BERT/трансформеры |
|---|---|---|
| Требования к данным | 1000+ размеченных примеров | 10000+ для fine-tuning |
| Скорость инференса | ~1 мс на документ | 100-1000 мс |
| Потребление памяти | 10-100 МБ | 1-10 ГБ |
| Интерпретируемость | Можно посмотреть важные слова | Чёрный ящик |
Но главное - закон убывающей отдачи. Первые 95% точности SVM добирает легко. Следующие 4% BERT получает ценой 100x ресурсов. Последний 1% - это уже игры с ансамблями и тонкой настройкой, которая в продакшене часто не нужна.
Когда НЕ использовать этот подход
Честно о недостатках:
- Контекстная неоднозначность: Слова «яблоко» (фрукт) и «Apple» (компания) получат одинаковые векторы. BERT справится лучше.
- Многоязычные данные: TF-IDF нужно обучать отдельно для каждого языка или использовать сложные трюки.
- Очень короткие тексты: Твиты, поисковые запросы - там, где контекст критичен, трансформеры выигрывают.
- Transfer learning: Хотите дообучить модель на новых данных без полного переобучения? С SVM это сложнее.
Для этих случаев посмотрите на гибридный поиск с BM25 или более лёгкие трансформеры типа DistilBERT.
Продвинутые трюки, которые поднимут точность на 5-10%
Базовая версия работает. Но если хочется выжать максимум:
1. Свои стоп-слова для научных текстов
Встроенный список стоп-слов в sklearn удаляет «the», «and», «is». Но в научных статьях свои мусорные слова: «paper», «propose», «method», «results». Создайте custom_stopwords и добавьте их:
custom_stopwords = set(stopwords.words('english')) | {
'paper', 'propose', 'method', 'approach', 'result',
'show', 'demonstrate', 'experiment', 'study'
}
tfidf = TfidfVectorizer(stop_words=custom_stopwords, max_features=15000)
2. Взвешивание классов
В научных статьях по computer science может быть 100 статей по ML и 10 по теории баз данных. LinearSVC с class_weight='balanced' автоматически скорректирует веса.
3. Feature engineering для научного текста
Добавьте в признаки не только слова, но и:
- Количество математических формул (по количеству знаков $ в LaTeX)
- Присутствие определённых шаблонов («et al.», «Figure 1», «Theorem 2»)
- Длину библиографии
Это превращает SVM в гибридную модель, которая учитывает не только текст, но и структуру.
4. Ансамбли с другими моделями
SVM + логистическая регрессия + наивный байес. Простой голосующий ансамбль часто даёт прирост в 1-2%, а обучается ненамного дольше.
from sklearn.ensemble import VotingClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.naive_bayes import MultinomialNB
# Каждый классификатор получает свои гиперпараметры
estimators = [
('svm', LinearSVC(C=1.0, class_weight='balanced')),
('lr', LogisticRegression(max_iter=1000)),
('nb', MultinomialNB())
]
ensemble = VotingClassifier(estimators, voting='hard')
# Используйте тот же pipeline с TF-IDF
Развёртывание в продакшене: дешёво и сердито
Вот как выглядит продакшен-архитектура:
# app.py - минимальный FastAPI сервис
from fastapi import FastAPI
import joblib
import numpy as np
app = FastAPI()
model = joblib.load('tfidf_svm_model.joblib')
@app.post("/predict")
async def predict(texts: list[str]):
predictions = model.predict(texts)
# Можно добавить вероятности (у LinearSVC нет predict_proba)
# Используйте decision_function для псевдовероятностей
scores = model.decision_function(texts)
return {"predictions": predictions.tolist(), "scores": scores.tolist()}
Этот сервис будет работать на самом слабом VPS. 1 ГБ RAM, 1 CPU ядро - достаточно для тысяч запросов в минуту. Попробуйте добиться такого же с BERT-large.
Что делать, когда SVM уже недостаточно?
Бывает. Данные стали сложнее, требования к точности выросли. Вот путь миграции:
- Сначала попробуйте Sentence Transformers: Всепрощенные эмбеддинги (например, all-MiniLM-L6-v2) + тот же SVM. Это даёт семантическое понимание при умеренных затратах.
- Затем - лёгкие трансформеры: DistilBERT, TinyBERT. В 5-10 раз меньше оригинала, но сохраняют 95% качества.
- И только потом - полноценные модели: Когда доказано, что более простые методы не справляются.
Кстати, для тематического моделирования больших коллекций документов посмотрите новые методы topic modeling 2026 - некоторые из них тоже обходятся без тяжёлых нейросетей.
Мой совет: начните с простого
Следующий раз, когда будете строить классификатор текстов:
- Соберите 100-200 размеченных примеров (можно даже вручную)
- Запустите код из этой статьи
- Оцените accuracy
Если получилось 85%+ - скорее всего, вам не нужны трансформеры. Сэкономленные $500/месяц на GPU лучше потратить на сбор более качественных данных.
Андрей Карпати не дурак. Если бы SVM+TF-IDF не работал для arXiv Sanity, он бы давно перешёл на что-то другое. Но система работает с 2015 года, обрабатывает сотни тысяч статей и не требует апгрейда железа каждые полгода.
Иногда лучшее решение - не самое модное, а самое подходящее. Особенно когда счёт идёт на реальные деньги, а не на хайп в твиттере.
P.S. Если интересно, как применять похожие принципы для поиска - почитайте про HashIndex как альтернативу векторному поиску. Там та же философия: простота, скорость, минимальные ресурсы.