【譯】對亞馬遜評論使用 NLP 進行主題建模

隱狄利克雷分布(Latent Dirichlet Allocation 或 LDA)的 Python 實現(xiàn)

作者:Enes Gokce
原文:Topic Modeling with NLP on Amazon Reviews Application of Latent Dirichlet Allocation (LDA) with Python
譯者:Ivy Lee
譯文于 2020-07-06 始發(fā)自【biendata】公眾號:《以隱含狄利克雷分布,實現(xiàn)對亞馬遜評論的主題建模》


主題建模(Topic modeling)是另一種流行的文本分析技術(shù),最終目標(biāo)是在評論中找到中心思想,并發(fā)現(xiàn)隱藏的主題。語料庫中的文檔都包含一個或多個主題。

主題建模有很多技術(shù),在這里,我將介紹 隱狄利克雷分布(Latent Dirichlet Allocation 或 LDA) 的結(jié)果,這是一種無監(jiān)督分類方法。當(dāng)文本量很大而又不知道從哪里開始時,可以應(yīng)用主題建模,然后查看出現(xiàn)的組別。LDA 是專門為文本數(shù)據(jù)設(shè)計的。

要使用主題建模技術(shù),你需要提供:

  1. 文檔-術(shù)語矩陣(document-term matrix)
  2. 你想要算法提取的主題數(shù)

在主題建模的過程中,我會創(chuàng)建不同的模型進行比較,最后選擇最有意義的主題模型。

如何使用隱狄利克雷分布(LDA)

本文不會介紹 LDA 的數(shù)學(xué)基礎(chǔ)知識,主要是討論的是如何解釋 LDA 主題模型的結(jié)果。

LDA 主題建模過程會創(chuàng)建很多不同的主題組,作為研究人員,我們需要決定輸出中的組數(shù),但是我們并不知道最好的組數(shù)是多少。因此,我們需要嘗試不同的組數(shù),檢驗并比較主題模型,確定哪個主題模型更有意義、最有意義,在模型中具有最明顯的區(qū)別,然后在所有主題組中選擇最有意義的組(模型)。

必須指出的是,LDA 的性質(zhì)是主觀的。在選擇最有意義的主題組時,不同的人可能會得出不同的結(jié)論。我們尋找的是最合理的主題組,不同背景、不同領(lǐng)域?qū)I(yè)知識的人可能會做出不同的選擇。

LDA 是一種無監(jiān)督聚類方法。提到無監(jiān)督聚類方法,就不得不提一下 K-Means 聚類——最著名的無監(jiān)督聚類方法之一。K-Means 聚類在很多情況下都非常實用且有用,已應(yīng)用于文本挖掘多年。與每個單詞只能屬于一個集群(硬聚類)的 K-Means 聚類相反,LDA 允許“模糊”成員資格(軟聚類)。軟聚類允許集群重疊,而在硬聚類中,集群是互斥的。也就是說,在 LDA 中,一個單詞可以屬于多個組,而在 K-Means 聚類中則不可能。在 LDA 中,這種折衷使查找單詞之間的相似性更加容易。然而,這種軟聚類會導(dǎo)致很難劃分組別,因為同一個單詞可以出現(xiàn)在不同的組中。后續(xù)分析中我們會體會到這種影響。

應(yīng)用了主題建模技術(shù)之后,研究人員的工作就是解釋結(jié)果,查看每個主題中單詞的混合是否有意義。如果沒有意義,則可以嘗試修改主題的數(shù)量、文檔-術(shù)語矩陣中的術(shù)語、模型參數(shù),甚至嘗試使用其他模型。

數(shù)據(jù)準(zhǔn)備

本文使用的數(shù)據(jù)簡介: 數(shù)據(jù)下載自 Kaggle,由 Stanford Network Analysis Project 上傳。原始數(shù)據(jù)來自于 J. McAuley 和 J. Leskovec 所做的研究“從業(yè)余愛好者到鑒賞家:通過網(wǎng)絡(luò)評論對用戶專業(yè)知識的發(fā)展進行建模”(2013)。該數(shù)據(jù)集由亞馬遜網(wǎng)站的美食評論構(gòu)成,包括 1999 年至 2012 年的全部 568,454 條評論。每條評論包括產(chǎn)品和用戶信息,評分以及純文本評價。

