gensim使用方法以及例子

gensim是一個python的自然語言處理庫,能夠將文檔根據TF-IDF, LDA, LSI 等模型轉化成向量模式,以便進行進一步的處理。此外,gensim還實現了word2vec功能,能夠將單詞轉化爲詞向量。關於詞向量的知識可以看我之前的文章

關於gensim的使用方法,我是根據官網的資料來看的,思路也是跟着官網tutorial走的,英文好的或者感覺我寫的不全面的可以去官網



1. corpora 和 dictionary

1.1 基本概念和用法

corpora是gensim中的一個基本概念,是文檔集的表現形式,也是後續進一步處理的基礎。從本質上來說,corpora其實是一種格式或者說約定,其實就是一個二維矩陣。舉個例子,現在有一個文檔集,裏面有兩篇文檔

hurry up
rise up

這兩篇文檔裏總共出現了3個詞,hurry, rise, up。如果將這3個詞映射到數字,比如說hurry, rise, up 分別對應1,2,3, 那麼上述的文檔集的一種表現形式可以是

1,0,1
0,1,1

這種方法只考慮了詞頻,且不考慮詞語間的位置關係。因爲第一個文檔中的兩個詞分別編號1,3且都只出現了一次,所以第1個和第3個爲1,第2個數爲0。
當然了,在實際運行中,因爲單詞數量極多(上萬甚至10萬級別),而一篇文檔的單詞數是有限的,所以如果還是採用密集矩陣來表示的話,會造成極大的內存浪費,所以gensim內部是用稀疏矩陣的形式來表示的。
那麼,如何將字符串形式的文檔轉化成上述形式呢?這裏就要提到詞典的概念(dictionary)。詞典是所有文檔中所有單詞的集合,而且記錄了各詞的出現次數等信息。
在實際的操作中,對於字符串形式的文檔,首先要將字符串分割成詞語列表。比如”hurry up”要分割成[“hurry”,”up”]。 對於中文來講,分詞就是一個很關鍵的問題,不過可以去找一些分詞庫來實現。我一般用的是jieba. 而對於英文來說,雖然分詞方便,但是要注意詞幹提取和詞形還原。
在將文檔分割成詞語之後,使用dictionary = corpora.Dictionary(texts)生成詞典,並可以使用save函數將詞典持久化。生成詞典以後 corpus = [dictionary.doc2bow(text) for text in texts]檔轉化爲向量形式。示例代碼如下

from gensim import corpora
from collections import defaultdict
documents = ["Human machine interface for lab abc computer applications",
             "A survey of user opinion of computer system response time",
             "The EPS user interface management system",
             "System and human system engineering testing of EPS",
             "Relation of user perceived response time to error measurement",
             "The generation of random binary unordered trees",
             "The intersection graph of paths in trees",
             "Graph minors IV Widths of trees and well quasi ordering",
             "Graph minors A survey"]

# 去掉停用詞
stoplist = set('for a of the and to in'.split())
texts = [[word for word in document.lower().split() if word not in stoplist]
         for document in documents]

# 去掉只出現一次的單詞
frequency = defaultdict(int)
for text in texts:
    for token in text:
        frequency[token] += 1
texts = [[token for token in text if frequency[token] > 1]
         for text in texts]

dictionary = corpora.Dictionary(texts)   # 生成詞典

# 將文檔存入字典,字典有很多功能,比如
# diction.token2id 存放的是單詞-id key-value對
# diction.dfs 存放的是單詞的出現頻率
dictionary.save('/tmp/deerwester.dict')  # store the dictionary, for future reference
corpus = [dictionary.doc2bow(text) for text in texts]
corpora.MmCorpus.serialize('/tmp/deerwester.mm', corpus)  # store to disk, for later use

注意最後的corpora.MmCorpus.serialize 將corpus持久化到磁盤中。相反,可以用

corpus = corpora.MmCorpus('/tmp/deerwester.mm')

來從磁盤中讀取corpus。

