Машинное обучение

Naive Bayes

Каждый день Gmail отфильтровывает 15 миллиардов спам-писем. В основе одного из первых и до сих пор работающих подходов лежит алгоритм, который делает заведомо неверное предположение - что все слова в письме появляются независимо друг от друга. Это математически некорректно: слова "бесплатно" и "скидка" явно коррелируют. Но алгоритм, построенный на этом "наивном" допущении, классифицирует спам с точностью выше 95%. Как ошибочная предпосылка приводит к правильным результатам?

  • **Gmail спам-фильтр** начинался именно с Naive Bayes (статья Пола Грэма "A Plan for Spam", 2002) и обрабатывает 15+ миллиардов писем ежедневно - NB до сих пор используется как один из слоёв фильтрации
  • **Медицинская диагностика:** байесовский классификатор помогает врачам оценивать вероятность заболевания на основе симптомов - медицинский тест с точностью 95% при редкой болезни (1%) даёт вероятность болезни всего 16%, и без теоремы Байеса врачи систематически ошибаются в интерпретации
  • **Анализ тональности в реальном времени:** компании мониторят миллионы упоминаний в соцсетях с помощью NB-классификаторов, определяя позитивные и негативные отзывы за миллисекунды - там, где BERT потребовал бы GPU, NB работает на обычном CPU

От посмертного эссе до спам-фильтров

Преподобный Томас Байес так и не опубликовал теорему, носящую его имя. Он умер в 1761 году, и спустя два года его друг Ричард Прайс представил Лондонскому королевскому обществу рукопись «An Essay towards solving a Problem in the Doctrine of Chances». Работа осталась почти незамеченной, пока около 1774 года Пьер-Симон Лаплас независимо не переоткрыл и не обобщил эту идею, придав ей современный вид и применив к реальным задачам астрономии и демографии. Почти два столетия байесовские рассуждения оставались в основном теоретическими. Практическое возрождение пришло вместе с текстом: в 1998 году Сахами с коллегами опубликовали байесовский подход к фильтрации мусорной почты, а в 2002 году эссе Пола Грэма «A Plan for Spam» превратило эту идею в движение, показав, что простой классификатор на подсчёте слов останавливает спам лучше написанных вручную правил. Теорема из тетради священника стала первой линией обороны в каждом почтовом ящике.

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

  • Math for ML

Теорема Байеса и наивное предположение

В 1763 году, через два года после смерти преподобного Томаса Байеса, его друг Ричард Прайс опубликовал рукопись, изменившую математику навсегда. Теорема Байеса отвечает на ключевой вопрос: **как обновить наши убеждения, получив новые данные?** Формула короткая, но её следствия пронизывают всё - от медицинской диагностики до спам-фильтров.

Результат медицинского примера кажется парадоксальным: тест с точностью 95%, но при положительном результате вероятность болезни всего 16%! Дело в **prior**: болезнь редкая (1%), поэтому даже точный тест даёт много ложных срабатываний. Это ключевой инсайт Байеса - **контекст (prior) радикально влияет на интерпретацию данных**.

Теперь перейдём к классификации. Допустим, у объекта есть признаки x1, x2, ..., xn, и мы хотим определить класс c. По теореме Байеса: P(c|x1,x2,...,xn) пропорционально P(x1,x2,...,xn|c) * P(c). Проблема: **вычислить P(x1,x2,...,xn|c) напрямую невозможно** - нужно хранить совместные распределения для всех комбинаций признаков. При 20 бинарных признаках это 2^20 = 1 048 576 параметров для каждого класса.

**Наивное (Naive) предположение** решает проблему размерности элегантным хаком: Все признаки **условно независимы** при данном классе: P(x1, x2, ..., xn | c) = P(x1|c) * P(x2|c) * ... * P(xn|c) Вместо 2^20 параметров нужно всего 20 * K параметров (K - число классов). Это упрощение **математически неверно** почти всегда - признаки в реальных данных зависимы. Слова "бесплатно" и "скидка" в спаме коррелируют. Рост и вес человека зависимы. Но **на практике Naive Bayes работает!** Оценки вероятностей неточны, но *ранжирование* классов часто корректно - нам не нужны точные вероятности, нужен правильный argmax.

**Проблема нулевых вероятностей:** в примере выше P("скидка" | not_spam) = 0. Это обнуляет *всё* произведение для класса not_spam, даже если остальные признаки указывают на не-спам. Решение - **Laplace smoothing** (сглаживание), о котором поговорим в концепте про Multinomial NB.

Почему алгоритм называется «наивным» (Naive)?

Gaussian Naive Bayes