在本次研究中,我將重點關(guān)注對亞馬遜的“好評”。我對“好評”的定義是:具有 4 星或 5 星(最高 5 星)的評論。換句話說,如果某條評論為 4 星或 5 星,就屬于本次研究中的“好評”;1 2 3 星則被標(biāo)記為“差評”。

數(shù)據(jù)準(zhǔn)備至關(guān)重要,如果準(zhǔn)備數(shù)據(jù)出現(xiàn)失誤,就無法實施主題建模。不過在本次研究中,我們不會深入探討如何準(zhǔn)備數(shù)據(jù),因為這不是本次研究的重點。但是,你需要做好心理準(zhǔn)備,這一步如果出現(xiàn)問題,會花費一些時間。如果你在處理自己的數(shù)據(jù)集時,需要對應(yīng)調(diào)整本文提供的代碼,希望一切順利。


先檢查數(shù)據(jù)的列行數(shù):

df.shape

(392384, 19) —— 數(shù)據(jù)集有 392,384 條評論。

對于很多家用計算機(以及 Google Colab)來說,這樣的數(shù)據(jù)量都是難以處理的。因此,我會只使用其中 10,000 條評論。非常遺憾,如果我們無法使用超級計算機,就無法使用所有評論數(shù)據(jù)。

# 從數(shù)據(jù)集中獲取前 10000 條好評
df_good_reviews= df.loc[df.Good_reviews ==1][0:10000]

下一步是計數(shù)向量化器(Count Vectorizer):

# 創(chuàng)建文檔-術(shù)語矩陣
from sklearn.feature_extraction import text
from sklearn.feature_extraction.text import CountVectorizer

cv = CountVectorizer()
data_cv = cv.fit_transform(df_good_reviews.Text)
data_cv.shape
Count Vectorizer 之后的數(shù)據(jù)形狀

Pickle 存儲數(shù)據(jù),為后續(xù)創(chuàng)建文檔-術(shù)語矩陣做準(zhǔn)備。

# Pickle it
import pickle
pickle.dump(cv, open("cv_stop.pkl", "wb"))
data_stop.to_pickle("dtm_stop.pkl") #saving the pickled data

data = pd.read_pickle('dtm_stop.pkl')
data

Pickle 數(shù)據(jù)后,我們得到一個寬格式的詞典:

現(xiàn)在到了創(chuàng)建術(shù)語-文檔矩陣的時候了,該術(shù)語-文檔矩陣數(shù)據(jù)將用于生成主題模型,所以對此次研究至關(guān)重要。

# 所需的輸入之一就是術(shù)語-文檔矩陣
tdm = data.transpose()
tdm.head()
創(chuàng)建術(shù)語-文檔矩陣后的數(shù)據(jù)
# 通過從 df -> 稀疏矩陣 -> Gensim 語料庫,將術(shù)語-文檔矩陣轉(zhuǎn)換為新的 Gensim 格式

sparse_counts = scipy.sparse.csr_matrix(tdm)
corpus = matutils.Sparse2Corpus(sparse_counts)

# Gensim 還需要一個字典,表示所有術(shù)語及其在術(shù)語-文檔矩陣中的對應(yīng)位置

cv = pickle.load(open("cv_stop.pkl", "rb"))
id2word = dict((v, k) for k, v in cv.vocabulary_.items())

現(xiàn)在開始創(chuàng)建主題模型!


使用 LDA 構(gòu)建主題模型

構(gòu)建主題模型的方法有很多。根據(jù)特定條件篩選文本,可以獲得不同的主題組。在本次研究中,我們會使用以下內(nèi)容創(chuàng)建主題模型:

  1. 所有文本
  2. 僅包含文本中的所有名詞
  3. 文本中的所有名詞和形容詞

主題建模 - 嘗試 1(所有文本)

