用隱藏語義分析(LSA)進行主題建模(附Python程式碼)
作者: ofollow,noindex">PRATEEK JOSHI
編譯:Bing
簡介
你是否曾去過一家管理完善的圖書館?在那兒,我常被它們井然有序的書籍管理所震撼,他們會根據書名、內容或者其他主題把所有書排列整齊。我不清楚每個管理員要經手的書有多少,也許是幾千本,也許有上萬本,如果以純手動的方式按類別對圖書進行管理,他們一定會累到吐血。
但是,如果我們有這些書的電子版,整件事做起來似乎就簡單的多了,可能只用幾秒鐘就能完成。NLP萬歲!

請看下面一小段話:

從三種顏色的高亮處我們可以知道,這些書一共有三個主題(或者概念)。一個好的主題模型可以分辨出相似的單詞,並把它們歸到一個群組中。在上面這段話中,最主要的主題就是綠色的主題2,通過它,我們可以瞭解到這段話的主要意思是有關假視訊的。
在這篇文章中,我們將學習一種文字挖掘方法——主題建模(topic modeling)。這是提取主題的一種非常有用的技術,在NLP問題中會經常用到。
注:在閱讀這篇文章前,我強烈推薦我們此前的另一篇文章: 《12種降維方法終極指南(含Python程式碼)》 ,從中可以瞭解SVD、UMAP等概念,這些概念會在本文出現。
什麼是主題模型?
主題模型可以看作是一種無監督技術,用於在多個文字檔案中發現主題。但這些主題在自然中是抽象的,而且一段文字中可能含有多種主題。就目前來說,我們暫且將主題模型理解成一個黑箱,如下圖所示:

黑箱(主題模型)將很多相似相關的詞語聚集起來,稱為主題。這些主題在文字中有特定的分佈形態,每種主題都是通過它包含的不同單詞比例確定的。
什麼時候會用到主題建模?
回到我們開頭說到的圖書館的例子,現在假設你要對幾個數字化文字進行管理,如果數量不多,完全可以手動完成,但如果電子文字數量很大該怎麼辦?
這就需要用到NLP技術,對於這項任務,主題建模是必須用到的。

主題建模可以幫助使用者處理大量文字資料,找到文字中相似的多個詞語,確定抽象的主題。除此之外,主題模型還可以用於搜尋引擎,讓搜尋結果與搜尋字元相匹配。
隱藏語義分析(LSA)概覽
所有語言都有自己細小的特徵,機器難以分辨(有時連人類都會認錯)。比如有時不同的單詞卻表達相同含義,或者同一個單詞卻表達不同意思。
例如,看以下兩個句子:
- I liked his last novel quite a lot.
- We would like to go for a novel marketing campaign.
在第一句話中,“novel”指的是一本書、小說,而在第二句話中它是形容詞,意為“新的”。
我們可以輕易地分辨二者,因為我們理解了“novel”前後詞語的意思。但是,機器無法理解這些概念,所以也不能理解詞語所處的語境。這就需要用到隱藏語義分析(LSA)了,它通過分析詞語周圍的語境捕捉其中的隱藏概念,即主題。
所以,僅僅將詞語對映到文字中並不能起到太大幫助,我們真正要做的是弄清楚詞語背後隱藏的概念或主題,這就是LSA的目的。
實施LSA的步驟
假設我們有m個文字文件,總共有n個不同的詞語,我們想從文件中所有文字資料中提取出k個主題,而k是由使用者決定的。
- 生成一個文字-單詞矩陣,計算TF-IDF分數。

- 之後,我們會將上述矩陣的維度降至k,利用奇異值分解(SVD)。
- SVD將一個矩陣分解成三個其他的矩陣,假設我們想用SVD分解矩陣A,它就會將其分成矩陣U、S和VT(矩陣V的轉置矩陣)。


