​用 Python 和 Gensim 庫進行文本主題識別

2022-04-25     CDA數據分析師

原標題:​用 Python 和 Gensim 庫進行文本主題識別

作者:雲朵君

來源:數據STUDIO

主題識別是一種在大量文本中識別隱藏主題的方法。潛在狄利克雷分配 (LDA) 技術是一種常見的主題建模算法,在 Python 的 Gensim 包中有很好的實現(推薦閱讀 強大的 Gensim 庫用於 NLP 文本分析 )。 問題是確定如何提取獨特、重要的高質量主題。這取決於文本準備質量和確定理想主題數量的方法。本文中雲朵君將和大家一起嘗試解決這兩個問題。

寫在前面

企業、行政人員和政治競選活動從了解人們在談論什麼、了解他們的關注和觀點中獲益匪淺。就我個人而言,閱讀如此龐大的書籍並整理主題是很困難的。

因此,我們需要一個自動化系統來閱讀文本文檔並自動輸出提到的主題。

在本中,將使用LDA 從 20Newsgroup 數據集中提取主題的實戰案例。

主題識別的基礎知識 詞形還原

將單詞簡化為詞根或詞幹稱為詞形還原。

首先實例化 WordNetLemmatizer 。調用 '.lemmatize' 方法來構建一個名為 LEM 的tokens 的新列表。然後調用 Counter 類並生成一個名為 bag_words 的新 Counter,最後輸出六個最有可能的主題。

lemmatizer = WordNetLemmatizer

lem_tokens = [lemmatizer.lemmatize(t) fort instopwords_removed]

bag_words = Counter(lem_tokens)

print(bag_words.most_common( 6))

Gensim 和 LDA

LDA 全稱為 Latent Dirichlet Allocation,中文為潛在狄利克雷分配。

Gensim 是一個可以創建和查詢語料庫的開源自然語言處理 (NLP) 庫。它通過構建詞嵌入(embeddings)或向量(vectors)來進行操作,然後將其用於對主題進行建模。

深度學習算法用於構建稱為詞向量的詞的多維數學表示。它們提供有關語料庫中術語之間關係的信息。例如, 「印度」和「新德里」這兩個詞之間的距離可能與 「中國」和「北京」這兩個詞之間的距離相當,因為它們是「國家-首都」向量。

Gensim 用於創建和查詢語料庫

之前雲朵君和大家一起學習了gensim的相關知識,本文將和大家一起動手開發第一個 gensim 詞典和語料庫!

這些數據結構將查看文檔集中的文字趨勢和其他有趣的主題。首先,我們導入了一些更混亂的 Wikipedia 文章,這些文章經過預處理,將所有單詞小寫、標記化並刪除停用詞和標點符號。然後這些文件被保存為文章,這是一個文檔標記的列表。在創建 gensim 詞彙和語料庫之前,需要做一些初步工作。

Gensim 的詞袋

現在,使用新的gensim語料庫和字典來查看每個文檔中和所有文檔中最常使用的術語。你可以在字典里查這些術語。

可以使用 defaultdict 創建一個字典,將默認值賦給不存在的鍵。我們可以使用int形參確保任何不存在的鍵被自動分配一個默認值0。

LDA 的文檔術語矩陣

創建LDA模型後,我們將在文檔術語矩陣上訓練LDA模型對象。必須指定主題的數量和字典。我們可能會將主題的數量限制在2到3個,因為我們有一個只有9個文檔的小語料庫。

數據集

本次案例使用可以 從 sklearn 下載的 20Newsgroup 數據集

fromsklearn.datasets importfetch_20newsgroups

newsgroups_train = fetch_20newsgroups(subset= 'train',

shuffle = True)

newsgroups_test = fetch_20newsgroups(subset= 'test',

shuffle = True)

該數據集中的新聞已被分類為關鍵主題。

print(list(newsgroups_train.target_names))

從結果中可以看到它涵蓋了各種主題,例如科學、政治、體育、宗教和技術。我們看一些最近新聞的例子。

newsgroups_train.data[: 2]

數據預處理 具體步驟如下:

使用tokenization標記化將文本拆分為句子,將句子拆分為單詞。

刪除所有標點符號和將所有單詞轉換為小寫單詞。

過濾少於三個字符的單詞。

刪除所有停用詞。

將名詞進行詞形還原,因此第三人稱詞被轉換為第一人稱,過去和將來時態動詞被改變為現在時態。

將它們被簡化成最簡單的詞根形式。

下載 nltk 停用詞和必要的包。

importgensim

fromgensim.utils importsimple_preprocess

fromgensim.parsing.preprocessing importSTOPWORDS