首先,先嘗試使用所有評論數(shù)據(jù),在此過程中不進行文本篩選。

lda = models.LdaModel(corpus=corpus, id2word=id2word, num_topics=2, passes=10)
lda.print_topics()

發(fā)現(xiàn)了兩組主題:

[(0, ‘0.020”coffee” + 0.011”like” + 0.010”tea” + 0.010”flavor” + 0.009”good” + 0.009”taste” + 0.007”one” + 0.007”great” + 0.006”use” + 0.006”cup”’),

(1, ‘0.010”great” + 0.009”like” + 0.009”good” + 0.007”love” + 0.007”food” + 0.007”one” + 0.007”product” + 0.005”taste” + 0.005”get” + 0.005”amazon”’)]

如何處理這一結(jié)果? 此時,我們可以檢查這兩個主題組,確定它們是否是具有差異性的組。我們不是預(yù)期一個組中的所有單詞都必須相關(guān),而是查看主題組的整體趨勢。具體到以上兩組,很難看到明顯的差異。這是我的個人看法,如果你認(rèn)為它們確實是兩個不同的組,并且能夠證明其合理性,也可以放心使用這一結(jié)果。

注意,之前提到過 LDA 是一種軟聚類模型。在這里,我們看到“l(fā)ike”一詞包含在兩組中,這是正常現(xiàn)象,因為在軟聚類模型中,一個單詞可以同時出現(xiàn)在不同的組。

不需要強迫自己努力使上述模型有意義,我們可以繼續(xù),創(chuàng)建更多的主題模型。

與創(chuàng)建 2 組主題模型的操作類似,創(chuàng)建 3 組和 4 組。

# LDA for num_topics = 3
lda = models.LdaModel(corpus=corpus, id2word=id2word, num_topics=3, passes=10)
lda.print_topics()

# LDA for num_topics = 4
lda = models.LdaModel(corpus=corpus, id2word=id2word, num_topics=4, passes=10)
lda.print_topics()

所有結(jié)果可在表 1(下表)中看到。此時,我們應(yīng)該檢查對比,嘗試找到最有意義的組。通過查看表 1,可以說“兩組主題的模型”是最有意義的,第一組與飲料有關(guān),第二組與反應(yīng)有關(guān)。當(dāng)然,你完全可以得出與我不同的結(jié)論!現(xiàn)在,這些結(jié)果先保留在這里,不會再進一步調(diào)整,因為我們會再創(chuàng)建 6 個主題模型。得到所有結(jié)果后,我可以更仔細(xì)地檢查輸出。在生成進一步的主題模型之后,我們會重新考慮表 1。

表 1:使用所有文本數(shù)據(jù)的主題模型

主題建模 - 嘗試 2(僅名詞): 在此步驟中,通過 LDA 方法僅使用名詞創(chuàng)建主題。同樣,我們的目的是在評論中找到隱藏的模式。現(xiàn)在,只需要使用不同的條件進行篩選。

與上一步類似,我們將運行帶有 2、3、4 個主題組的 LDA。

import nltk
from nltk.corpus import stopwords
nltk.download('punkt')
nltk.download('averaged_perceptron_tagger')
import string
from nltk import word_tokenize, pos_tag

創(chuàng)建一個從文本字符串中提取名詞的函數(shù):

from nltk import word_tokenize, pos_tag

def nouns(text):
    '''給定一個文本字符串,對其進行分詞,只提取出其中的名詞'''
    is_noun = lambda pos: pos[:2] == 'NN'
    tokenized = word_tokenize(text)
    all_nouns = [word for (word, pos) in pos_tag(tokenized) if     is_noun(pos)]
    return ' '.join(all_nouns)

# 如果此代碼不能運行,可能是由于頁面格式導(dǎo)致的縮進錯誤。

將定義好的 nouns 函數(shù)應(yīng)用于評論文本數(shù)據(jù):

data_nouns = pd.DataFrame(df_good_reviews.Text.apply(nouns))
data_nouns

僅使用名詞創(chuàng)建一個新的文檔-術(shù)語矩陣:

