2012-05-18 4 views
2

색인 생성시 TermVector를 지정하여 Lucene을 사용하여 문서 모음에 대해 색인을 생성합니다. 그런 다음 색인을 읽고 각 문서에 대한 TF-IDF 점수 벡터를 계산하여 용어와 빈도를 검색합니다. 그런 다음 TF-IDF 벡터를 사용하여 Wikipedia's cosine similarity equation을 사용하여 문서 사이의 쌍간 코사인 유사성을 계산합니다.더 나은 문서 계산 방법 Lucene을 사용한 유사성

이것이 내 문제입니다. A와 B가 두 세트 (A와 B가 200 개가 넘는 문장) 인이 컬렉션에 두 개의 동일한 문서가 있다고 가정 해보십시오. A와 B 사이의 pairwise 코사인 유사성을 계산하면 코사인 값 = 1을 얻을 수 있습니다. 이것은 완벽하게 정상입니다. 그러나 Doc "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.{55419, 40271, 15148, 0.98811} – Helium

+0

@ 모센 제거 벡터 B에서 한 문장을 제거하면 해당 벡터의 요소 수가 줄어 듭니다. 문장을 제거한 후에 크기가 1000 인 벡터를 얻으면 벡터 B의 크기는 995가되고 현재 벡터 A의 크기는 1000이지만 두 개의 벡터도 정렬되지 않습니다. 문장을 제거하면 벡터 요소는 가운데에서 제거되지만 벡터의 끝에서 제거되지는 않습니다. 따라서 중간에서 벡터 요소를 제거하여 시도 할 수 있다면 0.838 값 – Kasun

답변

4

가 포함되어 있습니다. 벡터에는 <word,frequency> 쌍이 포함되며 실제로는 words이 아닙니다. 따라서 문장을 삭제하면 해당 단어의 빈도 만 1에서 뺍니다 (이후 단어는 이동하지 않음).

문서화 : 다음 예를 고려

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 :

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"라는 용어가 있음을 알지 못합니다. – Kasun

+0

이 예제를 사용하십시오. Doc A {ABCEEBC. DDEEB. DEBCBE} Doc B {DDEEB.DEBCBE} 이제 벡터 A {1, B-5, C-3, D-3, E-6} (벡터 A 크기 = 5); 벡터 B {B-3, C-1, D-3, E-4} (벡터 B 크기 = 4). 그래서 이것은 실제로 용어가 이동 된 것을 보여주기 때문에 벡터 A의 용어 "A"와 벡터 B의 용어 "B"를 비교합니다. 이것은 ~ 0.7 주변의 코사인 값을 출력합니다. Lucene에서 색인 생성 후 문장을 제거하는 방법이 있습니까? – Kasun

+0

@ 카슌 : 몇 년 전에 코사인 유사성 측정을 구현했습니다. 당신을 묘사하고 완벽하게 작동하는 데 사용되었습니다. 문제는 단어를 바꾸지 말아야한다고 생각하는 것입니다. 위의 예에서 벡터 a와 b의 크기가 같지 않지만 벡터에 용어가 없으면 빈도는 단순히 0으로 간주됩니다. 따라서 문서 a의 용어 E의 빈도는 1이지만 문서 b의 빈도는 0입니다 (존재하지 않기 때문에). – Helium