除了MmCorpus以外,還有其他的格式,例如SvmLightCorpus, BleiCorpus, LowCorpus等等,用法類似。



1.2 dictionary的其他一些用法


dictionary還有其他的一些用法,現羅列一部分

dictionary.filter_n_most_frequent(N)
過濾掉出現頻率最高的N個單詞

dictionary.filter_extremes(no_below=5, no_above=0.5, keep_n=100000)
1.去掉出現次數低於no_below的
2.去掉出現次數高於no_above的。注意這個小數指的是百分數
3.在1和2的基礎上,保留出現頻率前keep_n的單詞

dictionary.filter_tokens(bad_ids=None, good_ids=None)
有兩種用法,一種是去掉bad_id對應的詞,另一種是保留good_id對應的詞而去掉其他詞。注意這裏bad_ids和good_ids都是列表形式

dictionary.compacity()
在執行完前面的過濾操作以後,可能會造成單詞的序號之間有空隙,這時就可以使用該函數來對詞典來進行重新排序,去掉這些空隙。



1.3 分批處理和分佈式計算結果的彙總


dictionary和corpora的基本用法在上一節已經提過,但是當文本的規模很大時,也許會造成內存不足以容納文本的情況,這就需要將所有文本分批處理,最後再將各批次計算得到的結果進行彙總。分佈式計算時也有類似的需求。

這裏假設在兩個批次中,分別生成了dict1,corpus1以及dict2,corpus2.
第一步,首先將兩個詞典合併。當然,如果是先統一生成詞典再分批生成詞向量的話,可以跳過這一步,因爲詞典是一樣的。
合併詞典很簡單

dict2_to_dict1 = dict1.merge_with(dict2)

要注意的是,得到的dict2_to_dict1並不是生成後的詞典,而是dict2中的單詞序號到這些詞在合併後詞典新序號的映射表。而dict1本身成爲合併後的新詞典。
第二部,合併corpus
如果之前跳過了第一步,即dict1就是dict2的話,可以直接進行合併。合併有兩種方式,一種是

 merged_corpus = [x for x in corpus1] + [x for x in corpus2]

另外一種,則需使用內置的itertools類

merged_corpus = itertools.chain(corpus1, corpus2)
merged_corpus = [x for x in merged_corpus]

如果之前的詞典也是分批生成的話,則需要對corpus2進行一定的處理

new_corpus2 = dict2_to_dict1[corpus2]
merged_corpus = itertools.chain(corpus1, new_corpus2)
merged_corpus = [x for x in merged_corpus]

這樣,就把分批處理得到的dict和corpus都合併起來了。



2. models


在models中,可以對corpus進行進一步的處理,比如使用tf-idf模型,lsi模型,lda模型等,非常強大。
在按照之前的方法生成了corpus和dictionary以後,就可以生成模型了

tfidf_model = models.TfidfModel(corpus) 

注意,目前只是生成了一個模型,但這是類似於生成器,並不是將對應的corpus轉化後的結果。對tf-idf模型而言,裏面存儲有各個單詞的詞頻,文頻等信息。想要將文檔轉化成tf-idf模式表示的向量,還要使用如下命令

corpus_tfidf = tfidf_model[corpus]

對於lda和lsi模型,用法有所不同

lsi_model = models.LsiModel(corpus_tfidf, id2word=dictionary, num_topics=2)
corpus_lsi = lsi_model[corpus_tfidf]

可以看到,這裏除了corpus以外,還多了num_topic的選項。這是指的潛在主題(topic)的數目,也等於轉成lsi模型以後每個文檔對應的向量長度。轉化以後的向量在各項的值,即爲該文檔在該潛在主題的權重。因此lsi和lda的結果也可以看做該文檔的文檔向量,用於後續的分類,聚類等算法。值得注意的是,id2word是所有模型都有的選項,可以指定使用的詞典。

由於這裏num_topics=2 ,所以可以用作圖的方式直觀的顯現出來


這裏寫圖片描述

可以很清楚的看到,9個文檔可以看成兩類,分別是前5行和後4行。

