Обработка естественного языка

Bag of Words и TF-IDF

2004 год. Gmail запускает бету с 1 ГБ хранилища - смешные 250 МБ у конкурентов. За месяц - миллион пользователей. И всё это держится на TF-IDF: спам-фильтр, который за 2 миллисекунды решает судьбу каждого письма ещё до тяжёлых нейросетей. Изобретённый в 1970-х алгоритм защищал почту миллиарда людей в 2024 году.

  • **Elasticsearch** (BM25 = улучшенный TF-IDF) - движок поиска для Wikipedia, GitHub, Netflix, Stack Overflow
  • **Спам-фильтры** Gmail используют TF-IDF + наивный Байес как первый быстрый фильтр перед тяжёлыми нейросетями
  • **Рекомендательные системы** Amazon сравнивают TF-IDF описания товаров для раздела "Похожие товары"

Герард Солтон и изобретение TF-IDF

Герард Солтон из Cornell University разработал SMART (System for the Mechanical Analysis and Retrieval of Text) - первую систему автоматического поиска информации. TF-IDF возник как часть этой системы в 1972 году. Солтон хотел ответить на вопрос: как машина может понять, что документ про «квантовую механику» релевантен запросу, а документ про «the» - нет? Ответ оказался математически лаконичным: штрафовать частые слова и поощрять редкие. Спустя 50 лет алгоритм живёт в Elasticsearch, обрабатывающем триллионы запросов в год.

Предварительные знания

  • Regular Expressions and Text

Bag of Words

**"Собака укусила человека"** и **"Человек укусил собаку"** - для BoW это ОДИНАКОВЫЕ тексты. Шокирует? Модель **Bag of Words** (мешок слов) - одно из самых простых, но эффективных представлений текста. Идея: забыть про порядок слов и считать только частоту каждого слова.

**Главная потеря BoW - порядок слов.** "Собака укусила человека" (новость) и "Человек укусил собаку" (сенсация) получат идентичные векторы. Для задач, где порядок критичен (перевод, генерация), BoW не подходит. Но для классификации тем - работает прекрасно.

**Bigrams и trigrams** (n-граммы) - простое расширение BoW, которое частично восстанавливает порядок. "not good" как единый токен несёт больше информации, чем "not" + "good" по отдельности. Но размер словаря растёт экспоненциально!

**CountVectorizer** в sklearn автоматически выполняет токенизацию, lowercasing и подсчёт частот. Параметры: `max_features` (ограничить словарь), `min_df`/`max_df` (фильтр по частоте), `stop_words` (удалить стоп-слова), `ngram_range` (n-граммы).

Два документа: "кот ест рыбу" и "рыбу ест кот". Какое утверждение верно для BoW (CountVectorizer)?

TF-IDF

Слово "the" встречается в каждом английском документе. Слово "quantum" - в каждом тысячном. Какое слово полезнее для поиска? **TF-IDF** (Term Frequency - Inverse Document Frequency) решает ключевую проблему BoW: не все слова одинаково важны. Частые повсюду слова получают низкий вес, редкие уникальные - высокий.

**Формула TF-IDF:** `TF-IDF(t, d) = TF(t, d) × IDF(t)` - **TF** измеряет важность слова *внутри* документа (локальный вес) - **IDF** измеряет уникальность слова *по всей коллекции* (глобальный вес) - Результат: высокий вес для слов, которые часты в ЭТОМ документе, но редки в других

СловоВстречается в документахIDF-весИнтерпретация
"the"9999 из 10000~0.0001Бесполезно для поиска
"machine"500 из 10000~3.0Средняя информативность
"quantum"10 из 10000~6.9Очень информативно
"supercalifragilistic"1 из 10000~9.2Уникальный маркер документа

**sklearn TfidfVectorizer** по умолчанию использует сглаженный IDF: `log((1+N)/(1+df)) + 1`. Это предотвращает деление на ноль и даёт чуть другие числа, чем классическая формула. Параметр `smooth_idf=False` отключает сглаживание.

В коллекции из 1000 документов слово "алгоритм" встречается в 10 документах, а слово "и" - в 999. У какого слова TF-IDF вес будет выше (при одинаковой TF)?

Document-Term Matrix

BoW и TF-IDF создают **Document-Term Matrix** - таблицу, где строки = документы, столбцы = термины, а значения = частоты или TF-IDF веса. Эта матрица - основа для поиска похожих документов, кластеризации и классификации текста.

**Cosine Similarity** измеряет угол между двумя векторами: `cos(A, B) = (A · B) / (||A|| × ||B||)`. Значение от 0 (ничего общего) до 1 (идентичны). В отличие от Euclidean distance, cosine similarity не зависит от длины документа - длинный документ и его краткое содержание будут похожи.

