What is Gensim?
Gensim是一款開源的第三方Python工具包,用於從原始的非結構化的文本中,無監督地學習到文本隱層的主題向量表達。它支持包括TF-IDF,LSA,LDA,和word2vec在內的多種主題模型算法,支持流式訓練,並提供了諸如相似度計算,信息檢索等一些常用任務的API接口。
基本概念
- 語料(Corpus):一組原始文本的集合,用於無監督地訓練文本主題的隱層結構。語料中不需要人工標注的附加信息。在Gensim中,Corpus通常是一個可迭代的對象(比如列表)。每一次迭代返回一個可用於表達文本對象的稀疏向量。
- 向量(Vector):由一組文本特征構成的列表。是一段文本在Gensim中的內部表達。
- 稀疏向量(Sparse Vector):通常,我們可以略去向量中多余的0元素。此時,向量中的每一個元素是一個(key, value)的tuple。
- 模型(Model):是一個抽象的術語。定義了兩個向量空間的變換(即從文本的一種向量表達變換為另一種向量表達)。
Step 1. 訓練語料的預處理
訓練語料的預處理指的是將文檔中原始的字符文本轉換成Gensim模型所能理解的稀疏向量的過程。
通常,我們要處理的原生語料是一堆文檔的集合,每一篇文檔又是一些原生字符的集合。在交給Gensim的模型訓練之前,我們需要將這些原生字符解析成Gensim能處理的稀疏向量的格式。
由於語言和應用的多樣性,Gensim沒有對預處理的接口做出任何強制性的限定。通常,我們需要先對原始的文本進行分詞、去除停用詞等操作,得到每一篇文檔的特征列表。例如,在詞袋模型中,文檔的特征就是其包含的word:
texts = [['human', 'interface', 'computer'],
['survey', 'user', 'computer', 'system', 'response', 'time'],
['eps', 'user', 'interface', 'system'],
['system', 'human', 'system', 'eps'],
['user', 'response', 'time'],
['trees'],
['graph', 'trees'],
['graph', 'minors', 'trees'],
['graph', 'minors', 'survey']]
其中,corpus的每一個元素對應一篇文檔。
接下來,我們可以調用Gensim提供的API建立語料特征(此處即是word)的索引字典,並將文本特征的原始表達轉化成詞袋模型對應的稀疏向量的表達。依然以詞袋模型為例:
from gensim import corpora
dictionary = corpora.Dictionary(texts)
corpus = [dictionary.doc2bow(text) for text in texts]
print corpus[0] # [(0, 1), (1, 1), (2, 1)]
到這里,訓練語料的預處理工作就完成了。我們得到了語料中每一篇文檔對應的稀疏向量(這里是bow向量);向量的每一個元素代表了一個word在這篇文檔中出現的次數。值得注意的是,雖然詞袋模型是很多主題模型的基本假設,這里介紹的doc2bow
函數並不是將文本轉化成稀疏向量的唯一途徑。在下一小節里我們將介紹更多的向量變換函數。
最后,出於內存優化的考慮,Gensim支持文檔的流式處理。我們需要做的,只是將上面的列表封裝成一個Python迭代器;每一次迭代都返回一個稀疏向量即可。
class MyCorpus(object):
def __iter__(self):
for line in open('mycorpus.txt'):
# assume there's one document per line, tokens separated by whitespace
yield dictionary.doc2bow(line.lower().split())
Step 2. 主題向量的變換
對文本向量的變換是Gensim的核心。通過挖掘語料中隱藏的語義結構特征,我們最終可以變換出一個簡潔高效的文本向量。
在Gensim中,每一個向量變換的操作都對應着一個主題模型,例如上一小節提到的對應着詞袋模型的doc2bow
變換。每一個模型又都是一個標准的Python對象。下面以TF-IDF模型為例,介紹Gensim模型的一般使用方法。
首先是模型對象的初始化。通常,Gensim模型都接受一段訓練語料(注意在Gensim中,語料對應着一個稀疏向量的迭代器)作為初始化的參數。顯然,越復雜的模型需要配置的參數越多。
from gensim import models
tfidf = models.TfidfModel(corpus)
其中,corpus是一個返回bow向量的迭代器。這兩行代碼將完成對corpus中出現的每一個特征的IDF值的統計工作。
接下來,我們可以調用這個模型將任意一段語料(依然是bow向量的迭代器)轉化成TFIDF向量(的迭代器)。需要注意的是,這里的bow向量必須與訓練語料的bow向量共享同一個特征字典(即共享同一個向量空間)。
doc_bow = [(0, 1), (1, 1)]
print tfidf[doc_bow] # [(0, 0.70710678), (1, 0.70710678)]
注意,同樣是出於內存的考慮,model[corpus]
方法返回的是一個迭代器。如果要多次訪問model[corpus]
的返回結果,可以先講結果向量序列化到磁盤上。
我們也可以將訓練好的模型持久化到磁盤上,以便下一次使用:
tfidf.save("./model.tfidf")
tfidf = models.TfidfModel.load("./model.tfidf")
Gensim內置了多種主題模型的向量變換,包括LDA,LSI,RP,HDP等。這些模型通常以bow向量或tfidf向量的語料為輸入,生成相應的主題向量。所有的模型都支持流式計算。關於Gensim模型更多的介紹,可以參考這里:API Reference
Step 3. 文檔相似度的計算
在得到每一篇文檔對應的主題向量后,我們就可以計算文檔之間的相似度,進而完成如文本聚類、信息檢索之類的任務。在Gensim中,也提供了這一類任務的API接口。
以信息檢索為例。對於一篇待檢索的query,我們的目標是從文本集合中檢索出主題相似度最高的文檔。
首先,我們需要將待檢索的query和文本放在同一個向量空間里進行表達(以LSI向量空間為例):
# 構造LSI模型並將待檢索的query和文本轉化為LSI主題向量
# 轉換之前的corpus和query均是BOW向量
lsi_model = models.LsiModel(corpus, id2word=dictionary, num_topics=2)
documents = lsi_model[corpus]
query_vec = lsi_model[query]
接下來,我們用待檢索的文檔向量初始化一個相似度計算的對象:
index = similarities.MatrixSimilarity(documents)
我們也可以通過save()
和load()
方法持久化這個相似度矩陣:
index.save('/tmp/deerwester.index')
index = similarities.MatrixSimilarity.load('/tmp/deerwester.index')
注意,如果待檢索的目標文檔過多,使用similarities.MatrixSimilarity
類往往會帶來內存不夠用的問題。此時,可以改用similarities.Similarity
類。二者的接口基本保持一致。
最后,我們借助index
對象計算任意一段query和所有文檔的(余弦)相似度:
sims = index[query_vec] # return: an iterator of tuple (idx, sim)