Наивный байесовский алгоритм в Python с помощью Scikit-Learn

При изучении теории вероятностей и статистики одной из первых и наиболее важных теорем, которые изучают студенты, является теорема Байеса [https://en.wikipedia.org/wiki/Bayes%27_theorem]. Эта теорема является основой дедуктивного мышления, которое фокусируется на определении вероятности возникновения события на основе предварительных знаний об условиях, которые могут быть связаны с этим событием. Наивный байесовский классификатор [https://en.wikipedia.org/wiki/Naive_Bayes_classifier] привносит силу этой теоремы в Машину Ли.

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

Наивный байесовский классификатор привносит силу этой теоремы в машинное обучение, создавая очень простой, но мощный классификатор. В этой статье мы увидим обзор того, как работает этот классификатор, какие у него подходящие приложения и как использовать его всего в нескольких строках 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-сообщений.

comments powered by Disqus