**Этот подход используют реальные системы!** Elasticsearch под капотом применяет BM25 - усовершенствованную версию TF-IDF. Google начинал именно с TF-IDF (PageRank добавил анализ ссылок, но текстовый поиск был на TF-IDF).

Размер Document-Term Matrix зависит от словаря. Для 10 000 документов и 50 000 уникальных слов - матрица 10 000 × 50 000 = **500 миллионов ячеек**. Но подавляющее большинство из них - нули. Как хранить это эффективно? Ответ - в следующем concept.

Cosine similarity между двумя документами = 0.92. Что это означает?

Разреженные матрицы

Document-Term Matrix для реального корпуса: 100 000 документов × 200 000 слов = **20 миллиардов** ячеек. При 8 байтах на float64 - это 160 ГБ оперативной памяти. Но каждый документ содержит лишь ~200 уникальных слов из 200 000. Значит, **99.9% матрицы - нули**. Хранить их - безумие.

ФорматРасшифровкаБыстрая операцияКогда использовать
CSRCompressed Sparse RowСтроковые срезы, матричное умножениеПосле построения - для вычислений
CSCCompressed Sparse ColumnСтолбцовые срезыКолоночные операции
COOCoordinateПостроение матрицыПри создании - затем конвертировать
LILList of ListsИнкрементальное заполнениеПостроение по элементам

**Никогда не вызывайте `.toarray()` на больших sparse-матрицах!** Для корпуса из 100K документов это может съесть всю RAM и убить процесс. Все операции sklearn (fit, predict, cosine_similarity) работают с sparse-матрицами напрямую.

**Правило:** если плотность матрицы < 10%, sparse-формат экономит память. Для NLP-матриц (плотность ~0.1-2%) экономия составляет 50-1000x. Всегда проверяйте: `print(f"Плотность: {matrix.nnz / (matrix.shape[0] * matrix.shape[1]):.2%}")`.

Sparse-матрицы - не только про экономию памяти. Операции (умножение, cosine similarity) на sparse-данных тоже быстрее, потому что нули пропускаются. На матрице 100K × 50K с плотностью 0.1% sparse cosine_similarity работает в **~100 раз быстрее**, чем dense.

BoW и TF-IDF устарели - в эпоху BERT и GPT они бесполезны

BoW/TF-IDF остаются рабочими инструментами для многих задач. Elasticsearch (основа поиска Google, Amazon, Wikipedia) использует BM25 - развитие TF-IDF. Для классификации коротких текстов TF-IDF + SVM часто не уступает BERT, работая в 100-1000 раз быстрее и не требуя GPU.

Трансформеры побеждают в задачах, где важен глубокий контекст: перевод, QA, диалоги. Но для поиска по ключевым словам, фильтрации спама, классификации тем - TF-IDF достаточен и в разы дешевле. На production-системах с миллионами запросов в секунду стоимость inference BERT может быть неприемлемой.

Document-Term Matrix: 50 000 документов × 100 000 слов. Плотность - 0.1%. Сколько памяти нужно в dense vs sparse (float64, 8 байт)?

Ключевые идеи

  • **Bag of Words** превращает текст в вектор частот слов, теряя порядок. "Собака укусила человека" = "Человек укусил собаку" - помните этот парадокс? N-граммы частично решают проблему
  • **TF-IDF** взвешивает слова: частые повсюду ("the") получают вес ~0, уникальные для документа - высокий вес. Формула: TF × IDF
  • **Document-Term Matrix** + **Cosine Similarity** = простой, но быстрый поисковый движок. Google начинался с этого
  • **Sparse-матрицы** экономят 100-1000x памяти для NLP-данных, где 99%+ элементов - нули

Связанные темы

BoW и TF-IDF - фундамент для более продвинутых методов:

  • Регулярные выражения и текст — Preprocessing и нормализация текста перед vectorization
  • Word Embeddings — Следующий уровень: dense-векторы (Word2Vec, GloVe) вместо sparse TF-IDF

Вопросы для размышления

  • Почему cosine similarity лучше Euclidean distance для сравнения документов разной длины?
  • В каких ситуациях BoW с биграммами будет лучше, чем BoW с юниграммами - и когда хуже?
  • Если TF-IDF всё ещё используется в 2026 году (BM25 в Elasticsearch), почему его часто называют "устаревшим"?

Связанные уроки

  • nlp-02 — Препроцессинг текста и регулярные выражения предшествуют векторизации
  • nlp-04 — Word2Vec и GloVe - следующий уровень после sparse TF-IDF
  • ml-01 — TF-IDF векторы - прямой input для классификаторов (SVM, Naive Bayes)
  • it-01 — IDF-взвешивание - частный случай информационной значимости токена
  • alg-01 — Понимание big-O помогает оценить стоимость sparse операций
  • prob-01-intro — TF-IDF + Naive Bayes требует понимания условных вероятностей
  • ml-01-intro
Bag of Words и TF-IDF

0

1

Войти