矩陣UK的每一行都是對應文字的向量表示,這些向量的長度是k,即目標主題的數量。我們資料中的詞語的向量表示可以在矩陣VK中找到。
通過SVD,我們得到了我們的資料中每個文字和詞語的向量表示,然後用這些向量,我們可以找到相似的單詞,利用餘弦相似性找到相似的文字。
用Python實現LSA
首先下載所需要的庫。
import numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn as sns pd.set_option("display.max_colwidth", 200)
在這篇文章中,我們會用到sklearn中的“20 Newsgroup”,下載地址: archive.ics.uci.edu/ml/datasets/Twenty+Newsgroups 。程式碼如下:
from sklearn.datasets import fetch_20newsgroups dataset = fetch_20newsgroups(shuffle=True, random_state=1, remove=('headers', 'footers', 'quotes')) documents = dataset.data len(documents)
輸出:11314。
dataset.target_names ['alt.atheism', 'comp.graphics', 'comp.os.ms-windows.misc', 'comp.sys.ibm.pc.hardware', 'comp.sys.mac.hardware', 'comp.windows.x', 'misc.forsale', 'rec.autos', 'rec.motorcycles', 'rec.sport.baseball', 'rec.sport.hockey', 'sci.crypt', 'sci.electronics', 'sci.med', 'sci.space', 'soc.religion.christian', 'talk.politics.guns', 'talk.politics.mideast', 'talk.politics.misc', 'talk.religion.misc']
資料及共有11314個文字文件,分佈在20各不同的newsgroup中。
資料預處理
開始之前,我們先嚐試著清理文字資料。主要思想就是清除其中的標點、數字和特殊字元。之後,我們需要刪除較短的單詞,因為通常它們不會包含什麼有用的資訊。最後,我們將文字變為不區分大小寫。
news_df = pd.DataFrame({'document':documents}) # removing everything except alphabets` news_df['clean_doc'] = news_df['document'].str.replace("[^a-zA-Z#]", " ") # removing short words news_df['clean_doc'] = news_df['clean_doc'].apply(lambda x: ' '.join([w for w in x.split() if len(w)>3])) # make all text lowercase news_df['clean_doc'] = news_df['clean_doc'].apply(lambda x: x.lower())
之後我們要刪除沒有特別意義的停止詞,例如“it”、“they”、“am”、“been”、“about”、“because”、“while”等等。為了實現這一目的,我們要對文字進行標記化,也就是將一串文字分割成獨立的標記或單詞。刪除停止詞之後,再把這些標記組合在一起。
from nltk.corpus import stopwords stop_words = stopwords.words('english') # tokenization tokenized_doc = news_df['clean_doc'].apply(lambda x: x.split()) # remove stop-words tokenized_doc = tokenized_doc.apply(lambda x: [item for item in x if item not in stop_words]) # de-tokenization detokenized_doc = [] for i in range(len(news_df)): t = ' '.join(tokenized_doc[i]) detokenized_doc.append(t) news_df['clean_doc'] = detokenized_doc
文字-詞語矩陣
這是通向主題建模的第一步。我們要用sklearn的TfidfVectorizer給1000個詞語創造一個文字-詞語矩陣。如果你有足夠的計算力,可以增加更多資料。
from sklearn.feature_extraction.text import TfidfVectorizer vectorizer = TfidfVectorizer(stop_words='english', max_features= 1000, # keep top 1000 terms max_df = 0.5, smooth_idf=True) X = vectorizer.fit_transform(news_df['clean_doc']) X.shape # check shape of the document-term matrix (11314, 1000)
主題建模
下一步是將每個詞語和文字用向量表示,我們會用到文字-詞語矩陣並對他們降維。這裡會用到TruncatedSVD執行矩陣的分解。
由於資料來自20個不同的分組,我們就假設文字資料有20個不同主題。
from sklearn.decomposition import TruncatedSVD # SVD represent documents and terms in vectors svd_model = TruncatedSVD(n_components=20, algorithm='randomized', n_iter=100, random_state=122) svd_model.fit(X) len(svd_model.components_) 20
svd_model中的元素就是我們的主題,我們可以用svd model.components 檢視。最後,在20個主題中輸入幾個重要單詞,看模型會做出什麼反應。
terms = vectorizer.get_feature_names() for i, comp in enumerate(svd_model.components_): terms_comp = zip(terms, comp) sorted_terms = sorted(terms_comp, key= lambda x:x[1], reverse=True)[:7] print("Topic "+str(i)+": ") for t in sorted_terms: print(t[0]) print(" ") Topic 0: like know people think good time thanks Topic 1: thanks windows card drive mail file advance Topic 2: game team year games season players good Topic 3: drive scsi disk hard card drives problem Topic 4: windows file window files program using problem Topic 5: government chip mail space information encryption data Topic 6: like bike know chip sounds looks look Topic 7: card sale video offer monitor price jesus Topic 8: know card chip video government people clipper Topic 9: good know time bike jesus problem work Topic 10: think chip good thanks clipper need encryption Topic 11: thanks right problem good bike time window Topic 12: good people windows know file sale files Topic 13: space think know nasa problem year israel Topic 14: space good card people time nasa thanks Topic 15: people problem window time game want bike Topic 16: time bike right windows file need really Topic 17: time problem file think israel long mail Topic 18: file need card files problem right good Topic 19: problem file thanks used space chip sale
主題視覺化
為了更方便地探索主題,我們應該對其視覺化。當然,可是話不能大於三維,但是PCA或t-SNE等技術可以幫我們將高維資料降成低維進行視覺化。這裡,我們用另一種相對較新的技術,稱作UMAP(Uniform Manifold Approximation and Projection)。
import umap X_topics = svd_model.fit_transform(X) embedding = umap.UMAP(n_neighbors=150, min_dist=0.5, random_state=12).fit_transform(X_topics) plt.figure(figsize=(7,5)) plt.scatter(embedding[:, 0], embedding[:, 1], c = dataset.target, s = 10, # size edgecolor='none' ) plt.show()

如上所示,結果非常明顯,每個點代表一段文字,不同的顏色表示20個分組。
完整程式碼地址: github.com/prateekjoshi565/latent_semantic_analysis
LSA的優缺點
如上所示,隱藏語義分析非常有用,但是它也有自己的缺點。在使用它之前,還需要了解它的優缺點。
優點:
- LSA非常快,並且易於實施。
- 結果很清晰,比單一的向量空間模型好得多。
缺點:
- 由於它是一個線性模型,可能在非線性資料集上表現的不是很好。
- LSA假設文字中的詞語是高斯分佈,可能不適用於所有問題。
- LSA中涉及SVD,可能在出現新資料或更新時需要大量計算力。