fromnltk.stem importWordNetLemmatizer, SnowballStemmer

fromnltk.stem.porter import*

importnumpy asnp

np.random.seed( 400)

importnltk

nltk.download( 'wordnet')

詞形還原器

在開始預處理數據之前,看一個詞形還原的例子。如果我們將 「Gone」 這個詞進行詞形還原,會發生什麼?

以將過去時轉換為現在時為例。

print(WordNetLemmatizer.lemmatize( 'gone', pos = 'v'))

go

詞根提取示例。試著往詞根分析器輸入幾句話,看看輸出結果是什麼。

importpandas aspd

stemmer = SnowballStemmer( "english")

original_words = [ 'sings', 'classes', 'dies', 'mules', 'denied', 'played', 'agreement', 'owner',

'humbled', 'sized', 'meeting', 'stating', 'siezing', 'itemization', 'sensational',

'traditional', 'reference', 'colon', 'plotting']

singles = [stemmer.stem(plural) forplural inoriginal_words]

pd.DataFrame(data={ 'original word':original_words, 'stemmed':singles })

接下來編寫一個函數來運行整個數據集的預處理階段。

deflemmatize_stemming(text):

returnstemmer.stem(WordNetLemmatizer.lemmatize(text, pos= 'v'))

# Tokenize and lemmatize

defpreprocess(text):

result=[]

fortoken ingensim.utils.simple_preprocess(text) :

iftoken notingensim.parsing.preprocessing.STOPWORDS andlen(token) > 3:

result.append(lemmatize_stemming(token))

returnresul

現在預覽預處理後的文檔,並得到 Tokenized 和 lemmized 文檔。

document_number = 50

doc_sample = 'Sara did not like to read. She was not very good at it.'

print( "Original document: ")

words = []

forword indoc_sample.split( ' '):

words.append(word)

print(words)

print( "nnTokenized and lemmatized document: ")

print(preprocess(doc_sample))

Original document:

['Sara', 'did', 'not', 'like', 'to', 'read.',

'She', 'was', 'not', 'very', 'good', 'at', 'it.']

nnTokenized and lemmatized document:

['sara', 'like', 'read', 'good']

開始預處理所有的新聞標題之前。需要仔細地檢查訓練示例中的文檔列表。

processed_docs = []

fordoc innewsgroups_train.data:

processed_docs.append(preprocess(doc))

# Preview 'processed_docs

print(processed_docs[: 2])

現在將使用"processed_docs"來構建一個字典,其中包含每個單詞在訓練集中出現的次數。為此,將其稱為"dictionary"並將處理後的文檔提供給 gensim.corpora.Dictionary[1] 。

創建詞袋 從文本中創建一個詞袋

使用 gensim.corpora.Dictionary ,從 "processed_docs" 創建一個字典,其中包含一個術語在訓練集中出現的次數,並將其命名為 "dictionary" 。

dictionary = gensim.corpora.Dictionary(processed_docs)

首先檢查字典是否被創建。

count = 0

fork, v indictionary.iteritems:

print(k, v)

count += 1

ifcount > 10:

break

0 addit1 bodi2 bricklin3 bring4 bumper5

call6 colleg7 door8 earli9 engin10 enlighten

過濾極值

刪除列表中出現如下所有tokens。

  • 大於沒有以上文檔的(絕對數量)或小於沒有以下文檔的(絕對數量)(總語料庫大小的分數,而不是絕對數量)。

  • 只保留(1)和(2)之後的第一個保留n個最常見的標記。(如果為None則保留所有標記)。

大於沒有以上文檔的(絕對數量)或小於沒有以下文檔的(絕對數量)(總語料庫大小的分數,而不是絕對數量)。

只保留(1)和(2)之後的第一個保留n個最常見的標記。(如果為None則保留所有標記)。

no_above= 0.1,

keep_n= 100000)

還可以過濾掉不經常或經常出現的單詞。

現在使用生成的字典對象將每個預處理頁面轉換成一個詞袋。即為每個文檔建立一個字典,存儲有多少單詞以及這些單詞出現了多少次。

Gensim doc2bow doc2bow(document)

將文檔(單詞列表)轉換為word格式的2元組列表(token id token計數)。每個單詞都是標準化和標記化的字符串(Unicode或utf8-encoded)。在調用此函數之前,對文檔中的單詞應用標記化、詞幹分析和其他預處理。

必須使用Bag-of-words模型為每個文檔創建一個字典,在這個字典中存儲有多少單詞以及這些單詞出現的次數。「bow corpus」用來保存該字典比較合適。

bow_corpus = [dictionary.doc2bow(doc) fordoc inprocessed_docs]

