如果想要開啟日志,別忘記設(shè)置:
>>>importlogging>>>logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)
1
2
轉(zhuǎn)換接口
在之前的教程《語料庫與向量空間》中,我們創(chuàng)建了一個用向量流表示文檔的語料庫。為了繼續(xù)征程,讓我們啟動gensim并使用該語料庫。
>>>fromgensimimportcorpora, models, similarities>>>dictionary = corpora.Dictionary.load('/tmp/deerwester.dict')>>>corpus = corpora.MmCorpus('/tmp/deerwester.mm')>>>print(corpus)MmCorpus(9documents,12features,28non-zero entries)
1
2
3
4
5
在本次教程中,我將會向你展示如何將文檔從一種向量表示方式轉(zhuǎn)換到另一種。這個處理是為了兩個目的:
將語料庫中隱藏的結(jié)構(gòu)發(fā)掘出來,發(fā)現(xiàn)詞語之間的關(guān)系,并且利用這些結(jié)構(gòu)、關(guān)系使用一種新的、更有語義價值的(這是我們最希望的)方式描述其中的文檔。
使得表示方式更加簡潔。這樣不僅能提高效率(新的表示方法一般消耗較少的資源)還能提高效果(忽略了邊際數(shù)據(jù)趨勢、降低了噪音)。
創(chuàng)建一個轉(zhuǎn)換
轉(zhuǎn)換(transformations)是標(biāo)準(zhǔn)的Python類,通常通過訓(xùn)練語料庫的方式初始化
>>>tfidf = models.TfidfModel(corpus)# 第一步 -- 初始化一個模型
1
我們使用了前一個教程中用過的語料庫來初始化(訓(xùn)練)這個轉(zhuǎn)換模型。不同的轉(zhuǎn)換可能需要不同的初始化參數(shù);在Tfidf案例中,“訓(xùn)練”僅僅是遍歷提供的語料庫然后計算所有屬性的文檔頻率(譯者注:在多少文檔中過)。訓(xùn)練其他模型,例如潛在語義分析或隱含狄利克雷分配,更加復(fù)雜,因此耗時也多。
作者注:轉(zhuǎn)換常常是在兩個特定的向量空間之間進行。訓(xùn)練與后續(xù)的轉(zhuǎn)換必須使用相同的向量空間(=有相同的屬性,且編號相同)。如若不然,例如使用不同的預(yù)處理方法處理字符串、使用不同的屬性編號、需要Tfidf向量的時候卻輸入了詞袋向量,將會導(dǎo)致轉(zhuǎn)換過程中屬性匹配錯誤,進而使輸出結(jié)果無意義并可能引發(fā)異常。
轉(zhuǎn)換向量
從現(xiàn)在開始,tfidf將被視為只讀的對象,可以用它來轉(zhuǎn)換將任何采用舊表示方法的向量(詞袋整數(shù)計數(shù))轉(zhuǎn)換為新的表示方法(Tfidf 實數(shù)權(quán)重):
>>>doc_bow = [(0,1), (1,1)]>>>print(tfidf[doc_bow])# 第二步 -- 使用模型轉(zhuǎn)換向量[(0,0.70710678), (1,0.70710678)]
1
2
3
或者對整個語料庫實施轉(zhuǎn)換:
>>>corpus_tfidf = tfidf[corpus]>>>fordocincorpus_tfidf:...print(doc)[(0,0.57735026918962573), (1,0.57735026918962573), (2,0.57735026918962573)][(0,0.44424552527467476), (3,0.44424552527467476), (4,0.44424552527467476), (5,0.32448702061385548), (6,0.44424552527467476), (7,0.32448702061385548)][(2,0.5710059809418182), (5,0.41707573620227772), (7,0.41707573620227772), (8,0.5710059809418182)][(1,0.49182558987264147), (5,0.71848116070837686), (8,0.49182558987264147)][(3,0.62825804686700459), (6,0.62825804686700459), (7,0.45889394536615247)][(9,1.0)][(9,0.70710678118654746), (10,0.70710678118654746)][(9,0.50804290089167492), (10,0.50804290089167492), (11,0.69554641952003704)][(4,0.62825804686700459), (10,0.45889394536615247), (11,0.62825804686700459)]
1
2
3
4
5
6
7
8
9
10
11
12
在這個特殊的情況中,被轉(zhuǎn)換的語料庫與用來訓(xùn)練的語料庫相同,但是這僅僅是偶然。一旦轉(zhuǎn)換模型被初始化了,它可以用來轉(zhuǎn)換任何向量(當(dāng)然最好使用與訓(xùn)練語料庫相同的向量空間 | 注:即語言環(huán)境),即使它們并沒有在訓(xùn)練語料庫中出現(xiàn)。這是通過潛在語義分析的調(diào)入(folding in)、隱含狄利克雷分配的主題推斷(topic inference)等得到的。
調(diào)用model[corpus]只能在舊的corpus文檔流的基礎(chǔ)上創(chuàng)建一個包裝-真正的轉(zhuǎn)化是在迭代文檔時即時計算的。我們可以通過調(diào)用corpus_transformed = model[corpus]一次轉(zhuǎn)化整個語料庫,因為這樣意味著我們要將結(jié)果存入內(nèi)存中,這與gensim內(nèi)存無關(guān)的設(shè)計理念不太符合。如果你還要多次迭代轉(zhuǎn)化后的corpus_transformed,負擔(dān)將會十分巨大。你可以先將結(jié)果序列化并存儲到硬盤上再做需要的操作。
轉(zhuǎn)換也可以被序列化,還可以一個(轉(zhuǎn)換)疊另一個,像一串鏈條一樣:
>>>lsi = models.LsiModel(corpus_tfidf, id2word=dictionary, num_topics=2)# 初始化一個LSI轉(zhuǎn)換>>>corpus_lsi = lsi[corpus_tfidf]# 在原始語料庫上加上雙重包裝: bow->tfidf->fold-in-lsi
1
2
這里我們利用潛在語義索引(LSI)將Tf-Idf語料轉(zhuǎn)化為一個潛在2-D空間(2-D是因為我們設(shè)置了num_topics=2)?,F(xiàn)在你可能想知道:2潛在維度意味著什么?讓我們利用models.LsiModel.print_topics()來檢查一下這個過程到底產(chǎn)生了什么變化吧:
>>>lsi.print_topics(2)topic#0(1.594): -0.703*"trees" + -0.538*"graph" + -0.402*"minors" + -0.187*"survey" + -0.061*"system" + -0.060*"response" + -0.060*"time" + -0.058*"user" + -0.049*"computer" + -0.035*"interface"topic#1(1.476): -0.460*"system" + -0.373*"user" + -0.332*"eps" + -0.328*"interface" + -0.320*"response" + -0.320*"time" + -0.293*"computer" + -0.280*"human" + -0.171*"survey" + 0.161*"trees"
1
2
3
(這些主題將會記錄在日志中,想要了解如何激活日志,請看開頭的注解)
根據(jù)LSI來看,“tree”、“graph”、“minors”都是相關(guān)的詞語(而且在第一主題的方向上貢獻最多),而第二主題實際上與所有的詞語都有關(guān)系。如我們所料,前五個文檔與第二個主題的關(guān)聯(lián)更強,而其他四個文檔與第一個主題關(guān)聯(lián)最強:
>>>fordocincorpus_lsi:# both bow->tfidf and tfidf->lsi transformations are actually executed here, on the fly...print(doc)[(0, -0.066), (1,0.520)]# "Human machine interface for lab abc computer applications"[(0, -0.197), (1,0.761)]# "A survey of user opinion of computer system response time"[(0, -0.090), (1,0.724)]# "The EPS user interface management system"[(0, -0.076), (1,0.632)]# "System and human system engineering testing of EPS"[(0, -0.102), (1,0.574)]# "Relation of user perceived response time to error measurement"[(0, -0.703), (1, -0.161)]# "The generation of random binary unordered trees"[(0, -0.877), (1, -0.168)]# "The intersection graph of paths in trees"[(0, -0.910), (1, -0.141)]# "Graph minors IV Widths of trees and well quasi ordering"[(0, -0.617), (1,0.054)]# "Graph minors A survey"
1
2
3
4
5
6
7
8
9
10
11
模型的持久可以借助save()和load()函數(shù)完成:
>>>lsi.save('/tmp/model.lsi')# same for tfidf, lda, ...>>>lsi = models.LsiModel.load('/tmp/model.lsi')
1
2
下一個問題可能就該是:這些文檔之間確切的相似度是多少呢?能否將相似性形式化,以便給定一個文檔,我們能夠根據(jù)其他文檔與該文檔的相似度排序呢?敬請閱讀下個教程——《相似度查詢》。
可用的轉(zhuǎn)換
Gensim實現(xiàn)了幾種常見的向量空間模型算法:
詞頻-逆文檔頻(Term Frequency * Inverse Document Frequency, Tf-Idf)
需要一個詞袋形式(整數(shù)值)的訓(xùn)練語料庫來實現(xiàn)初始化。轉(zhuǎn)換過程中,他將會接收一個向量同時返回一個相同維度的向量,在語料庫中非常稀有的屬性的權(quán)重將會提高。因此,他會將整數(shù)型的向量轉(zhuǎn)化為實數(shù)型的向量,同時讓維度不變。而且。你可以選擇是否將返回結(jié)果標(biāo)準(zhǔn)化至單位長度(歐幾里得范數(shù))。
>>> model = tfidfmodel.TfidfModel(bow_corpus, normalize=True)
1
潛在語義索引(Latent Semantic Indexing,LSI,or sometimes LSA)
將文檔從詞袋或TfIdf權(quán)重空間(更好)轉(zhuǎn)化為一個低維的潛在空間。對于我們上面用到的玩具級的語料庫,我們使用了2潛在維度,但是在真正的語料庫上,推薦200-500的目標(biāo)維度為“金標(biāo)準(zhǔn)”。[1]
>>>model = lsimodel.LsiModel(tfidf_corpus, id2word=dictionary, num_topics=300)
1
LSI訓(xùn)練的獨特之處是我們能在任何繼續(xù)“訓(xùn)練”,僅需提供更多的訓(xùn)練文本。這是通過對底層模型進行增量更新,這個過程被稱為“在線訓(xùn)練”。正因為它的這個特性,輸入文檔流可以是無限大——我們能在以只讀的方式使用計算好的模型的同時,還能在新文檔到達時一直“喂食”給LSI“消化”!
>>>model.add_documents(another_tfidf_corpus)# 現(xiàn)在LSI已經(jīng)使用tfidf_corpus + another_tfidf_corpus進行過訓(xùn)練了>>>lsi_vec = model[tfidf_vec]# 將新文檔轉(zhuǎn)化到LSI空間不會影響該模型>>>...>>>model.add_documents(more_documents)# tfidf_corpus + another_tfidf_corpus + more_documents>>>lsi_vec = model[tfidf_vec]>>>...
1
2
3
4
5
6
有關(guān)在無限大的流中,如何讓LSI逐漸“忘記”舊的觀測結(jié)果,詳情請看gensim.models.lsimodel的幫助文檔。如果你不怕麻煩,有幾個參數(shù)可以控制LSI算法的影響速度、內(nèi)存占用和數(shù)值精度等。
gensim使用一個新穎的在線增量流分布式訓(xùn)練算法(還挺拗口的…),我曾將該方法發(fā)表在[5]中。gensim內(nèi)部執(zhí)行了一個來自Halko等[4]的隨機多通道算法(stochastic multi-pass algorithm)來加速核心(in-core)部分的計算。參考《在英文維基百科上的實驗》教程了解如何通過計算機集群分布式計算來提高速度。
目的在于減小空維度。這是一個非常高效(對CPU和內(nèi)存都很友好)方法,通過拋出一點點隨機性,來近似得到兩個文檔之間的Tfidf距離。推薦目標(biāo)維度也是成百上千,具體數(shù)值要視你的數(shù)據(jù)集大小而定。
>>>model = rpmodel.RpModel(tfidf_corpus, num_topics=500)
1
隱含狄利克雷分配(Latent Dirichlet Allocation, LDA)
也是將詞袋計數(shù)轉(zhuǎn)化為一個低維主題空間的轉(zhuǎn)換。LDA是LSA(也叫多項式PCA)的概率擴展,因此LDA的主題可以被解釋為詞語的概率分布。這些分布式從訓(xùn)練語料庫中自動推斷的,就像LSA一樣。相應(yīng)地,文檔可以被解釋為這些主題的一個(軟)混合(又是就像LSA一樣)。
>>>model = ldamodel.LdaModel(bow_corpus, id2word=dictionary, num_topics=100)
1
gensim使用一個基于[2]的快速的在線LDA參數(shù)估計實現(xiàn),修改并使其可以在計算機集群上以分布式模式運行。
分層狄利克雷過程(Hierarchical Dirichlet Process,HDP)
是一個無參數(shù)貝葉斯方法(注意:這里沒有num_topics參數(shù)):
>>>model = hdpmodel.HdpModel(bow_corpus, id2word=dictionary)
1
gensim使用一種基于[3]的快速在線來實現(xiàn)。該算法是新加入gensim的,并且還是一種粗糙的學(xué)術(shù)邊緣產(chǎn)品——小心使用。
增加新的VSM轉(zhuǎn)化(例如新的權(quán)重方案)相當(dāng)平常;參見API參考或者直接參考我們的源代碼以獲取信息與幫助。
值得一提的是,這些模型增量模型,無需一次將所有的訓(xùn)練語料庫全部放到內(nèi)存中。在關(guān)心內(nèi)存的同時,我還在不斷改進分布式計算,來提高CPU效率。如果你自覺能夠貢獻一份力量(測試、提供用例或代碼),請讓我知道。
下一個教程,我們將會講《相似度查詢》。
[1] Bradford. 2008. An empirical study of required dimensionality for large-scale latent semantic indexing applications.
[2] Hoffman, Blei, Bach. 2010. Online learning for Latent Dirichlet Allocation.
[3] Wang, Paisley, Blei. 2011. Online variational inference for the hierarchical Dirichlet process.
[4] Halko, Martinsson, Tropp. 2009. Finding structure with randomness.
[5] ?eh??ek. 2011. Subspace tracking for Latent Semantic Analysis.