Тематическое моделирование - это тип статистического моделирования для выявления абстрактных тем, которые встречаются в коллекции документов. Скрытое размещение Дирихле (LDA) является примером тематической модели и используется для классификации текста в документе по определенной теме. Он строит тему для модели документа и слова для модели темы, смоделированные как распределения Дирихле.

Здесь мы собираемся применить LDA к набору документов и разделить их на темы. Давайте начнем!

Данные

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

import pandas as pd
data = pd.read_csv('abcnews-date-text.csv', error_bad_lines=False);
data_text = data[['headline_text']]
data_text['index'] = data_text.index
documents = data_text

Взгляните на данные.

print(len(documents))
print(documents[:5])

1048575

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

Выполним следующие действия:

  • Токенизация: разделите текст на предложения, а предложения - на слова. Слова в нижнем регистре и удаление знаков препинания.
  • Слова, содержащие менее 3 символов, удаляются.
  • Все игнорируемые слова удалены.
  • Слова лемматизируются - слова от третьего лица заменяются на первое, а глаголы в прошедшем и будущем времени заменяются на настоящее.
  • Слова выделены - слова сокращаются до их корневой формы.

Загрузка библиотек gensim и nltk

import gensim
from gensim.utils import simple_preprocess
from gensim.parsing.preprocessing import STOPWORDS
from nltk.stem import WordNetLemmatizer, SnowballStemmer
from nltk.stem.porter import *
import numpy as np
np.random.seed(2018)
import nltk
nltk.download('wordnet')

[nltk_data] Загрузка пакета wordnet в
[nltk_data] C: \ Users \ SusanLi \ AppData \ Roaming \ nltk_data…
[nltk_data] Пакет wordnet уже обновлен!

Верно

Напишите функцию для выполнения шагов лемматизации и предварительной обработки набора данных.

def lemmatize_stemming(text):
    return stemmer.stem(WordNetLemmatizer().lemmatize(text, pos='v'))
def preprocess(text):
    result = []
    for token in gensim.utils.simple_preprocess(text):
        if token not in gensim.parsing.preprocessing.STOPWORDS and len(token) > 3:
            result.append(lemmatize_stemming(token))
    return result

Выберите документ для предварительного просмотра после предварительной обработки.

doc_sample = documents[documents['index'] == 4310].values[0][0]
print('original document: ')
words = []
for word in doc_sample.split(' '):
    words.append(word)
print(words)
print('\n\n tokenized and lemmatized document: ')
print(preprocess(doc_sample))

исходный документ:

[«дождь», «помогает», «тушить», «лесные пожары»]

токенизированный и лемматизированный документ:

[«дождь», «помощь», «увлажнение», «бушфир»]

Это сработало!

Предварительно обработайте текст заголовка, сохранив результаты как «loaded_docs»

processed_docs = documents['headline_text'].map(preprocess)
processed_docs[:10]

Мешок слов в наборе данных

Создайте словарь из «processing_docs», в котором будет указано, сколько раз слово встречается в обучающем наборе.

dictionary = gensim.corpora.Dictionary(processed_docs)
count = 0
for k, v in dictionary.iteritems():
    print(k, v)
    count += 1
    if count > 10:
        break

0 трансляций

1 сообщество

2 решающих

3 лицензии

4 награды

5 клеветы

6 умов

7 звонков

8 инфраструктур

9 защиты

10 саммит

Gensim filter_extremes

Отфильтровать токены, которые появляются в

  • менее 15 документов (абсолютное количество) или
  • более 0,5 документа (часть от общего размера корпуса, а не абсолютное количество).
  • после двух вышеуказанных шагов оставьте только первые 100000 наиболее часто используемых токенов.
dictionary.filter_extremes(no_below=15, no_above=0.5, keep_n=100000)

Gensim doc2bow

Для каждого документа мы создаем словарь, в котором указывается, сколько
слов и сколько раз они встречаются. Сохраните это в "bow_corpus", затем проверьте ранее выбранный нами документ.

