2

インデックス作成時に TermVector を指定して、Lucene を使用してドキュメントのコレクションをインデックス作成しています。次に、インデックスを読み取り、各ドキュメントの TF-IDF スコア ベクトルを計算することで、用語とその頻度を取得します。次に、TF-IDF ベクトルを使用して、ウィキペディアのコサイン類似度方程式を使用して、ドキュメント間のペアワイズ コサイン類似度を計算します。

これが私の問題です。このコレクションに 2 つの同一のドキュメント「A」と「B」があるとします (A と B には 200 を超える文があります)。AとBの間のペアワイズコサイン類似度を計算すると、コサイン値= 1が得られますが、これは完全に問題ありません。しかし、ドキュメント「B」から 1 つの文を削除すると、これら 2 つのドキュメント間のコサイン類似度は約 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;
} 
4

1 に答える 1

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:

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));
    }

}
于 2012-05-18T11:10:00.313 に答える