現在,預覽預處理的示例文檔11的BOW。

document_num = 11

bow_doc_x = bow_corpus[document_num]

fori inrange(len(bow_doc_x)):

print( 'Word {} ("{}") appears {} time.'.format(bow_doc_x[i][ 0],

dictionary[bow_doc_x[i][ 0]],

bow_doc_x[i][ 1]))

執行 LDA 使用 Bag of Words

在文檔語料庫中,我們的目標是十個主題。為了並行化和加速模型訓練,我們在所有 CPU 內核上執行 LDA。

以下是我們將要調整的一些參數:

要求從訓練語料庫中檢索到的潛在主題個數為1個主題。

id2word 映射將單詞 id(整數)轉換為單詞(字符串)。它用於調試和主題列印,以及確定詞彙量。

用於並行化的額外進程的數量是workers數量。默認情況下,使用所有可用的內核。

超參數 alpha 和 eta 分別影響文檔-主題 (theta) 和主題-單詞 (lambda) 分布的稀疏性。目前,這些將是默認值(默認值為 1/num 個主題)。

  • 高alpha值: 每個文檔都有多個主題(文檔看起來彼此相似)。

  • 低alpha值: 每個文檔包含一些主題。

高alpha值: 每個文檔都有多個主題(文檔看起來彼此相似)。

低alpha值: 每個文檔包含一些主題。

  • 高eta值: 每個主題包含各種單詞(主題看起來彼此相似)。

  • 低eta值: 每個主題包含少量的單詞。

高eta值: 每個主題包含各種單詞(主題看起來彼此相似)。

低eta值: 每個主題包含少量的單詞。

因為我們可以使用gensim LDA模型,所以這是相當簡單的。但必須指定數據收集中的主題數量。假設我們從八個不同的主題開始。通過該文件的培訓次數稱為通過次數。

gensim.models 將訓練 LDA model. LdaMulticore ,並將其放在 "LDA model" 文件夾。

lda_model = gensim.models.LdaMulticore(bow_corpus,

num_topics = 8,

id2word = dictionary,

passes = 10,

workers = 2)

訓練模型後,查看該主題中出現的單詞以及它們對每個單詞的比例重要性。

foridx, topic inlda_model.print_topics( -1):

print( "Topic: {} nWords: {}".format(idx, topic ))

print( "n")

主題 Tokens

你能夠從每個主題中的單詞及其相應的權重中得出哪些類別?

  • 0: Gun Violence

  • 1: Sports

  • 2: Politics

  • 3: Space

  • 4: Encryption

  • 5: Technology

  • 6: Graphics Cards

  • 7: Religion

0: Gun Violence

1: Sports

2: Politics

3: Space

4: Encryption

5: Technology

6: Graphics Cards

7: Religion

對未知文檔的數據進行預處理。

num = 70

unseen_document = newsgroups_test.data[num]print(unseen_document)

bow_vector = dictionary.doc2bow(preprocess(unseen_document))

forindex, score insorted(lda_model[bow_vector],

key= lambdatup: -1*tup[ 1]):

print( "Score: {}t Topic: {}".format(score, lda_model.print_topic(index, 5)))

這裡模型已經完成。現在思考下,如何解釋它,看看結果是否有意義。

該模型產生八個主題的輸出,每個主題都由一組單詞分類。LDA 模型沒有給這些詞一個主題名稱。

模型評估

該模型在提取數據集的不同主題方面表現出色,可以通過目標名稱評估模型。

模型運行速度非常快。僅僅在幾分鐘內,就可以從數據集中提取主題。

假設數據集包含離散的主題,如果數據集是隨機推文的集合,則模型結果可能難以解釋。

簡單總結

通過結合 LDA 主題機率和句子嵌入,上下文主題識別模型同時利用了詞袋和上下文信息。

儘管LDA在主題識別任務中表現良好,但它在處理要建模的簡短文本和不能連貫地解釋主題的文檔時很困難。它也有局限性,因為它是基於一堆單詞。

當文本內部是連貫的,詞袋信息(LDA或TF-IDF)通過檢測頻繁的詞來識別主題非常好。當文本不連貫時(在用詞或句子意義上),就需要更多的信息來反映文本的思想。

gensim.corpora.Dictionary: https://radimrehurek.com/gensim/corpora/dictionary.html

gensim.corpora.Dictionary: https://radimrehurek.com/gensim/corpora/dictionary.html

點這裡關注我,記得標星哦~

CDA課程諮詢

文章來源: https://twgreatdaily.com/zh-tw/6a7a070b72114299ca69a6dcac1f9a3b.html