Мы разобрались с общей идеей Naive Bayes, но возникает вопрос: **как вычислить P(x|class), если x - непрерывное число?** Например, рост человека - 175.3 см. Вероятность *ровно* такого значения в непрерывном распределении равна нулю. Gaussian Naive Bayes решает это, предполагая, что значения каждого признака в каждом классе подчиняются **нормальному (гауссову) распределению**.

Обучение Gaussian NB - одно из самых быстрых среди всех алгоритмов ML. Нужно лишь **посчитать среднее и стандартное отклонение** каждого признака для каждого класса. Нет итераций, нет градиентного спуска, нет гиперпараметров для тюнинга. Один проход по данным - и модель готова. На датасете из миллиона объектов обучение занимает доли секунды.

**Когда Gaussian NB работает хорошо:** - Признаки действительно приближены к нормальному распределению - Мало данных для обучения (нужно оценить только mean и std) - Нужен быстрый baseline для сравнения с другими моделями - Задача с высокой размерностью (много признаков) **Когда Gaussian NB работает плохо:** - Признаки сильно коррелируют друг с другом (нарушение "наивного" предположения) - Распределение признаков далеко от нормального (например, бимодальное) - Нужна высокая точность на границе между классами

Gaussian NB часто используют как **первый baseline**: если Gaussian NB на данных даёт accuracy 92%, а сложная модель - 93%, стоит задуматься, оправдана ли дополнительная сложность. Если же Gaussian NB даёт 60%, а LogReg - 90%, значит, предположение о нормальности и независимости признаков для этих данных не работает, и нужны более гибкие алгоритмы.

Что хранит обученная модель Gaussian Naive Bayes для каждого признака в каждом классе?

Multinomial и Bernoulli Naive Bayes

Gaussian NB отлично работает с непрерывными признаками, но что если данные - это **счётчики** или **бинарные флаги**? Например, количество раз, когда слово встречается в документе, или факт наличия/отсутствия слова. Для таких данных существуют два специализированных варианта: **Multinomial NB** (для счётчиков) и **Bernoulli NB** (для бинарных признаков).

**Multinomial NB** моделирует данные как результат многократного "бросания кости" - каждое слово в документе выбирается из мультиномиального распределения. Если слово "скидка" встречается 5 раз в одном письме - это важнее, чем одно упоминание. Multinomial NB учитывает **частоту** слов и является стандартом для классификации текстов.

**Bernoulli NB** моделирует каждый признак как бинарный: слово либо есть, либо нет. Важная деталь: Bernoulli NB **явно моделирует отсутствие** признака - вычисляет P(word НЕТ | class). Для коротких текстов (твиты, SMS) или задач, где важен сам факт наличия слова, а не его частота, Bernoulli NB может работать лучше Multinomial.

**Laplace Smoothing (сглаживание Лапласа):** Параметр **alpha** (по умолчанию = 1) решает критическую проблему нулевых вероятностей. Без сглаживания: если слово "blockchain" никогда не встречалось в спаме, то P("blockchain"|spam) = 0. Из-за умножения вероятностей **весь класс обнуляется**, даже если все остальные слова кричат "спам". С alpha = 1 (Laplace smoothing): к каждому счётчику добавляется 1. Ни одна вероятность не будет нулевой. - alpha = 0: без сглаживания (опасно) - alpha = 1: Laplace smoothing (стандарт) - alpha < 1: Lidstone smoothing (менее агрессивное) - alpha = большое число: все вероятности стремятся к uniform

На практике alpha подбирают через **cross-validation**. Для текстов обычно оптимальные значения лежат в диапазоне 0.01--1.0. Важно помнить: Multinomial NB требует **неотрицательные** значения признаков (счётчики), поэтому его нельзя напрямую применять к TF-IDF-весам без дополнительных преобразований, хотя sklearn это обрабатывает корректно.

Зачем нужен параметр alpha (Laplace smoothing) в Multinomial Naive Bayes?

Текстовая классификация

Naive Bayes нашёл свою главную нишу в **текстовой классификации** - и не собирается её покидать. Несмотря на десятилетия развития ML, от SVM до трансформеров, Naive Bayes остаётся практичным выбором для многих текстовых задач. Причина: тексты создают **высокоразмерные разреженные данные** (десятки тысяч уникальных слов, но каждый документ содержит лишь малую часть словаря) - и именно здесь "наивное" предположение работает лучше всего.

**Bag of Words (BoW)** - простейшая векторизация: каждый документ представляется как вектор частот слов. Порядок слов полностью игнорируется - "собака укусила человека" и "человек укусил собаку" получают одинаковый вектор. Несмотря на эту потерю информации, для задач классификации BoW работает хорошо.

**TF-IDF** улучшает BoW, взвешивая слова по их информативности. Слово "и" встречается везде - его вес низкий. Слово "интерференция" встречается только в физических текстах - его вес высокий. TF-IDF автоматически выделяет слова, которые **отличают** один класс документов от другого. В связке с Multinomial NB это даёт мощный и быстрый классификатор.