bow_corpus = [dictionary.doc2bow(doc) for doc in processed_docs]
bow_corpus[4310]

[(76, 1), (112, 1), (483, 1), (3998, 1)]

Предварительный просмотр пакета слов для нашего образца предварительно обработанного документа.

bow_doc_4310 = bow_corpus[4310]
for i in range(len(bow_doc_4310)):
    print("Word {} (\"{}\") appears {} time.".format(bow_doc_4310[i][0], 
                                               dictionary[bow_doc_4310[i][0]], 
bow_doc_4310[i][1]))

Слово 76 («бушфир») появляется 1 раз.

Слово 112 («справка») появляется 1 раз.

Слово 483 («дождь») появляется 1 раз.

Word 3998 («увлажняющий») появляется 1 раз.

TF-IDF

Создайте объект модели tf-idf с помощью models.TfidfModel на «bow_corpus» и сохраните его в «tfidf», затем примените преобразование ко всему корпусу и назовите его «corpus_tfidf». Наконец, мы предварительно просматриваем оценки TF-IDF для нашего первого документа.

from gensim import corpora, models
tfidf = models.TfidfModel(bow_corpus)
corpus_tfidf = tfidf[bow_corpus]
from pprint import pprint
for doc in corpus_tfidf:
    pprint(doc)
    break

[(0, 0.5907943557842693),

(1, 0.3900924708457926),

(2, 0.49514546614015836),

(3, 0.5036078441840635)]

Запуск LDA с помощью пакета слов

Обучите нашу модель lda с помощью gensim.models.LdaMulticore и сохраните ее в lda_model.

lda_model = gensim.models.LdaMulticore(bow_corpus, num_topics=10, id2word=dictionary, passes=2, workers=2)

Для каждой темы мы исследуем слова, встречающиеся в этой теме, и их относительный вес.

for idx, topic in lda_model.print_topics(-1):
    print('Topic: {} \nWords: {}'.format(idx, topic))

Можете ли вы различать разные темы, используя слова в каждой теме и их соответствующие веса?

Запуск LDA с использованием TF-IDF

lda_model_tfidf = gensim.models.LdaMulticore(corpus_tfidf, num_topics=10, id2word=dictionary, passes=2, workers=4)
for idx, topic in lda_model_tfidf.print_topics(-1):
    print('Topic: {} Word: {}'.format(idx, topic))

Опять же, можете ли вы различать разные темы, используя слова в каждой теме и их соответствующие веса?

Оценка эффективности путем классификации образца документа с использованием модели LDA Bag of Words

Мы проверим, где будет классифицироваться наш тестовый документ.

processed_docs[4310]

[«дождь», «помощь», «увлажнение», «бушфир»]

for index, score in sorted(lda_model[bow_corpus[4310]], key=lambda tup: -1*tup[1]):
    print("\nScore: {}\t \nTopic: {}".format(score, lda_model.print_topic(index, 10)))

Наш тестовый документ имеет наибольшую вероятность быть частью темы, назначенной нашей моделью, а именно точной классификации.

Оценка эффективности путем классификации образца документа с использованием модели LDA TF-IDF.

for index, score in sorted(lda_model_tfidf[bow_corpus[4310]], key=lambda tup: -1*tup[1]):
    print("\nScore: {}\t \nTopic: {}".format(score, lda_model_tfidf.print_topic(index, 10)))

Наш тестовый документ имеет наибольшую вероятность быть частью темы, назначенной нашей моделью, а именно точной классификации.

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

unseen_document = 'How a Pentagon deal became an identity crisis for Google'
bow_vector = dictionary.doc2bow(preprocess(unseen_document))
for index, score in sorted(lda_model[bow_vector], key=lambda tup: -1*tup[1]):
    print("Score: {}\t Topic: {}".format(score, lda_model.print_topic(index, 5)))

Исходный код можно найти на Github. Я с нетерпением жду любых отзывов или вопросов.

Ссылка:

Udacity - НЛП