При изучении теории вероятностей и статистики одной из первых и наиболее важных теорем, которые изучают студенты, является теорема Байеса . Эта теорема является основой дедуктивного мышления, которое фокусируется на определении вероятности возникновения события на основе предварительных знаний об условиях, которые могут быть связаны с этим событием.
Наивный байесовский классификатор привносит силу этой теоремы в машинное обучение, создавая очень простой, но мощный классификатор. В этой статье мы увидим обзор того, как работает этот классификатор, какие у него подходящие приложения и как использовать его всего в нескольких строках 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)
- вероятность наступления события (независимо от гипотезы).
Это теорема Байеса. На первый взгляд, это может быть трудно понять, но это очень интуитивно понятно, если мы исследуем это на примере:
Допустим, нас интересует, является ли электронное письмо, содержащее слово « секс» (событие), спамом (гипотеза). Если вернуться к описанию теоремы, эту задачу можно сформулировать так:
P(class=SPAM|contains="sex") = (P(contains="sex"|class=SPAM) * P(class=SPAM)) / P(contains="sex")
что на простом английском языке выглядит следующим образом: вероятность того, что электронное письмо, содержащее слово « секс», является спамом, равна доле спама, содержащих слово « секс», умноженной на долю электронных писем, являющихся спамом, и разделенной на долю электронных письма, содержащие слово " секс" .
Давайте разберем это по частям:
P(class=SPAM|contains="sex")
- это вероятность того, что электронное письмо будет СПАМом, учитывая, что это электронное письмо содержит слово " секс" . Это то, что нам интересно предсказывать.P(contains="sex"|class=SPAM)
- вероятность того, что электронное письмо будет содержать слово " пол", при условии, что это сообщение было распознано как СПАМ. Это наши обучающие данные, которые представляют собой взаимосвязь между электронным письмом, считающимся СПАМом, и таким электронным письмом, содержащим слово « секс» .P(class=SPAM)
- это вероятность того, что электронное письмо является СПАМом (без каких-либо предварительных сведений о содержащихся в нем словах). Это просто доля электронных писем, являющихся СПАМом во всей нашей обучающей выборке. Мы умножаем на это значение, потому что нам интересно знать, насколько важна информация о спаме в электронных письмах. Если это значение низкое, значимость любых событий, связанных со спамом, также будет низкой.P(contains="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")
представляет
количество случаев, в которых сообщение электронной почты считается
спамом и содержит слово " пол" , разделенное на общее количество
сообщений электронной почты, содержащих слово секс :
P(class=SPAM|contains="sex") = count(class=SPAM & contains=sex) / count(contains=sex)
Приложения
Применение наивного байесовского классификатора было успешным в различных сценариях. Классическим вариантом использования является классификация документов: определение того, соответствует ли данный документ определенным категориям. Тем не менее, у этого метода есть свои преимущества и ограничения.
Преимущества
- Наивный байесовский алгоритм - это простой и легкий в реализации алгоритм. Из-за этого он может превзойти более сложные модели, когда объем данных ограничен.
- Наивный Байес хорошо работает с числовыми и категориальными данными. Его также можно использовать для выполнения регрессии с помощью гауссовского наивного байеса.
Ограничения
-
Учитывая построение теоремы, она не работает, когда вам не хватает определенной комбинации значений в ваших обучающих данных. Другими словами, если у вас нет совпадений метки класса и определенного значения атрибута (например, class = "spam", contains = "$$$"), то оценка вероятности на основе частоты будет равна нулю. Учитывая предположение об условной независимости Наивного-Байеса, при умножении всех вероятностей вы получите ноль.
-
Наивный байесовский метод работает хорошо, пока категории остаются простыми. Например, он хорошо работает для задач, связанных с ключевыми словами в качестве функций (например, обнаружение спама), но не работает, когда взаимосвязь между словами важна (например, анализ тональности).
Демо в Scikit-Learn
Пришло время демонстрации! Мы будем использовать Python 3 вместе с Scikit-Learn, чтобы создать очень простой детектор спама для SMS-сообщений (для тех из вас, кто является молодым, это то, что мы использовали для обмена сообщениями еще в средние века). Вы можете найти и скачать набор данных по этой ссылке .
Нам понадобятся три библиотеки, которые значительно упростят
кодирование: scikit-learn
, pandas
и nltk
. Вы можете использовать
pip
или conda
для их установки.
Загрузка данных
Сборник SMS-спама v.1 - это набор сообщений с тегами SMS, которые были собраны для исследования SMS-спама. Он содержит один набор SMS-сообщений на английском языке из 5 574 сообщений, помеченных в зависимости от того, являются ли они легальными (законными) или спамом. Всего распространено 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...
Чтобы загрузить данные, мы можем использовать метод 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)
В-пятых, мы выполним определение корней слов . Идея выделения корней состоит в том, чтобы нормализовать наш текст, так как все варианты слов несут одно и то же значение, независимо от времени. Один из самых популярных алгоритмов стемминга - 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-сообщений.