from sklearn.feature_extraction import text
from sklearn.feature_extraction.text import CountVectorizer

# 重新添加其他停用詞,因為我們正在重新創(chuàng)建文檔-術(shù)語矩陣

add_stop_words = ['like', 'im', 'know', 'just', 'dont', 'thats', 'right', 'people','youre', 'got', 'gonna', 'time', 'think', 'yeah', 'said']

stop_words = text.ENGLISH_STOP_WORDS.union(add_stop_words)

# 重新創(chuàng)建僅包含名詞的文檔-術(shù)語矩陣
cvn = CountVectorizer(stop_words=stop_words)
data_cvn = cvn.fit_transform(data_nouns.Text)

data_dtmn = pd.DataFrame(data_cvn.toarray(), columns=cvn.get_feature_names())

data_dtmn.index = data_nouns.index
data_dtmn

創(chuàng)建 Gensim 語料庫:

corpusn=matutils.Sparse2Corpus(scipy.sparse.csr_matrix(data_dtmn.transpose()))

# 創(chuàng)建詞匯字典

id2wordn = dict((v, k) for k, v in cvn.vocabulary_.items())

? 從 2 組主題開始

ldan = models.LdaModel(corpus=corpusn, num_topics=2, id2word=id2wordn, passes=10)

ldan.print_topics()

? 3 組主題的 LDA

ldan = models.LdaModel(corpus=corpusn, num_topics=3, id2word=id2wordn, passes=10)

ldan.print_topics()

? 4 組主題的 LDA

ldan = models.LdaModel(corpus=corpusn, num_topics=4, id2word=id2wordn, passes=10)

ldan.print_topics()

表 2 是僅使用名詞嘗試的 LDA 主題模型輸出。現(xiàn)在,我們需要再次檢查主題,嘗試找到具有不同主題組的模型。同樣,仍然不需要花費很多時間,因為我們還會生成更多的主題模型。

表 2:僅包含名詞的主題模型

對我來說,其中具有三個小組的主題模型很有意義。它包含以下組:

  1. 寵物食品
  2. 餅干和零食
  3. 飲品

當(dāng)然,得出不同的結(jié)論也是完全正常的。

主題建模-嘗試 3(名詞和形容詞): 在此步驟中,僅使用名詞和形容詞通過 LDA 方法創(chuàng)建主題模型。

準(zhǔn)備數(shù)據(jù):

from nltk.corpus import stopwords
import nltk
nltk.download('punkt')
nltk.download('averaged_perceptron_tagger')
import string
from nltk import word_tokenize, pos_tag

自定義獲取名詞或形容詞的函數(shù):

def nouns_adj(text):
    '''給定一個文本字符串,對其進行分詞,獲取其中的名詞和形容詞'''

    is_noun_adj = lambda pos: pos[:2] == 'NN' or pos[:2] == 'JJ'
    tokenized = word_tokenize(text)
    nouns_adj = [word for (word, pos) in pos_tag(tokenized) if   is_noun_adj(pos)]
    return ' '.join(nouns_adj)

# 如果此代碼不能運行,可能是由于頁面格式導(dǎo)致的縮進錯誤。

對評論數(shù)據(jù)使用 nouns_adj 進行篩選:

data_nouns_adj = pd.DataFrame(df_good_reviews.Text.apply(nouns_adj))
data_nouns_adj

如你所見,這里僅包含名詞和形容詞。現(xiàn)在,我們要求 LDA 使用篩選后的這一個數(shù)據(jù)集版本創(chuàng)建主題模型。

為 LDA 準(zhǔn)備數(shù)據(jù):

# 僅使用名詞和形容詞創(chuàng)建一個新的文檔-術(shù)語矩陣,同時使用 max_df 刪除常見單詞
cvna = CountVectorizer(max_df=.8) # max_df 用于刪除出現(xiàn)頻率過高的數(shù)據(jù)值,也稱為“特定語料庫的停用詞(corpus-specific stop words)”
# 例如,max_df=.8 表示會忽略文檔中出現(xiàn)頻率大于 80% 的詞。