這一部分的代碼如下

import os
from gensim import corpora, models, similarities
from pprint import pprint
from matplotlib import pyplot as plt
import logging

# logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)

def PrintDictionary(dictionary):
    token2id = dictionary.token2id
    dfs = dictionary.dfs
    token_info = {}
    for word in token2id:
        token_info[word] = dict(
            word = word,
            id = token2id[word],
            freq = dfs[token2id[word]]
        )
    token_items = token_info.values()
    token_items = sorted(token_items, key = lambda x:x['id'])
    print('The info of dictionary: ')
    pprint(token_items)
    print('--------------------------')

def Show2dCorpora(corpus):
    nodes = list(corpus)
    ax0 = [x[0][1] for x in nodes] # 繪製各個doc代表的點
    ax1 = [x[1][1] for x in nodes]
    # print(ax0)
    # print(ax1)
    plt.plot(ax0,ax1,'o')
    plt.show()

if (os.path.exists("/tmp/deerwester.dict")):
    dictionary = corpora.Dictionary.load('/tmp/deerwester.dict')
    corpus = corpora.MmCorpus('/tmp/deerwester.mm')
    print("Used files generated from first tutorial")
else:
    print("Please run first tutorial to generate data set")

PrintDictionary(dictionary)

# 嘗試將corpus(bow形式) 轉化成tf-idf形式
tfidf_model = models.TfidfModel(corpus) # step 1 -- initialize a model 將文檔由按照詞頻表示 轉變爲按照tf-idf格式表示
doc_bow = [(0, 1), (1, 1),[4,3]]
doc_tfidf = tfidf_model[doc_bow]

# 將整個corpus轉爲tf-idf格式
corpus_tfidf = tfidf_model[corpus]
# pprint(list(corpus_tfidf))
# pprint(list(corpus))

## LSI模型 **************************************************
# 轉化爲lsi模型, 可用作聚類或分類
lsi_model = models.LsiModel(corpus_tfidf, id2word=dictionary, num_topics=2)
corpus_lsi = lsi_model[corpus_tfidf]
nodes = list(corpus_lsi)
# pprint(nodes)
lsi_model.print_topics(2) # 打印各topic的含義

# ax0 = [x[0][1] for x in nodes] # 繪製各個doc代表的點
# ax1 = [x[1][1] for x in nodes]
# print(ax0)
# print(ax1)
# plt.plot(ax0,ax1,'o')
# plt.show()

lsi_model.save('/tmp/model.lsi') # same for tfidf, lda, ...
lsi_model = models.LsiModel.load('/tmp/model.lsi')
#  *********************************************************

## LDA模型 **************************************************
lda_model = models.LdaModel(corpus_tfidf, id2word=dictionary, num_topics=2)
corpus_lda = lda_model[corpus_tfidf]
Show2dCorpora(corpus_lsi)
# nodes = list(corpus_lda)
# pprint(list(corpus_lda))

# 此外,還有Random Projections, Hierarchical Dirichlet Process等模型

3. similarities

這一部分主要負責計算文檔間的相似度。與向量的相似度計算方式一樣,採用餘弦方法計算得到。一般來講,使用lsi模型得到的向量進行計算效果比較好。

corpus_simi_matrix = similarities.MatrixSimilarity(corpus_lsi)
# 計算一個新的文本與既有文本的相關度
test_text = "Human computer interaction".split()
test_bow = dictionary.doc2bow(test_text)
test_tfidf = tfidf_model[test_bow]
test_lsi = lsi_model[test_tfidf]
test_simi = corpus_simi_matrix[test_lsi]
print(list(enumerate(test_simi)))

得到結果[(0, 0.99916452), (1, 0.99632162), (2, 0.9990505), (3, 0.99886364), (4, 0.99996823), (5, -0.058117405), (6, -0.021589279), (7, 0.013524055), (8, 0.25163394)]。可以看到顯然屬於第一類

發佈了88 篇原創文章 · 獲贊 591 · 訪問量 147萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章