**Почему Naive Bayes хорош именно для текстов:** 1. **Высокая размерность:** словарь из 50 000+ слов = 50 000 признаков. Многие алгоритмы страдают от curse of dimensionality, а NB - нет 2. **Разреженные данные:** каждый документ содержит малую долю словаря. NB обрабатывает разреженность естественно 3. **Мало данных:** NB нужно оценить только P(word|class) для каждого слова. LogReg и SVM нужно оценить веса в высокоразмерном пространстве 4. **Условная независимость приближённо верна:** слова в тексте менее зависимы, чем, например, пиксели в изображении. "Скидка" и "бесплатно" коррелируют, но не так сильно, как соседние пиксели

В реальных задачах Naive Bayes используется как первая линия: **спам-фильтры** (Gmail начинался именно с NB), **анализ тональности** (позитивный/негативный отзыв), **классификация документов** по темам, **автоматическая маршрутизация** обращений в поддержку. Для масштабных систем, где нужно классифицировать миллионы документов в реальном времени, скорость NB становится критическим преимуществом.

Naive Bayes бесполезен, потому что предположение о независимости признаков почти никогда не выполняется в реальных данных

Naive Bayes даёт конкурентоспособные результаты на практике, особенно на текстах, несмотря на нарушение предположения о независимости. Точные вероятности неверны, но порядок классов (argmax) часто правильный

Для классификации важен не точный P(class|x), а только какой класс имеет максимальную вероятность. Ошибки в оценках вероятностей часто "сокращаются" при сравнении классов. Исследования (Domingos & Pazzani, 1997) доказали, что NB оптимален по accuracy гораздо чаще, чем предсказывает теория

Почему связка TF-IDF + MultinomialNB так хорошо работает на текстах?

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

  • **Теорема Байеса** переворачивает вероятность: вместо P(симптомы|болезнь) вычисляет P(болезнь|симптомы), обновляя prior новыми данными (evidence)
  • **Naive предположение** (независимость признаков) математически неверно, но позволяет обучаться за один проход по данным и работает на практике, потому что для классификации важен argmax, а не точные вероятности
  • **Три варианта NB:** Gaussian (непрерывные данные, mean/std), Multinomial (счётчики, частоты слов), Bernoulli (бинарные признаки, наличие/отсутствие слов) - выбор зависит от типа данных
  • **Laplace smoothing (alpha)** предотвращает нулевые вероятности: одно невстреченное слово не обнуляет весь класс
  • **NB + TF-IDF** - стандартный pipeline для текстовой классификации: проигрывает LogReg и SVM 3-4% в accuracy, но в 10-15x быстрее - именно поэтому Gmail начинал с Naive Bayes для фильтрации 15 миллиардов писем в день, о чём мы говорили в начале

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

Naive Bayes стоит на пересечении теории вероятностей и практической классификации. Вот темы, которые углубят понимание:

  • Математические основы ML — Теорема Байеса, условная вероятность и распределения - математический фундамент, на котором строится Naive Bayes
  • Логистическая регрессия — Главный конкурент NB для текстовой классификации - дискриминативная модель, которая напрямую моделирует P(class|features) без оценки P(features|class)
  • Предобработка текста — Токенизация, стемминг, удаление стоп-слов - этапы, предшествующие TF-IDF и Naive Bayes в pipeline текстовой классификации
  • Word Embeddings — Современная альтернатива Bag of Words: dense-вектора слов (Word2Vec, GloVe) учитывают семантику, которую TF-IDF теряет

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

  • Если Naive Bayes предполагает независимость признаков, а в реальности слова зависимы ("Нью" почти всегда рядом с "Йорк") - почему алгоритм всё равно работает? В каких ситуациях нарушение предположения о независимости критично?
  • Врач получает положительный результат теста (точность 99%) на редкую болезнь (0.1% населения). Используя теорему Байеса, какова реальная вероятность, что пациент болен? Как это влияет на принятие медицинских решений?
  • Сейчас для текстовой классификации всё чаще используют BERT и трансформеры. В каких сценариях Naive Bayes всё ещё предпочтительнее, несмотря на меньшую точность?

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

  • ml-03-math-foundations — Формула Байеса требует понимания условной вероятности
  • prob-04-bayes — Теорема Байеса - математическая основа классификатора
  • ml-10-logistic-regression — Naive Bayes и LR - два подхода к одной задаче классификации
  • ml-34-text-preprocessing — Спам-фильтрация текста - классика применения NB
  • it-03 — Naive Bayes loss связан с cross-entropy через log-likelihood
  • ml-07-polynomial-regression — Оба работают с предположениями о распределении данных
Naive Bayes

0

1

Войти