Рубрики
Без рубрики

Наивный Байесовский алгоритм в Python с Scikit-Learn

Автор оригинала: Daniyal Shahrokhian.

Наивный Байесовский алгоритм в Python с Scikit-Learn

При изучении теории вероятностей и статистики одной из первых и наиболее важных теорем, которые изучают студенты, является теорема Байеса . Эта теорема является основой дедуктивного рассуждения, которое фокусируется на определении вероятности наступления события на основе предварительного знания условий, которые могут быть связаны с этим событием.

Наивный Байесовский классификатор привносит силу этой теоремы в машинное обучение, создавая очень простой, но мощный классификатор. В этой статье мы увидим обзор того, как работает этот классификатор, какие подходящие приложения он имеет и как использовать его всего в нескольких строках Python и библиотеке Scikit-Learn.

Теория, лежащая в основе теоремы Байеса

Если вы изучали информатику, математику или любую другую область, связанную со статистикой, очень вероятно, что в какой-то момент вы наткнулись на следующую формулу:

P(H|E) = (P(E|H) * P(H)) / P(E)

где

  • P(H|E) – вероятность гипотезы H данного события E , апостериорная вероятность.
  • P(E|H) – вероятность события E при условии, что гипотеза H истинна.
  • P(H) – это вероятность того, что гипотеза H истинна (независимо от любого связанного с ней события), или априорная вероятность H .
  • P(E) – вероятность наступления события (независимо от гипотезы).

Это теорема Байеса. На первый взгляд это может быть трудно понять, но это очень интуитивно, если мы исследуем это на примере:

Допустим, нам интересно узнать, является ли письмо, содержащее слово sex (событие), спамом (гипотезой). Если вернуться к описанию теоремы, то эту задачу можно сформулировать так::

P(class=SPAM|contains="sex") = (P(contains="sex"|class=SPAM) * P(class=SPAM)) / P(contains="sex")

что на простом английском языке означает: Вероятность того, что электронное письмо, содержащее слово sex , является спамом, равна доле СПАМ-писем, содержащих слово sex , умноженной на долю электронных писем, являющихся спамом, и деленной на долю электронных писем, содержащих слово sex .

Давайте разберем это по кусочкам:

  • P(class=SPAM|contains="sex") – вероятность того, что письмо будет СПАМОМ, учитывая, что это письмо содержит слово sex . Именно это мы и хотим предсказать.
  • P(contains="sex"|class=SPAM) – вероятность того, что письмо содержит слово sex , учитывая, что это письмо было распознано как СПАМ. Это наши обучающие данные, которые представляют корреляцию между электронной почтой, считающейся СПАМОМ, и такой электронной почтой, содержащей слово sex .
  • P(class=SPAM) – это вероятность того, что письмо будет СПАМОМ (без какого-либо предварительного знания слов, которые оно содержит). Это просто доля электронных писем, являющихся спамом во всем нашем учебном наборе. Мы умножаем на это значение, потому что нам интересно знать, насколько важна информация о СПАМЕ. Если это значение низкое, то значимость любых событий, связанных со спамом, также будет низкой.
  • P(contains="sex") -вероятность того, что электронное письмо содержит слово sex . Это просто доля электронных писем, содержащих слово секс во всем нашем обучающем наборе. Мы делим на это значение, потому что чем более исключительным является слово sex , тем важнее контекст, в котором оно появляется. Таким образом, если это число невелико (слово появляется очень редко), оно может быть отличным индикатором того, что в тех случаях, когда оно действительно появляется, оно является релевантной характеристикой для анализа.

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

Вероятности классов

В теореме P(A) представляет вероятности каждого события. В Наивном Байесовском классификаторе мы можем интерпретировать эти вероятности классов как просто частоту каждого экземпляра события, деленную на общее число экземпляров. Например, в предыдущем примере обнаружения спама P(class=SPAM) представляет собой количество писем, классифицированных как спам, деленное на сумму всех экземпляров (это spam + not spam )

P(class=SPAM) = count(class=SPAM) / (count(class=notSPAM) + count(class=SPAM))

Условные вероятности

В теореме P(A|B) представляет условные вероятности события A при заданном другом событии B . В Наивном байесовском классификаторе они кодируют апостериорную вероятность того, что A произойдет, когда B истинно.

Для примера спама P(class=SPAM|contains="sex") представляет количество экземпляров, в которых электронное письмо считается спамом и содержит слово sex , деленное на общее количество электронных писем, содержащих слово sex :

P(class=SPAM|contains="sex") = count(class=SPAM & contains=sex) / count(contains=sex)

Приложения

Применение наивного Байесовского классификатора было показано успешным в различных сценариях. Классическим вариантом использования является классификация документов: определение того, соответствует ли данный документ определенным категориям. Тем не менее, эта техника имеет свои преимущества и ограничения.

Преимущества

  • Наивный Байес-это простой и легкий в реализации алгоритм. Из-за этого он может превосходить более сложные модели, когда объем данных ограничен.
  • Наивный Байес хорошо работает с числовыми и категориальными данными. Он также может быть использован для выполнения регрессии с помощью гауссовского наивного Байеса.

Ограничения

  • Учитывая построение теоремы, она не работает хорошо, когда вы пропускаете определенную комбинацию значений в ваших обучающих данных. Другими словами, если у вас нет вхождений метки класса и определенного значения атрибута вместе (например,), то оценка вероятности на основе частоты будет равна нулю. Учитывая наивное предположение об условной независимости Байеса, когда все вероятности будут умножены, вы получите ноль.

  • Наивный Байес работает хорошо, пока категории остаются простыми. Например, он хорошо работает для проблем, связанных с ключевыми словами как функциями (например, обнаружение спама), но он не работает, когда важна связь между словами (например, анализ настроений).

