2012-05-18 117 views
2

我通過在索引時指定TermVector來使用Lucene索引一組文檔。 然後,我通過閱讀索引並計算每個文檔的TF-IDF分數向量來檢索詞條及其頻率。然後,使用TF-IDF向量,我使用Wikipedia's cosine similarity equation計算文檔之間的成對餘弦相似度。使用Lucene計算文檔相似度的更好方法

這是我的問題:說我在這個集合中有兩個相同的文檔「A」和「B」(A和B有200多個句子)。如果我計算A和B之間的成對餘弦相似度,它會給我餘弦值= 1,這完全可以。但是如果我從文檔「B」中刪除單個句子,則這兩個文檔之間的餘弦相似度值約爲0.85。文件幾乎相似,但餘弦值不是。我知道問題出在我正在使用的公式上。

是否有更好的方法/方程可用於計算文檔之間的餘弦相似度?

被修改

這是我如何計算餘弦相似度,doc1[]doc2[]是對應文檔的TF-IDF矢量。矢量只包含scores但不是words

private double cosineSimBetweenTwoDocs(float doc1[], float doc2[]) { 
    double temp; 
    int doc1Len = doc1.length; 
    int doc2Len = doc2.length; 
    float numerator = 0; 
    float temSumDoc1 = 0; 
    float temSumDoc2 = 0; 
    double equlideanNormOfDoc1 = 0; 
    double equlideanNormOfDoc2 = 0; 
    if (doc1Len > doc2Len) { 
     for (int i = 0; i < doc2Len; i++) { 
      numerator += doc1[i] * doc2[i]; 
      temSumDoc1 += doc1[i] * doc1[i]; 
      temSumDoc2 += doc2[i] * doc2[i]; 
     } 
     equlideanNormOfDoc1=Math.sqrt(temSumDoc1); 
     equlideanNormOfDoc2=Math.sqrt(temSumDoc2); 
    } else { 
     for (int i = 0; i < doc1Len; i++) { 
      numerator += doc1[i] * doc2[i]; 
      temSumDoc1 += doc1[i] * doc1[i]; 
      temSumDoc2 += doc2[i] * doc2[i]; 
     } 
     equlideanNormOfDoc1=Math.sqrt(temSumDoc1); 
     equlideanNormOfDoc2=Math.sqrt(temSumDoc2); 
    } 

    temp = numerator/(equlideanNormOfDoc1 * equlideanNormOfDoc2); 
    return temp; 
} 
+0

我想你的代碼有些問題。從200個句子中刪除一個句子應該給你一個> 0.98的數字。爲了驗證它,你可以生成一個隨機矢量,對矢量進行修改並計算餘弦相似度,以查看你得到的結果。對於大小爲1000的矢量和在[10,100]範圍內的隨機數,如果我從矢量中的所有數中減去範圍[10,20]中的一個隨機數,那麼對於我來說,所得到的相似性度量總是> 0.98。 – Helium

+0

我使用Mathematica來驗證案例。這是我的代碼:a = RandomInteger [{10,100},1000]; b = a - RandomInteger [{10,20},1000]; {總計[a],總計[b],總計[a - b], N [(a。b)/(Norm [a] Norm [b])]},這裏是輸出:{55419,40271,15148,0.98811} – Helium

+0

@Mohsen刪除矢量B中的一個句子將減少該矢量中的元素數如果我們在刪除句子後得到一個尺寸爲1000的矢量,矢量B的大小將變成995,現在矢量A的大小爲1000,但是兩個矢量也不會對齊。通過刪除句子,矢量元素從中間移除,但不從矢量的末尾移除。所以如果你可以嘗試從中間刪除矢量元素,你可以觀察0.85值 – Kasun

回答

4

正如我告訴你在我的評論,我覺得你的地方犯了一個錯誤。 載體實際上包含<word,frequency>對,而不是words。 因此,當您刪除該句子時,只會將相應單詞的頻率減1(後面的單詞未被移位)。 考慮下面的例子:

文件A:

A B C A A B C. D D E A B. D A B C B A. 

文獻B:

A B C A A B C. D A B C B A. 

矢量一個:

A:6, B:5, C:3, D:3, E:1 

矢量B:

A:5, B:4, C:3, D:1, E:0 

這造成以下的相似性度量:

(6*5+5*4+3*3+3*1+1*0)/(Sqrt(6^2+5^2+3^2+3^2+1^2) Sqrt(5^2+4^2+3^2+1^2+0^2))= 
62/(8.94427*7.14143)= 
0.970648 

編輯 我覺得你的源代碼不工作爲好。考慮下面的代碼,它適用於上面的例子:

import java.util.HashMap; 
import java.util.Map; 

public class DocumentVector { 
    Map<String, Integer> wordMap = new HashMap<String, Integer>(); 

    public void incCount(String word) { 
     Integer oldCount = wordMap.get(word); 
     wordMap.put(word, oldCount == null ? 1 : oldCount + 1); 
    } 

    double getCosineSimilarityWith(DocumentVector otherVector) { 
     double innerProduct = 0; 
     for(String w: this.wordMap.keySet()) { 
      innerProduct += this.getCount(w) * otherVector.getCount(w); 
     } 
     return innerProduct/(this.getNorm() * otherVector.getNorm()); 
    } 

    double getNorm() { 
     double sum = 0; 
     for (Integer count : wordMap.values()) { 
      sum += count * count; 
     } 
     return Math.sqrt(sum); 
    } 

    int getCount(String word) { 
     return wordMap.containsKey(word) ? wordMap.get(word) : 0; 
    } 

    public static void main(String[] args) { 
     String doc1 = "A B C A A B C. D D E A B. D A B C B A."; 
     String doc2 = "A B C A A B C. D A B C B A."; 

     DocumentVector v1 = new DocumentVector(); 
     for(String w:doc1.split("[^a-zA-Z]+")) { 
      v1.incCount(w); 
     } 

     DocumentVector v2 = new DocumentVector(); 
     for(String w:doc2.split("[^a-zA-Z]+")) { 
      v2.incCount(w); 
     } 

     System.out.println("Similarity = " + v1.getCosineSimilarityWith(v2)); 
    } 

} 
+0

我從文檔「B」中手動刪除句子,然後使用Lucene進行索引。因此,對於您的示例,在Doc「B」中,Lucene不知道之前在文檔「B」中有一個術語「E」。以這個例子Doc A {ABCEEBC。 – Kasun

+0

爲例。 DDEEB。現在向量A {A-1,B-5,C-3,D-3,E-6}(Vector A Size = 5);向量B {B-3,C-1,D-3,E-4}(向量B大小= 4)。所以這表明實際上術語是移位的,所以它將比較矢量A的術語「A」和矢量B的術語「B」。這將輸出約爲0.7的餘弦值。有沒有一種方法可以在Lucene中編制索引後刪除語句? – Kasun

+0

@Kasun:我自己已經在幾年前實施了餘弦相似性度量,正如我描述的那樣,它曾經完美地工作。問題在於,你認爲這些單詞必須改變,而它們不能。在上面的例子中,向量a和b的大小不一樣,但是如果一個向量中不存在一個項,它的頻率就應該是零。因此,文件a中E項的頻率爲1,而文件b中的頻率爲0(因爲它不存在)。 – Helium