data_cvna = cvna.fit_transform(data_nouns_adj.Text)
data_dtmna = pd.DataFrame(data_cvna.toarray(), columns=cvna.get_feature_names())

data_dtmna.index = data_nouns_adj.index
data_dtmna

創(chuàng)建 Gensim 語料庫:

corpusna=matutils.Sparse2Corpus(scipy.sparse.csr_matrix(data_dtmna.transpose()))

# 創(chuàng)建詞匯字典
id2wordna = dict((v, k) for k, v in cvna.vocabulary_.items())

現(xiàn)在可以運行 LDA 了。

? 從 2 組主題開始

ldana = models.LdaModel(corpus=corpusna, num_topics=2, id2word=id2wordna, passes=10)
ldana.print_topics()

? 嘗試 3 組主題

ldana = models.LdaModel(corpus=corpusna, num_topics=3, id2word=id2wordna, passes=10)
ldana.print_topics()

? 嘗試 4 組主題

ldana = models.LdaModel(corpus=corpusna, num_topics=4, id2word=id2wordna, passes=10)
ldana.print_topics()

表 3 是僅使用名詞和形容詞的 LDA 主題模型輸出。我們再次檢查主題,確定是否具有有意義的主題組。

表 3:僅包含名詞和形容詞的主題模型

評估結(jié)果

現(xiàn)在到了最后階段,表 1、表 2 和表 3 的結(jié)果必須一起評估。我們一共創(chuàng)建了 9 個主題模型,問一下自己:“哪一組更有意義?”現(xiàn)在,需要集中精力仔細(xì)檢查各個主題。哪一組有意義?如果都沒有意義,我們需要返回到數(shù)據(jù)清理步驟,更改參數(shù)或使用其他篩選條件和模型,這是一個遞歸過程。

在 9 個主題模型中,對我來說最有意義的是:僅名詞具有 3 個主題組的模型(表 4)。我在這里看到三個不同的類別:(1)寵物食品、(2)餅干和零食、(3)飲品。同樣,找到其他更有意義的主題模型是完全可以的。

表 4:最有意義的主題 — 僅名詞的三組主題

記住一點,該數(shù)據(jù)集僅包含食品評論。因此,看到這些組都與食品有關(guān)是很正常的。

找出最有意義的主題模型后,通過更多迭代,獲得微調(diào)模型。3 組主題(僅名詞)對我來說最有意義,所以我將微調(diào)這一主題模型,把迭代數(shù)從 10 調(diào)整到 80。

# Fine-tuned LDA with topics = 3
ldan = models.LdaModel(corpus=corpusn, num_topics=3, id2word=id2wordn, passes=80)

ldan.print_topics()
表 5:微調(diào)參數(shù)后的最終主題模型

在上表中,我們看到與表 4 類似的組,只是順序不同。通過以上使用 LDA 方法進行主題分析的所有步驟,可以得出結(jié)論,亞馬遜評論的好評可分為三個主要主題:(1)飲品、(2)寵物食品、(3)餅干和零食。

最后需要注意的是,要了解哪個主題組更有意義,我們可能需要對應(yīng)領(lǐng)域的專業(yè)知識。作為一名研究人員,有責(zé)任證明我們對主題組的選擇是正確的。只要理由充足,我們可以將任何主題組確定為最終主題模型。

感謝閱讀!分析愉快!

特別感謝我的朋友 Bibor Szabo 在撰寫本文時提供的寶貴建議。


提示: 此次研究所使用的 Python 代碼,可以在我的 GitHub 文件夾 中查找。另外,這次的主題模型研究是另一個更大項目的一部分。如果你對前面的步驟感興趣,可以查看我之前的文章:數(shù)據(jù)清洗情緒分析

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,197評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,415評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,104評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,884評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,647評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,130評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,208評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,366評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,887評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,737評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,939評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,478評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,174評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,586評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,827評論 1 283
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,608評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 47,914評論 2 372

推薦閱讀更多精彩內(nèi)容