1 LSA Introductionhtml
LSA(latent semantic analysis)潛在語義分析,也被稱爲LSI(latent semantic index),是Scott Deerwester, Susan T. Dumais等人在1990年提出來的一種新的索引和檢索方法。該方法和傳統向量空間模型(vector space model)同樣使用向量來表示詞(terms)和文檔(documents),並經過向量間的關係(如夾角)來判斷詞及文檔間的關係;而不一樣的是,LSA將詞和文檔映射到潛在語義空間,從而去除了原始向量空間中的一些「噪音」,提升了信息檢索的精確度。node
2 傳統方法的缺點算法
傳統向量空間模型使用精確的詞匹配,即精確匹配用戶輸入的詞與向量空間中存在的詞。因爲一詞多義(polysemy)和一義多詞(synonymy)的存在,使得該模型沒法提供給用戶語義層面的檢索。好比用戶搜索」automobile」,即汽車,傳統向量空間模型僅僅會返回包含」automobile」單詞的頁面,而實際上包含」car」單詞的頁面也多是用戶所須要的。編程
下面是LDA原始Paper[1]裏舉的一個例子:app
上圖是一個Term-Document矩陣,X表明該單詞出如今對應的文件裏,星號表示該詞出如今查詢(Query)中,當用戶輸入查詢」IDF in computer-based information look up」 時,用戶是但願查找與信息檢索中IDF(文檔頻率)相關的網頁,按照精確詞匹配的話,文檔2和3分別包含查詢中的兩個詞,所以應該被返回,而文檔1不包含任何查詢中的詞,所以不會被返回。但咱們仔細看看會發現,文檔1中的access, retrieval, indexing, database這些詞都是和查詢類似度十分高的,其中retrieval和look up是同義詞。顯然,從用戶的角度看,文檔1應該是相關文檔,應該被返回。再來看文檔2:computer information theory,雖然包含查詢中的一次詞information,但文檔2和IDF或信息檢索無關,不是用戶須要的文檔,不該該被返回。從以上分析能夠看出,在本次檢索中,和查詢相關的文檔1並未返回給用戶,而無查詢無關的文檔2卻返回給了用戶。這就是同義詞和多義詞如何致使傳統向量空間模型檢索精確度的降低。機器學習
3 LSA如何解決這些問題編程語言
LSA潛在語義分析的目的,就是要找出詞(terms)在文檔和查詢中真正的含義,也就是潛在語義,從而解決上節所描述的問題。具體說來就是對一個大型的文檔集合使用一個合理的維度建模,並將詞和文檔都表示到該空間,好比有2000個文檔,包含7000個索引詞,LSA使用一個維度爲100的向量空間將文檔和詞表示到該空間,進而在該空間進行信息檢索。而將文檔表示到此空間的過程就是SVD奇異值分解和降維的過程。降維是LSA分析中最重要的一步,經過降維,去除了文檔中的「噪音」,也就是無關信息(好比詞的誤用或不相關的詞偶爾出如今一塊兒),語義結構逐漸呈現。相比傳統向量空間,潛在語義空間的維度更小,語義關係更明確。函數
4 SVD分解[2]工具
SVD分解做爲掌握LSA的基礎知識,我單獨把它做爲一篇文章,能夠在這裏找到。學習
5 LSA技術細節[1][3]
本節主要討論LSA技術細節的理論部分,具體代碼層面分析和實踐在第7節討論。
LSA的步驟以下:
1. 分析文檔集合,創建Term-Document矩陣。
2. 對Term-Document矩陣進行奇異值分解。
3. 對SVD分解後的矩陣進行降維,也就是奇異值分解一節所提到的低階近似。
4. 使用降維後的矩陣構建潛在語義空間,或重建Term-Document矩陣。
下面是Introduction to Latent Semantic Analysis裏面的一個例子,描述了完整的LSA步驟,例子後面有個人補充:
假設文檔集合以下:
原始的Term-Document矩陣以下:
對其進行奇異值分解:
而後對分解後的矩陣降維,這裏保留{S}的最大兩個奇異值,相應的{W}{P}矩陣如圖,注意{P}在公式中須要轉置。
到了這一步後,咱們有兩種處理方法,論文Introduction to Latent Semantic Analysis是將降維後的三個矩陣再乘起來,從新構建了{X}矩陣以下:
觀察{X}矩陣和{X^}矩陣能夠發現:
{X}中human-C2值爲0,由於C2中並不包含human單詞,可是{X^}中human-C2爲0.40,代表human和C2有必定的關係,爲何呢?由於C2:」A survey of user opinion of computer system response time」中包含user單詞,和human是近似詞,所以human-C2的值被提升了。同理還能夠分析其餘在{X^}中數值改變了的詞。
以上分析方法清晰的把LSA的效果顯示出來了,也就是在{X^}中呈現出了潛在語義,而後但願能建立潛在語義空間,並在該空間中檢索信息。這裏以比較兩個單詞爲例:
設奇異值分解形式爲:X = T S DT,T表明term,s表明single value矩陣,D表明Document,DT表示D的轉置。X的兩個行向量點乘的值表明了兩個詞在文檔中共同出現的程度。好比T1在D1中出現10詞,T2在D1中出現5次,T3在D1中出現0詞,那麼只考慮在D1維度上的值,T1(dot)T2=50,T1(dot)T2=0,顯然T1與T2更類似,T1與T3就不那麼類似。那麼用矩陣X(dot)XT就能夠求出全部詞與詞的類似程度。而由奇異值分解的公式的:
X(dot)XT = T(dot)S2(dot)TT = TS(dot)(TS)T
上面公式代表了,咱們想求X(dot)XT的(i,j)個元素時,能夠點乘TS矩陣的第i和j列來表示。所以咱們能夠把TS矩陣的行看做是term的座標,這個座標就是潛在語義空間的座標。同理咱們還能夠推出XT(dot)X = D(dot)S2(dot)DT,從而DS的行表示了文檔的座標。
這樣,咱們就得到了全部文檔和單詞在潛在語義空間的座標,這時咱們就能夠經過向量間的夾角來判斷兩個對象的類似程度,方法和傳統向量空間模型相同。接下來主要討論下檢索文本的步驟。
用戶輸入的檢索語句被稱爲僞文本,由於它也是有多個詞彙構成,和文本類似。因此很天然的想法就是將該僞文本轉換爲文檔座標,而後經過比較該僞文檔與每一個文檔的空間夾角,檢索出該僞文本的相關文檔。設Xq表示僞文本的列向量,其中該列表明文檔集合的索引詞,該列的值表明僞文本中該索引詞出現的次數。好比一個文檔集合有索引詞{T1,T2,T3},僞文本爲t1,t3,t2,t1,則Xq={2,1,1}。得到Xq後,經過公式
Dq = XqT T S-1
計算僞文檔的文檔座標。其中T和S分別表明奇異分解中獲得的矩陣(S = T S DT).注意上面的公式中S-1表明S的逆矩陣。
Dq計算出來後,就能夠迭代比較Dq和文檔集合中全部全部文檔,計算二者個cosine夾角
6 LSA實踐
本節主要討論LSA的實現,編程語言使用C++,環境Linux gcc,使用了GNU Scientific Library[5]。本節代碼能夠在http://code.google.com/p/lsa-lda/找到。
1. 建立Term-Document矩陣
LSA是基於向量空間模型的,所以首先須要建立一個M x N的Term-Document矩陣,其中行表示每個詞,列表示每個文檔。而矩陣的值等於相應詞的TF*IDF值。待檢索的文檔集合放在程序根目錄下的corpus文件夾,每個文檔一個文件。
首先須要建立語料的單詞列表,做爲T-D矩陣的列向量,每個單詞對應一個id。
[code=cpp]
CreateVectorSpace.cc
Function int CreateKeyWordMap()
// 循環讀入每一個文檔
while((ent=readdir(currentDir))!=NULL)
{
//omit . and ..
if((strcmp(ent->d_name,"." ==0)||(strcmp(ent->d_name,".."
==0))
continue;
else
{
//read each file in directory 'corpus'
string filename = "./corpus/";
filename += ent->d_name;
ifstream in(filename.c_str());
// check if file open succeeded
if (!in)
{
cout<<"error, cannot open input file"<<endl;
return -1;
}
Parse(); //分析單詞
[/code]
在循環的過程當中,識別每個單詞,並判斷該單詞是否爲stop word。英文的stop word能夠在ftp://ftp.cs.cornell.edu/pub/smart/english.stop找到。
[code=cpp]
CreateVectorSpace.cc
Function Parse()
// read one char each time
// then recognize a word and check if it is in the stop list
void Parse(ifstream *in,int *wordIndex)
{
string pendingWord;
char ch;
while (1)
{
……
if (!LETTER(ch)) /*after recognized a word*/
{
if (!stoplist.count(pendingWord))
{
/*if not exist in the list*/
if (wordList.find(pendingWord) == wordList.end())
{
wordList.insert(make_pair(pendingWord,*wordIndex));
(*wordIndex)++;
}
}
……
[/code]
接下來須要處理單詞,因爲英文單詞有前綴和後綴,如單詞的單複數(book->books),過去時(like->liked),這些詞雖然形式不一樣但含義相同,所以要將它們處理爲同一的形式,也就是單詞的原型。相關的算法爲Porter Stemming[6]算法。
得到單詞列表後,就能夠構造T-D矩陣了,過程是依次讀入每一個文檔,遇到單詞列表中存在的詞,相應的矩陣單元加1。這裏用到了GSL的幾個函數,用法可參考GSL手冊[5]。
[code=cpp]
CreateVectorSpace.cc
Function CreateMatrix()
gsl_matrix* CreateMatrix()
{
……
// 分配T-D矩陣空間
gsl_matrix * mtx = gsl_matrix_alloc(wordList.size(),docList.size());
map<string, int>::const_iterator map_it = docList.begin();
// for each document
while (map_it != docList.end())
{
…..
// 若是當前單詞在單詞列表中存在
if (wordList.find(pendingWord) != wordList.end())
{
// 矩陣相應的單元值加1
gsl_matrix_set (mtx, wordList[pendingWord], map_it->second,
gsl_matrix_get(mtx, wordList[pendingWord], map_it->second)+1);
wordCount[map_it->second] += 1;
}
……
[/code]
如今已經建立了T-D矩陣,可是矩陣單元值爲單詞在文檔中出現的頻率,所以下一步是求每一個單詞的TF*IDF值[7]。TF表明單詞在某一文檔中出現的頻率,IDF爲inverse document frequency,表明的含義是若是一個單詞在不少文檔中都出現了,那麼用它來區分文檔的價值就下降。具體公式:
[code=cpp]
SVD.CC
Function CreateTfIdfMatrix()
gsl_matrix* CreateTfIdfMatrix()
{
……
double termfrequence = gsl_matrix_get(mtx,i,j)/wordCount[j];
double idf = log((double)docList.size()/(double)getDocumentFrequence(mtx,i));
gsl_matrix_set(mtx,i,j,termfrequence*idf);
……
[/code]
至此T-D矩陣建立完成。
2. SVD分解
SVD分解使用GSL庫中的gsl_linalg_SV_decomp函數
[code=cpp]
SVD.cc
Function CountSVD(gsl_matrix *)
void CountSVD(gsl_matrix* mtx)
{
// S = U S V^T so first let's allocate U,S,V these three matrix
v_mtx = gsl_matrix_alloc(docList.size(),docList.size()); /*V is a N by N matrix*/
s_vct = gsl_vector_alloc(docList.size()); /*S is stored in a n-d vector*/
gsl_vector * workspace = gsl_vector_alloc(docList.size()); /* workspace for gsl function*/
gsl_linalg_SV_decomp(mtx, v_mtx, s_vct, workspace);
}
[/code]
3. 降維
降維在程序你實現十分簡單,也就是給矩陣(因爲是對角矩陣,所以程序裏表示爲向量)賦值零。
[code=cpp]
SVD.cc
Function ReduceDim(int)
void ReduceDim(int keep)
{
for (int i=keep;i<docList.size();i++)
gsl_vector_set(s_vct,i,0);
}
[/code]
4. 查詢
SVD分解完成後,咱們就已經得到了潛在語義空間,接下來就能夠接受用戶的輸入,將僞文本轉換到文檔座標,而後經過比較向量的夾角,找出相關文檔。
[code=cpp]
void Query(string query)
{
// transform query into LSA space
istringstream stream(query);
string word;
//爲Xq建立gsl向量, Xq表示僞文本的列向量
gsl_vector * q_vct = gsl_vector_alloc(wordList.size());
// 爲Dq建立gsl向量,Dq表示僞文本的文檔向量
gsl_vector * d_vct = gsl_vector_alloc(LSD);
// 首先計算Xq
while (stream >> word)
{
if (wordList.count(word)!=0) /*word is in the list*/
gsl_vector_set(q_vct,wordList[word],
gsl_vector_get(q_vct,wordList[word])+1);
}
// Dq = Xq' T S^-1
// 再求Xq'乘T
for (int i = 0; i < LSD; i++)
{
double sum = 0;
for (int j = 0; j < wordList.size(); j++)
sum += gsl_vector_get(q_vct,j) * gsl_matrix_get(mtx,j,i);
gsl_vector_set(d_vct,i,sum);
}
// 最後求(Xq' T) S^-1
for (int k = 0; k < LSD; k++)
gsl_vector_set(d_vct, k,
gsl_vector_get(d_vct,k) * (1/gsl_vector_get(s_vct,k)));
//用文檔集合中每一個文檔和Dq比較
for (int l=0;l<docList.size();l++)
{
……
// 求兩向量夾角,返回cosine值
relation = CompareVector(d_vct, temp_d_vct, LSD);
}
}
[/code]
5. 測試
咱們先用之前討論過的文檔集
將C1~M4分別保存到9個文件裏,放到corpus文件夾
運行程序,輸入格式爲lsa.out [query]
./lsa.out human computer interaction
能夠看出與主題最相關的文檔是C3,其次是C1。C1~C5文件是同主題文檔,主題是人機互交,而M1~M4的共同主題是計算機圖形。而查詢」human computer interaction」顯然描述的是人機互交。所以也能夠從結果看到C1~C5的相關度所有都高於M1~M4文檔。最後,觀察C3,C5文檔,它們並不包含任何查詢中的詞,而計算出的類似度卻不爲0,而且C3的類似度達0.999658,這也正是LSA潛在語義的效果。
下面是文檔兩兩比較後的結果表格(已導入到Excel)
上圖1~9和A~B都分別表明文檔{C1,C2,C3,C4,C5,M1,M2,M3,M4}
上圖很是清晰的顯示出了文檔的關係:
先來看[1~5][A~E]也就是第1~5行,
A~E列,因爲文檔C1~C5是一個主題的文檔,因此能夠看出[1~5][A~E]都大於0.9,而[1~5][F~I]都不超過0.5,也代表C1~C5文檔與M1~M4文檔主題是不相干的。
同理能夠分析[6~9][F~I]。
上面的討論代表,潛在語義分析在主題分類上效果明顯。若是設定一個分類的閾值,好比0.8,那麼上面9個文檔就被自動分爲了{C1,C2,C3,C4,C5}和{M1,M2,M3,M4}
在另外一個測試中,我從New York Times網站收集的6個主題,每一個主題5篇文章
搜索」 what a great day」結果以下:
僞文本座標(0.00402821,-0.0183549,0.00361756),每一個文檔的相關度如上圖。若是設定檢索閾值爲0.9,那麼文檔movie2,sport4,art2會被返回。
7 總結
LSA經過對潛在語義空間的建模,提升的信息檢索的精確度。然後又有人提出了PLSA(Probabilistic latent semantic analysis)和LDA(Latent Dirichlet allocation),將LSA的思想帶入到機率統計模型中。
LSA對一詞多義問題依然沒有解決,僅僅解決了一義多詞。由於LSA將每個詞表示爲潛在語義空間中的一個點,所以一個詞的多個意義在空間中對於的是一個點,沒有被區分。
8 References
[1] Deerwester, S., Dumais, S. T., Furnas, G. W., Landauer, T. K., & Harshman, R.(1990). Indexing By Latent Semantic Analysis. Journal of the American Society For Information Science, 41, 391-407. 10
[2] Christopher D. Manning, Prabhakar Raghavan and Hinrich Schütze, Introduction to Information Retrieval, Cambridge University Press. 2008.
[3] Thomas Landauer, P. W. Foltz, & D. Laham (199 . "Introduction to Latent Semantic Analysis". Discourse Processes 25: 259–284.
[4] Michael Berry, S.T. Dumais, G.W. O'Brien (1995). Using Linear Algebra for Intelligent Information Retrieval. Illustration of the application of LSA to document retrieval.
[5] http://www.gnu.org/software/gsl/manual/html_node/
[6] http://tartarus.org/~martin/PorterStemmer/
[7] http://en.wikipedia.org/wiki/TF_IDF
9 External Link
[1] http://code.google.com/p/lsa-lda/
本文中程序的代碼實現和LSA相關資料
[2] http://en.wikipedia.org/wiki/Latent_semantic_analysis
LSA的WIKI條目,有LSA的大體介紹
Colorado大學的一個LSA項目,提供了基於LSA的terms比較,文本比較等
[4] http://www.bluebit.gr/matrix-calculator/
在線矩陣計算工具,可計算SVD
10 Further Reading
[1] Thomas Hofmann, Probabilistic Latent Semantic Indexing, Proceedings of the Twenty-Second Annual International SIGIR Conference on Research and Development in Information Retrieval (SIGIR-99), 1999
[2] Blei, David M.; Ng, Andrew Y.; Jordan, Michael I (January 2003). "Latent Dirichlet allocation". Journal of Machine Learning Research 3: pp. 993–1022. doi:10.1162/jmlr.2003.3.4-5.993 (inactive 2009-03-30).