Демо-версия в Scikit-Learn

Это демо-время! Мы будем использовать Python 3 вместе с Scikit-Научимся создавать очень простой детектор спама для SMS-сообщений (для тех из вас, кто молод, это то, что мы использовали для обмена сообщениями еще в средние века). Вы можете найти и скачать набор данных по ссылке this link .

Нам понадобятся три библиотеки, которые значительно облегчат наше кодирование: scikit-learn , pandas и nltk . Вы можете использовать pip или conda для их установки.

Загрузка данных

SMS Spam Collection v. 1 – это набор SMS-сообщений с тегами, собранных для исследования SMS-спама. Он содержит один набор SMS – сообщений на английском языке из 5574 сообщений, помеченных в соответствии с тем, являются ли они ham (законными) или спамом. Распространение составляет в общей сложности 4827 SMS-сообщений (86,6%) и в общей сложности 747 (13,4%) спам-сообщений.

Если мы откроем набор данных, то увидим , что он имеет формат [label] [tab] [message] , который выглядит примерно так:

ham	Go until jurong point, crazy.. Available only in bugis n great world la e buffet... Cine there got amore wat...

ham	Ok lar... Joking wif u oni...

spam	Free entry in 2 a wkly comp to win FA Cup final tkts 21st May 2005. Text FA to 87121 to receive entry question(std txt rate)T&C's apply 08452810075over18's

ham	U dun say so early hor... U c already then say...

Для загрузки данных мы можем использовать метод Pandas Data frame read_table . Это позволяет нам определить разделитель (в данном случае табуляцию) и соответствующим образом переименовать столбцы:

import pandas as pd

df = pd.read_table('SMSSpamCollection',
                   sep='\t', 
                   header=None,
                   names=['label', 'message'])

Предварительная обработка

Как только наши данные будут готовы, пришло время сделать некоторую предварительную обработку. Мы сосредоточимся на устранении бесполезных отклонений для нашей задачи. Во-первых, мы должны преобразовать метки из строк в двоичные значения для нашего классификатора:

df['label'] = df.label.map({'ham': 0, 'spam': 1})

Во-вторых, преобразуйте все символы в сообщении в нижний регистр:

df['message'] = df.message.map(lambda x: x.lower())

В-третьих, удалите все знаки препинания:

df['message'] = df.message.str.replace('[^\w\s]', '')

В-четвертых, токенизируйте сообщения в отдельные слова с помощью nltk . Во-первых, мы должны импортировать и загрузить токенизатор из консоли:

import nltk
nltk.download()

Появится окно установки. Перейдите на вкладку “Модели” и выберите “Пункт” в столбце “Идентификатор”. Затем нажмите кнопку “Скачать”, и он установит необходимые файлы. Тогда это должно сработать! Теперь мы можем применить токенизацию:

df['message'] = df['message'].apply(nltk.word_tokenize)

Fifth, we will perform some word stemming . The idea of stemming is to normalize our text for all variations of words carry the same meaning, regardless of the tense. One of the most popular stemming algorithms is the Porter Stemmer :

from nltk.stem import PorterStemmer

stemmer = PorterStemmer()
 
df['message'] = df['message'].apply(lambda x: [stemmer.stem(y) for y in x])

Наконец, мы преобразуем данные в вхождения, которые будут функциями, которые мы будем вводить в нашу модель:

from sklearn.feature_extraction.text import CountVectorizer

# This converts the list of words into space-separated strings
df['message'] = df['message'].apply(lambda x: ' '.join(x))

count_vect = CountVectorizer()
counts = count_vect.fit_transform(df['message'])

Мы могли бы оставить его как простой подсчет слов на сообщение, но лучше использовать Term Frequency Inverse Document Frequency , более известный как tf-idf :

from sklearn.feature_extraction.text import TfidfTransformer

transformer = TfidfTransformer().fit(counts)

counts = transformer.transform(counts)

Обучение модели

Теперь, когда мы выполнили извлечение признаков из наших данных, пришло время построить нашу модель. Мы начнем с разделения ваших данных на обучающие и тестовые наборы:

from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(counts, df['label'], test_size=0.1, random_state=69)

Затем все, что нам нужно сделать, это инициализировать Наивный байесовский классификатор и подогнать данные. Для задач классификации текстов хорошо подходит Полиномиальный наивный байесовский классификатор:

from sklearn.naive_bayes import MultinomialNB

model = MultinomialNB().fit(X_train, y_train)

Оценка модели

После того, как мы собрали наш классификатор, мы можем оценить его производительность в тестовом наборе:

import numpy as np

predicted = model.predict(X_test)

print(np.mean(predicted == y_test))

Поздравляю! Наш простой наивный байесовский классификатор имеет точность 98,2% с этим конкретным тестовым набором! Но этого недостаточно, просто обеспечивая точность, поскольку наш набор данных несбалансирован, когда дело доходит до меток (86,6% легитимных в отличие от 13,4% спама). Может случиться так, что наш классификатор переоснащает законный класс, игнорируя класс спама. Чтобы решить эту неопределенность, давайте взглянем на матрицу путаницы :

from sklearn.metrics import confusion_matrix

print(confusion_matrix(y_test, predicted))

Метод confusion_matrix напечатает что-то вроде этого:

[[478   4]
[   6  70]]

Как мы видим, количество ошибок довольно сбалансировано между законным и спамом, причем 4 законных сообщения классифицируются как спам и 6 спам-сообщений классифицируются как законные. В целом, это очень хорошие результаты для нашего простого классификатора.

Вывод

В этой статье мы видели ускоренный курс как по теории, так и по практике наивного Байесовского классификатора. Мы собрали простой мультимодальный Наивный байесовский классификатор, который достигает 98,2% точности обнаружения спама для SMS-сообщений.