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

Python для НЛП: Тематическое моделирование

Автор оригинала: Usman Malik.

Это шестая статья в моей серии статей по Python для НЛП. В своей предыдущей статье я рассказывал о том , как выполнять анализ настроений данных Twitter с помощью библиотеки Scikit-Learn Python. В этой статье мы рассмотрим тему моделирования, которая является еще одним очень важным применением НЛП. Мы увидим, как сделать тематическое моделирование с помощью Python.

Что такое Тематическое моделирование

Тематическое моделирование-это неконтролируемый метод, предназначенный для анализа больших объемов текстовых данных путем кластеризации документов в группы. В случае тематического моделирования текстовые данные не имеют прикрепленных к ним меток. Скорее, тематическое моделирование пытается сгруппировать документы в кластеры на основе сходных характеристик.

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

Для тематического моделирования в основном используются два подхода: Латентное распределение Дирихле и Неотрицательная матричная факторизация . В следующих разделах мы кратко рассмотрим оба этих подхода и посмотрим, как они могут быть применены к тематическому моделированию в Python.

Латентное распределение Дирихле (LDA)

LDA основывается на двух общих предположениях:

  • Документы с похожими словами обычно имеют одну и ту же тему
  • Документы, в которых группы слов часто встречаются вместе, обычно имеют одну и ту же тему.

Эти предположения имеют смысл, потому что документы, имеющие одну и ту же тему, например, бизнес-темы, будут иметь такие слова, как “экономика”, “прибыль”, “фондовый рынок”, “убыток” и т. Д. Второе предположение гласит, что если эти слова часто встречаются вместе в нескольких документах, то эти документы могут принадлежать к одной и той же категории.

Математически эти два допущения можно представить в виде:

  • Документы-это распределения вероятностей по скрытым темам
  • Темы-это распределения вероятностей по словам

LDA для тематического моделирования в Python

В этом разделе мы увидим, как Python может быть использован для реализации LDA для тематического моделирования. Набор данных можно загрузить с сайта Kaggle .

Набор данных содержит отзывы пользователей о различных продуктах в категории продуктов питания. Мы будем использовать LDA для группировки отзывов пользователей в 5 категорий.

Первым шагом, как всегда, является импорт набора данных вместе с необходимыми библиотеками. Для этого выполните следующий сценарий:

import pandas as pd
import numpy as np

reviews_datasets = pd.read_csv(r'E:\Datasets\Reviews.csv')
reviews_datasets = reviews_datasets.head(20000)
reviews_datasets.dropna()

В приведенном выше скрипте мы импортируем набор данных с помощью метода read_csv библиотеки pandas . Исходный набор данных содержит около 500 тысяч отзывов. Однако из-за ограничений памяти я буду выполнять LDA только на первых 20 тысячах записей. В приведенном выше скрипте мы фильтруем первые 20 тысяч строк, а затем удаляем нулевые значения из набора данных.

Затем мы печатаем первые пять строк набора данных, используя функцию head() для проверки наших данных:

reviews_datasets.head()

В выходных данных вы увидите следующие данные:

Мы будем применять LDA к столбцу “Текст”, так как он содержит отзывы, остальные столбцы будут проигнорированы.

Давайте посмотрим обзор номер 350.

reviews_datasets['Text'][350]

В выходных данных вы увидите следующий текст обзора:

'These chocolate covered espresso beans are wonderful!  The chocolate is very dark and rich and the "bean" inside is a very delightful blend of flavors with just enough caffine to really give it a zing.'

Прежде чем мы сможем применить LDA, нам нужно создать словарь всех слов в наших данных. Помните из предыдущей статьи, что мы могли бы сделать это с помощью счетчика. Посмотрите на следующий сценарий:

from sklearn.feature_extraction.text import CountVectorizer

count_vect = CountVectorizer(max_df=0.8, min_df=2, stop_words='english')
doc_term_matrix = count_vect.fit_transform(reviews_datasets['Text'].values.astype('U'))

В приведенном выше скрипте мы используем класс CountVectorizer из модуля sklearn.feature_extraction.text для создания матрицы терминов документа. Мы указываем включать только те слова, которые появляются менее чем в 80% документа и появляются как минимум в 2 документах. Мы также удаляем все стоп-слова, поскольку они на самом деле не способствуют моделированию темы.

Теперь давайте посмотрим на нашу матрицу терминов документа:

doc_term_matrix

Выход:

<20000x14546 sparse matrix of type ''
with 594703 stored elements in Compressed Sparse Row format>

Каждый из 20 тысяч документов представлен в виде 14546-мерного вектора, что означает, что наш словарь содержит 14546 слов.

Далее мы будем использовать LDA для создания тем вместе с распределением вероятностей для каждого слова в нашем словаре для каждой темы. Выполните следующий сценарий:

