內容
1.簡介
2.先決條件 - 下載nltk停用詞和spacy模型
3.匯入包
4. LDA做什麼?
5.準備停用詞
6.匯入新聞組資料
7.刪除電子郵件和換行符
8.標記單詞和清理文字
9.建立Bigram和Trigram模型
10.刪除停用詞,製作雙字母組合詞和詞形變換
11.建立所需的詞典和語料庫主題建模
12.構建主題模型
13.檢視LDA模型中的主題
14.計算模型複雜度和一致性得分
15.視覺化主題 - 關鍵字
16.構建LDA Mallet模型
17.如何找到LDA的最佳主題數?
18.在每個句子中找到主要主題
19.為每個主題找到最具代表性的檔案
20.跨檔案分配主題
1.簡介
自然語言處理的主要應用之一是從大量文字中自動提取人們正在討論的主題。大量文字的一些示例可以是來自社交媒體的饋送,酒店的客戶評論,電影等,使用者反饋,新聞報導,客戶投訴的電子郵件等。
瞭解人們在談論什麼並理解他們的問題和意見對於企業,管理者和政治活動來說非常有價值。並且很難人工閱讀如此大資料量的文字並識別主題。
因此,需要一種自動演算法,該演算法可以讀取文字文件並自動輸出所討論的主題。
在本教程中,我們將採用'20新聞組'資料集的真實示例,並使用LDA提取自然討論的主題。
我將使用Gensim包中的Latent Dirichlet Allocation(LDA)以及Mallet的實現(通過Gensim)。Mallet有效地實現了LDA。眾所周知,它可以更快地執行並提供更好的主題隔離。
我們還將提取每個主題的數量和百分比貢獻,以瞭解主題的重要性。
讓我們開始!
使用Gensim在Python中進行主題建模。攝影:Jeremy Bishop。
2.先決條件 - 下載nltk停用詞和spacy模型
我們需要來自NLTK的stopwords
和spacy的en
模型進行文字預處理。稍後,我們將使用spacy模型進行詞形還原。
詞形還原只不過是將一個詞轉換為詞根。例如:“機器”這個詞的引理是“機器”。同樣,'走路' - >'走路','老鼠' - >'老鼠'等等。
# Run in python console
import nltk; nltk.download('stopwords')
# Run in terminal or command prompt
python3 -m spacy download en複製程式碼
3.匯入包
在本教程中使用的核心包re
,gensim
,spacy
和pyLDAvis
。除此之外,我們還將使用matplotlib
,numpy
以及pandas
資料處
理和視覺化。讓我們匯入它們。
import re
import numpy as np
import pandas as pd
from pprint import pprint
# Gensim
import gensim
import gensim.corpora as corpora
from gensim.utils import simple_preprocess
from gensim.models import CoherenceModel
# spacy for lemmatization
import spacy
# Plotting tools
import pyLDAvis
import pyLDAvis.gensim # don't skip this
import matplotlib.pyplot as plt
%matplotlib inline
# Enable logging for gensim - optional
import logging
logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.ERROR)
import warnings
warnings.filterwarnings("ignore",category=DeprecationWarning)複製程式碼
4. LDA做什麼?
LDA的主題建模方法是將每個文件視為一定比例的主題集合。並且每個主題作為關鍵字的集合,再次以一定比例構成主題。
一旦您為演算法提供了主題數量,它就會重新排列文件中的主題分佈和主題內的關鍵字分佈,以獲得主題 - 關鍵字分佈的良好組合。
當我說主題時,它實際上是什麼以及如何表示?
一個主題只不過是典型代表的主導關鍵詞集合。只需檢視關鍵字,您就可以確定主題的內容。
以下是獲得良好隔離主題的關鍵因素:
文字處理的質量。
文字談論的各種主題。
主題建模演算法的選擇。
提供給演算法的主題數量。
演算法引數調整。
5.準備關鍵詞
我們已經下載了停用詞。讓我們匯入它們並使其可用stop_words
。
# NLTK Stop words
from nltk.corpus import stopwords
stop_words = stopwords.words('english')
stop_words.extend(['from', 'subject', 're', 'edu', 'use'])複製程式碼
6.匯入新聞組資料
我們將使用20-Newsgroups資料集進行此練習。此版本的資料集包含來自20個不同主題的大約11k個新聞組帖子。這可以作為newsgroups.json使用。
這是使用匯入的pandas.read_json
,結果資料集有3列,如圖所示。
# Import Dataset
df = pd.read_json('https://raw.githubusercontent.com/selva86/datasets/master/newsgroups.json')
print(df.target_names.unique())
df.head()複製程式碼
['rec.autos' 'comp.sys.mac.hardware' 'rec.motorcycles' 'misc.forsale'
'comp.os.ms-windows.misc' 'alt.atheism' 'comp.graphics'
'rec.sport.baseball' 'rec.sport.hockey' 'sci.electronics' 'sci.space'
'talk.politics.misc' 'sci.med' 'talk.politics.mideast'
'soc.religion.christian' 'comp.windows.x' 'comp.sys.ibm.pc.hardware'
'talk.politics.guns' 'talk.religion.misc' 'sci.crypt']複製程式碼
複製程式碼
20個新聞組資料集
7.刪除電子郵件和換行符
正如您所看到的那樣,有許多電子郵件,換行符和額外空間非常分散注意力。讓我們使用正規表示式擺脫它們。
# Convert to list
data = df.content.values.tolist()
# Remove Emails
data = [re.sub('\S*@\S*\s?', '', sent) for sent in data]
# Remove new line characters
data = [re.sub('\s+', ' ', sent) for sent in data]
# Remove distracting single quotes
data = [re.sub("\'", "", sent) for sent in data]
pprint(data[:1])
複製程式碼
['From: (wheres my thing) Subject: WHAT car is this!? Nntp-Posting-Host: '
'rac3.wam.umd.edu Organization: University of Maryland, College Park Lines: '
'15 I was wondering if anyone out there could enlighten me on this car I saw '
'the other day. It was a 2-door sports car, looked to be from the late 60s/ '
'early 70s. It was called a Bricklin. The doors were really small. In '
'addition, the front bumper was separate from the rest of the body. This is '
'all I know. (..truncated..)]
複製程式碼
刪除電子郵件和額外空格後,文字仍然看起來很亂。它尚未準備好讓LDA消費。您需要通過標記化將每個句子分解為單詞列表,同時清除過程中的所有雜亂文字。
Gensim對此很有幫助simple_preprocess
。
8.標記單詞和清理文字
讓我們將每個句子標記為一個單詞列表,完全刪除標點符號和不必要的字元。
Gensim對此很有幫助simple_preprocess()
。此外,我已經設定deacc=True
刪除標點符號。
def sent_to_words(sentences):
for sentence in sentences:
yield(gensim.utils.simple_preprocess(str(sentence), deacc=True)) # deacc=True removes punctuations
data_words = list(sent_to_words(data))
print(data_words[:1])
複製程式碼
[['from', 'wheres', 'my', 'thing', 'subject', 'what', 'car', 'is', 'this', 'nntp', 'posting', 'host', 'rac', 'wam', 'umd', 'edu', 'organization', 'university', 'of', 'maryland', 'college', 'park', 'lines', 'was', 'wondering', 'if', 'anyone', 'out', 'there', 'could', 'enlighten', 'me', 'on', 'this', 'car', 'saw', 'the', 'other', 'day', (..truncated..))]]
複製程式碼
9.建立Bigram和Trigram模型
Bigrams是文件中經常出現的兩個詞。Trigrams是經常出現3個單詞。
我們示例中的一些示例是:'front_bumper','oil_leak','maryland_college_park'等。
Gensim的Phrases
模型可以構建和實現bigrams,trigrams,quadgrams等。兩個重要的論點Phrases
是min_count
和threshold
。這些引數的值越高,將單片語合成雙字母組的難度就越大。
# Build the bigram and trigram models
bigram = gensim.models.Phrases(data_words, min_count=5, threshold=100) # higher threshold fewer phrases.
trigram = gensim.models.Phrases(bigram[data_words], threshold=100)
# Faster way to get a sentence clubbed as a trigram/bigram
bigram_mod = gensim.models.phrases.Phraser(bigram)
trigram_mod = gensim.models.phrases.Phraser(trigram)
# See trigram example
print(trigram_mod[bigram_mod[data_words[0]]])
複製程式碼
['from', 'wheres', 'my', 'thing', 'subject', 'what', 'car', 'is', 'this', 'nntp_posting_host', 'rac_wam_umd_edu', 'organization', 'university', 'of', 'maryland_college_park', 'lines', 'was', 'wondering', 'if', 'anyone', 'out', 'there', 'could', 'enlighten', 'me', 'on', 'this', 'car', 'saw', 'the', 'other', 'day', 'it', 'was', 'door', 'sports', 'car', 'looked', 'to', 'be', 'from', 'the', 'late', 'early', 'it', 'was', 'called', 'bricklin', 'the', 'doors', 'were', 'really', 'small', 'in', 'addition', 'the', 'front_bumper' (..truncated..)]
複製程式碼
10.刪除停用詞,製作Bigrams和Lemmatize
雙詞模型準備就緒。讓我們定義函式來刪除停用詞,製作雙字母組合和詞形還原並按順序呼叫它們。
# Define functions for stopwords, bigrams, trigrams and lemmatization
def remove_stopwords(texts):
return [[word for word in simple_preprocess(str(doc)) if word not in stop_words] for doc in texts]
def make_bigrams(texts):
return [bigram_mod[doc] for doc in texts]
def make_trigrams(texts):
return [trigram_mod[bigram_mod[doc]] for doc in texts]
def lemmatization(texts, allowed_postags=['NOUN', 'ADJ', 'VERB', 'ADV']):
"""https://spacy.io/api/annotation"""
texts_out = []
for sent in texts:
doc = nlp(" ".join(sent))
texts_out.append([token.lemma_ for token in doc if token.pos_ in allowed_postags])
return texts_out
複製程式碼
我們按順序呼叫這些函式。
# Remove Stop Words
data_words_nostops = remove_stopwords(data_words)
# Form Bigrams
data_words_bigrams = make_bigrams(data_words_nostops)
# Initialize spacy 'en' model, keeping only tagger component (for efficiency)
# python3 -m spacy download en
nlp = spacy.load('en', disable=['parser', 'ner'])
# Do lemmatization keeping only noun, adj, vb, adv
data_lemmatized = lemmatization(data_words_bigrams, allowed_postags=['NOUN', 'ADJ', 'VERB', 'ADV'])
print(data_lemmatized[:1])
複製程式碼
[['where', 's', 'thing', 'car', 'nntp_post', 'host', 'rac_wam', 'umd', 'organization', 'university', 'maryland_college', 'park', 'line', 'wonder', 'anyone', 'could', 'enlighten', 'car', 'see', 'day', 'door', 'sport', 'car', 'look', 'late', 'early', 'call', 'bricklin', 'door', 'really', 'small', 'addition', 'front_bumper', 'separate', 'rest', 'body', 'know', 'anyone', 'tellme', 'model', 'name', 'engine', 'spec', 'year', 'production', 'car', 'make', 'history', 'whatev', 'info', 'funky', 'look', 'car', 'mail', 'thank', 'bring', 'neighborhood', 'lerxst']]複製程式碼
11.建立主題建模所需的詞典和語料庫
LDA主題模型的兩個主要輸入是字典(id2word
)和語料庫。讓我們創造它們。
# Create Dictionary
id2word = corpora.Dictionary(data_lemmatized)
# Create Corpus
texts = data_lemmatized
# Term Document Frequency
corpus = [id2word.doc2bow(text) for text in texts]
# View
print(corpus[:1])
複製程式碼
[[(0, 1), (1, 2), (2, 1), (3, 1), (4, 1), (5, 1), (6, 5), (7, 1), (8, 1), (9, 2), (10, 1), (11, 1), (12, 1), (13, 1), (14, 1), (15, 1), (16, 1), (17, 1), (18, 1), (19, 1), (20, 1), (21, 1), (22, 2), (23, 1), (24, 1), (25, 1), (26, 1), (27, 1), (28, 1), (29, 1), (30, 1), (31, 1), (32, 1), (33, 1), (34, 1), (35, 1), (36, 1), (37, 1), (38, 1), (39, 1), (40, 1), (41, 1), (42, 1), (43, 1), (44, 1), (45, 1), (46, 1), (47, 1), (48, 1), (49, 1), (50, 1)]]複製程式碼
Gensim為文件中的每個單詞建立一個唯一的ID。上面顯示的產生的語料庫是(word_id,word_frequency)的對映。
例如,上面的(0,1)暗示,單詞id 0在第一個文件中出現一次。同樣,單詞id 1出現兩次,依此類推。
這用作LDA模型的輸入。
如果要檢視給定id對應的單詞,請將id作為鍵傳遞給字典。
id2word[0]複製程式碼
'addition'複製程式碼
或者,您可以看到語料庫本身的人類可讀形式。
# Human readable format of corpus (term-frequency)
[[(id2word[id], freq) for id, freq in cp] for cp in corpus[:1]]複製程式碼
[[('addition', 1), ('anyone', 2), ('body', 1), ('bricklin', 1), ('bring', 1), ('call', 1), ('car', 5), ('could', 1), ('day', 1), ('door', 2), ('early', 1), ('engine', 1), ('enlighten', 1), ('front_bumper', 1), ('maryland_college', 1), (..truncated..)]]複製程式碼
好吧,讓我們重新回到正軌,進行下一步:構建主題模型。
12.構建主題模型
我們擁有培訓LDA模型所需的一切。除語料庫和字典外,您還需要提供主題數量。
除此之外,alpha
還有eta
影響主題稀疏性的超引數。根據Gensim文件,預設為1.0 / num_topics之前。
chunksize
是每個訓練塊中使用的文件數。update_every
確定應更新模型引數的頻率,以及passes
培訓通過的總數。
# Build LDA model
lda_model = gensim.models.ldamodel.LdaModel(corpus=corpus,
id2word=id2word,
num_topics=20,
random_state=100,
update_every=1,
chunksize=100,
passes=10,
alpha='auto',
per_word_topics=True)
複製程式碼
13.檢視LDA模型中的主題
上述LDA模型由20個不同的主題構建,其中每個主題是關鍵字的組合,並且每個關鍵字對主題貢獻一定的權重。
您可以看到每個主題的關鍵字以及每個關鍵字的權重(重要性),lda_model.print_topics()
如下所示。
# Print the Keyword in the 10 topics
pprint(lda_model.print_topics())
doc_lda = lda_model[corpus]複製程式碼
[(0,
'0.016*"car" + 0.014*"power" + 0.010*"light" + 0.009*"drive" + 0.007*"mount" '
'+ 0.007*"controller" + 0.007*"cool" + 0.007*"engine" + 0.007*"back" + '
'0.006*"turn"'),
(1,
'0.072*"line" + 0.066*"organization" + 0.037*"write" + 0.032*"article" + '
'0.028*"university" + 0.027*"nntp_post" + 0.026*"host" + 0.016*"reply" + '
'0.014*"get" + 0.013*"thank"'),
(2,
'0.017*"patient" + 0.011*"study" + 0.010*"slave" + 0.009*"wing" + '
'0.009*"disease" + 0.008*"food" + 0.008*"eat" + 0.008*"pain" + '
'0.007*"treatment" + 0.007*"syndrome"'),
(3,
'0.013*"key" + 0.009*"use" + 0.009*"may" + 0.007*"public" + 0.007*"system" + '
'0.007*"order" + 0.007*"government" + 0.006*"state" + 0.006*"provide" + '
'0.006*"law"'),
(4,
'0.568*"ax" + 0.007*"rlk" + 0.005*"tufts_university" + 0.004*"ei" + '
'0.004*"m" + 0.004*"vesa" + 0.004*"differential" + 0.004*"chz" + 0.004*"lk" '
'+ 0.003*"weekly"'),
(5,
'0.029*"player" + 0.015*"master" + 0.015*"steven" + 0.009*"tor" + '
'0.009*"van" + 0.008*"king" + 0.008*"scripture" + 0.007*"cal" + '
'0.007*"helmet" + 0.007*"det"'),
(6,
'0.028*"system" + 0.020*"problem" + 0.019*"run" + 0.018*"use" + 0.016*"work" '
'+ 0.015*"do" + 0.013*"window" + 0.013*"driver" + 0.013*"bit" + 0.012*"set"'),
(7,
'0.017*"israel" + 0.011*"israeli" + 0.010*"war" + 0.010*"armenian" + '
'0.008*"kill" + 0.008*"soldier" + 0.008*"attack" + 0.008*"government" + '
'0.007*"lebanese" + 0.007*"greek"'),
(8,
'0.018*"money" + 0.018*"year" + 0.016*"pay" + 0.012*"car" + 0.010*"drug" + '
'0.010*"president" + 0.009*"rate" + 0.008*"face" + 0.007*"license" + '
'0.007*"american"'),
(9,
'0.028*"god" + 0.020*"evidence" + 0.018*"christian" + 0.012*"believe" + '
'0.012*"reason" + 0.011*"faith" + 0.009*"exist" + 0.008*"bible" + '
'0.008*"religion" + 0.007*"claim"'),
(10,
'0.030*"physical" + 0.028*"science" + 0.012*"direct" + 0.012*"st" + '
'0.012*"scientific" + 0.009*"waste" + 0.009*"jeff" + 0.008*"cub" + '
'0.008*"brown" + 0.008*"msg"'),
(11,
'0.016*"wire" + 0.011*"keyboard" + 0.011*"md" + 0.009*"pm" + 0.008*"air" + '
'0.008*"input" + 0.008*"fbi" + 0.007*"listen" + 0.007*"tube" + '
'0.007*"koresh"'),
(12,
'0.016*"motif" + 0.014*"serial_number" + 0.013*"son" + 0.013*"father" + '
'0.011*"choose" + 0.009*"server" + 0.009*"event" + 0.009*"value" + '
'0.007*"collin" + 0.007*"prediction"'),
(13,
'0.098*"_" + 0.043*"max" + 0.015*"dn" + 0.011*"cx" + 0.009*"eeg" + '
'0.008*"gateway" + 0.008*"c" + 0.005*"mu" + 0.005*"mr" + 0.005*"eg"'),
(14,
'0.024*"book" + 0.009*"april" + 0.007*"group" + 0.007*"page" + '
'0.007*"new_york" + 0.007*"iran" + 0.006*"united_state" + 0.006*"author" + '
'0.006*"include" + 0.006*"club"'),
(15,
'0.020*"would" + 0.017*"say" + 0.016*"people" + 0.016*"think" + 0.014*"make" '
'+ 0.014*"go" + 0.013*"know" + 0.012*"see" + 0.011*"time" + 0.011*"get"'),
(16,
'0.026*"file" + 0.017*"program" + 0.012*"window" + 0.012*"version" + '
'0.011*"entry" + 0.011*"software" + 0.011*"image" + 0.011*"color" + '
'0.010*"source" + 0.010*"available"'),
(17,
'0.027*"game" + 0.027*"team" + 0.020*"year" + 0.017*"play" + 0.016*"win" + '
'0.010*"good" + 0.009*"season" + 0.008*"fan" + 0.007*"run" + 0.007*"score"'),
(18,
'0.036*"drive" + 0.024*"card" + 0.020*"mac" + 0.017*"sale" + 0.014*"cpu" + '
'0.010*"price" + 0.010*"disk" + 0.010*"board" + 0.010*"pin" + 0.010*"chip"'),
(19,
'0.030*"space" + 0.010*"sphere" + 0.010*"earth" + 0.009*"item" + '
'0.008*"launch" + 0.007*"moon" + 0.007*"mission" + 0.007*"nasa" + '
複製程式碼
'0.007*"orbit" + 0.006*"research"')]怎麼解釋這個?複製程式碼
主題0表示為_0.016
這意味著貢獻這個主題的前10個關鍵詞是:'car','power','light'等等,主題0上單詞'car'的權重是0.016。
權重反映了關鍵字對該主題的重要程度。
看看這些關鍵詞,您能猜出這個主題是什麼嗎?您可以將其概括為“汽車”或“汽車”。
同樣,您是否可以瀏覽剩餘的主題關鍵字並判斷主題是什麼?
從關鍵字推斷主題
14.計算模型複雜度和一致性分數
模型複雜度和主題一致性提供了一種方便的方法來判斷給定主題模型的好壞程度。根據我的經驗,特別是主題一致性得分更有幫助。
# Compute Perplexity
print('\nPerplexity: ', lda_model.log_perplexity(corpus)) # a measure of how good the model is. lower the better.
# Compute Coherence Score
coherence_model_lda = CoherenceModel(model=lda_model, texts=data_lemmatized, dictionary=id2word, coherence='c_v')
coherence_lda = coherence_model_lda.get_coherence()
print('\nCoherence Score: ', coherence_lda)
複製程式碼
Perplexity: -8.86067503009
Coherence Score: 0.532947587081複製程式碼
你有一個0.53的一致性得分。
15.視覺化主題 - 關鍵字
現在已經構建了LDA模型,下一步是檢查生成的主題和關聯的關鍵字。沒有比pyLDAvis包的互動式圖表更好的工具,並且設計為與jupyter notebook一起使用。
# Visualize the topicspyLDAvis.enable_notebook()vis = pyLDAvis.gensim.prepare(lda_model, corpus, id2word)vis複製程式碼
PYLDAVIS輸出
那麼如何推斷pyLDAvis的輸出呢?
左側圖中的每個氣泡代表一個主題。氣泡越大,該主題就越普遍。
一個好的主題模型將在整個圖表中分散相當大的非重疊氣泡,而不是聚集在一個象限中。
具有太多主題的模型通常會有許多重疊,小尺寸的氣泡聚集在圖表的一個區域中。
好吧,如果將游標移動到其中一個氣泡上,右側的單詞和條形將會更新。這些單詞是構成所選主題的顯著關鍵字。
我們已經成功構建了一個好的主題模型。
鑑於我們之前對文件中自然主題數量的瞭解,找到最佳模型非常簡單。
其餘部分下篇繼續。。。
檢視英文原文:https://www.machinelearningplus.com/nlp/topic-modeling-gensim-python/
公眾號:銀河系1號
聯絡郵箱:public@space-explore.com
(未經同意,請勿轉載)