from sklearn.decomposition import LatentDirichletAllocation

LDA = LatentDirichletAllocation(n_components=5, random_state=42)
LDA.fit(doc_term_matrix)

В приведенном выше скрипте мы используем класс Latent Dirichlet Allocation из библиотеки sklearn.decomposition для выполнения LDA на нашей матрице терминов документа. Параметр n_components определяет количество категорий или тем, на которые мы хотим разделить наш текст. Параметр random_state (он же seed ) имеет значение 42, так что вы получаете результаты, аналогичные моим.

Давайте случайным образом выберем слова из нашего словаря. Мы знаем, что графвекторизатор содержит все слова в нашем словаре. Мы можем использовать метод get_feature_names() и передать ему идентификатор слова, которое мы хотим извлечь.

Следующий скрипт случайным образом извлекает 10 слов из нашего словаря:

import random

for i in range(10):
    random_id = random.randint(0,len(count_vect.get_feature_names()))
    print(count_vect.get_feature_names()[random_id])

Вывод выглядит следующим образом:

bribe
tarragon
qualifies
prepare
hangs
noted
churning
breeds
zon
chunkier

Давайте найдем 10 слов с наибольшей вероятностью для первой темы. Чтобы получить первую тему, вы можете использовать атрибут components_ и передать индекс 0 в качестве значения:

first_topic = LDA.components_[0]

Первая тема содержит вероятности 14546 слов для темы 1. Для сортировки индексов по значениям вероятности можно использовать функцию argsort () . После сортировки 10 слов с наибольшей вероятностью теперь будут принадлежать последним 10 индексам массива. Следующий скрипт возвращает индексы 10 слов с наибольшей вероятностью:

top_topic_words = first_topic.argsort()[-10:]

Выход:

array([14106,  5892,  7088,  4290, 12596,  5771,  5187, 12888,  7498,
       12921], dtype=int64)

Затем эти индексы можно использовать для извлечения значения слов из объекта count_vector , что можно сделать следующим образом:

for i in top_topic_words:
    print(count_vect.get_feature_names()[i])

В выходных данных вы должны увидеть следующие слова:

water
great
just
drink
sugar
good
flavor
taste
like
tea

Слова показывают, что первая тема может быть о чае.

Давайте напечатаем 10 слов с наибольшей вероятностью для всех пяти тем:

for i,topic in enumerate(LDA.components_):
    print(f'Top 10 words for topic #{i}:')
    print([count_vect.get_feature_names()[i] for i in topic.argsort()[-10:]])
    print('\n')

Вывод выглядит следующим образом:

Top 10 words for topic #0:
['water', 'great', 'just', 'drink', 'sugar', 'good', 'flavor', 'taste', 'like', 'tea']


Top 10 words for topic #1:
['br', 'chips', 'love', 'flavor', 'chocolate', 'just', 'great', 'taste', 'good', 'like']


Top 10 words for topic #2:
['just', 'drink', 'orange', 'sugar', 'soda', 'water', 'like', 'juice', 'product', 'br']


Top 10 words for topic #3:
['gluten', 'eat', 'free', 'product', 'like', 'dogs', 'treats', 'dog', 'br', 'food']


Top 10 words for topic #4:
['cups', 'price', 'great', 'like', 'amazon', 'good', 'br', 'product', 'cup', 'coffee']

Вывод показывает, что вторая тема может содержать отзывы о шоколадных конфетах и т. Д. Точно так же третья тема может снова содержать отзывы о газированных напитках или соках. Вы можете видеть, что есть несколько общих слов во всех категориях. Это потому, что есть несколько слов, которые используются почти для всех тем. Например, “хорошо”, “отлично”, “нравится” и т. Д.

В качестве заключительного шага мы добавим столбец в исходный фрейм данных, в котором будет храниться тема для текста. Для этого мы можем использовать метод LDA.transform() и передать ему нашу матрицу терминов документа. Этот метод назначит вероятность всех тем для каждого документа. Посмотрите на следующий код:

topic_values = LDA.transform(doc_term_matrix)
topic_values.shape

В выходных данных вы увидите (20000, 5), что означает, что каждый документ имеет 5 столбцов, где каждый столбец соответствует значению вероятности конкретной темы. Чтобы найти индекс темы с максимальным значением, мы можем вызвать метод argmax() и передать 1 в качестве значения параметра axis.

Следующий сценарий добавляет новый столбец для темы во фрейме данных и присваивает значение темы каждой строке в столбце:

reviews_datasets['Topic'] = topic_values.argmax(axis=1)

Давайте теперь посмотрим, как выглядит набор данных:

reviews_datasets.head()

Выход:

Вы можете увидеть новый столбец для этой темы в выходных данных.

Неотрицательная матричная факторизация (NMF)

В предыдущем разделе мы видели, как LDA можно использовать для тематического моделирования. В этом разделе мы увидим, как неотрицательная матричная факторизация может быть использована для тематического моделирования.

Неотрицательная матричная факторизация также является контролируемым методом обучения, который выполняет кластеризацию, а также уменьшение размерности. Он может быть использован в сочетании со схемой TF-IDF для выполнения тематического моделирования. В этом разделе мы увидим, как Python можно использовать для выполнения неотрицательной матричной факторизации для тематического моделирования.

NMF для тематического моделирования в Python

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

Начнем с импорта набора данных:

import pandas as pd
import numpy as np

reviews_datasets = pd.read_csv(r'E:\Datasets\Reviews.csv')
reviews_datasets = reviews_datasets.head(20000)
reviews_datasets.dropna()

В предыдущем разделе мы использовали countvectorizer, но в этом разделе мы будем использовать TfidfVectorizer, так как NMF работает с TFIDF. Мы создадим матрицу терминов документа с помощью TF IDF. Посмотрите на следующий сценарий:

from sklearn.feature_extraction.text import TfidfVectorizer

tfidf_vect = TfidfVectorizer(max_df=0.8, min_df=2, stop_words='english')
doc_term_matrix = tfidf_vect.fit_transform(reviews_datasets['Text'].values.astype('U'))

Как только матрица терминов документа будет сгенерирована, мы можем создать матрицу вероятностей, которая содержит вероятности всех слов в словаре для всех тем. Для этого мы можем использовать класс NMF из модуля sklearn.decomposition . Посмотрите на следующий сценарий:

from sklearn.decomposition import NMF

nmf = NMF(n_components=5, random_state=42)
nmf.fit(doc_term_matrix )

Как и в предыдущем разделе, давайте случайным образом получим 10 слов из нашего словаря:

import random

for i in range(10):
    random_id = random.randint(0,len(tfidf_vect.get_feature_names()))
    print(tfidf_vect.get_feature_names()[random_id])

В выходных данных вы увидите следующие слова:

safest
pith
ache
formula
fussy
frontier
burps
speaker
responsibility
dive

Далее мы получим вектор вероятности слов для первой темы и получим индексы десяти слов с наибольшими вероятностями:

first_topic = nmf.components_[0]
top_topic_words = first_topic.argsort()[-10:]

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

for i in top_topic_words:
    print(tfidf_vect.get_feature_names()[i])

Вывод выглядит следующим образом:

really
chocolate
love
flavor
just
product
taste
great
good
like

Слова для темы 1 показывают, что тема 1 может содержать отзывы о шоколадных конфетах. Давайте теперь напечатаем десять слов с наибольшей вероятностью для каждой из тем:

for i,topic in enumerate(nmf.components_):
    print(f'Top 10 words for topic #{i}:')
    print([tfidf_vect.get_feature_names()[i] for i in topic.argsort()[-10:]])
    print('\n')

Вывод приведенного выше скрипта выглядит следующим образом:

Top 10 words for topic #0:
['really', 'chocolate', 'love', 'flavor', 'just', 'product', 'taste', 'great', 'good', 'like']


Top 10 words for topic #1:
['like', 'keurig', 'roast', 'flavor', 'blend', 'bold', 'strong', 'cups', 'cup', 'coffee']


Top 10 words for topic #2:
['com', 'amazon', 'orange', 'switch', 'water', 'drink', 'soda', 'sugar', 'juice', 'br']


Top 10 words for topic #3:
['bags', 'flavor', 'drink', 'iced', 'earl', 'loose', 'grey', 'teas', 'green', 'tea']


Top 10 words for topic #4:
['old', 'love', 'cat', 'eat', 'treat', 'loves', 'dogs', 'food', 'treats', 'dog']

Слова для темы 1 показывают, что эта тема содержит отзывы о кофе. Аналогично, слова для темы 2 показывают, что она содержит отзывы о газированных напитках и соках. Тема 3 снова содержит отзывы о напитках. Наконец, тема 4 может содержать отзывы о животной пище, поскольку она содержит такие слова, как “кошка”, “собака”, “лакомство” и т. Д.

Следующий сценарий добавляет темы в набор данных и отображает первые пять строк:

topic_values = nmf.transform(doc_term_matrix)
reviews_datasets['Topic'] = topic_values.argmax(axis=1)
reviews_datasets.head()

Вывод приведенного выше кода выглядит следующим образом:

Как вы можете видеть, каждому обзору была назначена тема, которая была сгенерирована с помощью метода NMF.

Вывод

Тематическое моделирование-одно из самых востребованных направлений исследований в НЛП. Он используется для группировки больших объемов немаркированных текстовых данных. В этой статье были объяснены два подхода к тематическому моделированию. В этой статье мы увидели, как Латентное распределение Дирихле и Неотрицательная матричная факторизация могут быть использованы для тематического моделирования с помощью библиотек Python.