Lucene评分机制

4

我有3个产品名称,它们分别是:

  1. Bounty Select-A-Size白色纸巾12兆卷
  2. Bounty Select-A-Size纸巾(12卷)
  3. Bounty Select-A-Size白色纸巾12兆卷

如您所见,第一个和第三个术语除了“白色”一词的位置不同外,其他都相同。第二个术语缺少“白色”和“兆”这两个词

现在,当我运行以下代码时:

public static void main(String[] args) throws IOException, ParseException {
    StandardAnalyzer analyzer = new StandardAnalyzer();

    // 1. create the index
    Directory index = new RAMDirectory();

    IndexWriterConfig config = new IndexWriterConfig(analyzer);

    IndexWriter w = new IndexWriter(index, config);
    addDoc(w, "Bounty Select-A-Size White Paper Towels 12 Mega Rolls");
    addDoc(w, "Bounty Select-A-Size Paper Towels (12 rolls)");
    addDoc(w, "Bounty Select-A-Size Paper Towels White 12 Mega Rolls");
    w.close();

    // 2. query
    String querystr = "Bounty Select-A-Size White Paper Towels 12 Mega Rolls";

    Query q = new QueryParser("title", analyzer).parse(querystr);

    // 3. search
    IndexReader reader = DirectoryReader.open(index);
    IndexSearcher searcher = new IndexSearcher(reader);
    ScoreDoc[] hits = searcher.search(q, 4).scoreDocs;

    // 4. display results
    System.out.println("Found " + hits.length + " hits.");
    for(int i=0;i<hits.length;++i) {
        int docId = hits[i].doc;
        Document d = searcher.doc(docId);
        System.out.println((i + 1) + ". " + d.get("title") + "\t score " + hits[i].score);
    }

    reader.close();
}

private static void addDoc(IndexWriter w, String title) throws IOException {
    Document doc = new Document();
    doc.add(new TextField("title", title, Field.Store.YES));
    w.addDocument(doc);
}

结果如下:
 1. Bounty Select-A-Size White Paper Towels 12 Mega Rolls    score 0.7363191
 2. Bounty Select-A-Size Paper Towels White 12 Mega Rolls    score 0.7363191
 3. Bounty Select-A-Size Paper Towels (12 rolls)     score 0.42395753

到目前为止,一切都很好,前两个术语具有相同的构成,因此它们得分相同。

然而,当我将要搜索的术语数量扩展到更多(使用相同的代码,但不是静态输入3个,而是从文件中获取了大约5000个),得分发生了变化。

 1. Bounty Select-A-Size White Paper Towels 12 Mega Rolls             4.1677103
 2. Bounty Select-A-Size Paper Towels (12 rolls)                     4.1677103
 3. Bounty Select-A-Size Paper Towels White 12 Mega Rolls            2.874553

我的问题是:

当数据集发生变化时,得分是否可能以这种方式改变?

如果可以,那么如何实现?

如果不行,那么我知道我的代码有错误...


作为一般规则,Lucene在不同查询(或在不同数据集上的相同查询)中的得分是不可比较的。如果您接受这个事实,您和Lucene将成为好朋友。重要的是,在两种情况下,两个“等效”的条目都获得了第一名,并且不太正确的条目排名第三(大约获胜分数的60-70%)。 - biziclop
1
我的回答中有一条评论提到我错误地阅读了第二个结果集中的结果顺序。我猜想在输入问题时可能出现了错误,导致结果2和3应该交换位置。我的假设正确吗? - femtoRgon
@femtoRgon 感谢您与Codo的讨论,我找到了代码中的错误。您是正确的,结果2和3应该交换,这不是我的问题中的笔误,而是我的代码中的错误导致了这种情况...... 我学到的是:如果两个字符串是彼此变异的(相同元素,不同位置),它们应该始终具有相同的分数(td-idf是每个元素分数的总和,相同元素,相同分数)。但是,当使用不同的数据集时,该分数可能会发生变化。非常感谢! - user2628641
1
@user2628641 如果两个字符串是彼此变异的(相同元素,不同位置),它们应该始终具有相同的分数。除非您使用接近搜索,请参见此问题作为示例。 - biziclop
问题:如果我不添加接近约束,只查询“白色纸巾”,那么据我所知,Lucene将查找所有包含“白色”或“纸”或“巾”的术语,并给出td-idf分数。因此,我认为我的陈述不仅适用于接近搜索。在我的示例中,在修复错误后,术语1和3得分相同,即使它们之间有4个距离。如果我理解有误,请纠正我。谢谢! - user2628641
@user2628641 不,你是正确的。我只是补充说,接近搜索是一个例外,因为它可以返回包含相同单词但顺序不同的文档的不同结果。 - biziclop
1个回答

1

这是完全正常的,不代表你的代码有错误。

当你的索引内容发生变化时,分数可能会改变,即使这些变化似乎与你的特定查询没有太大关系。分数实际上只在特定搜索执行的上下文中有效,因此它们的“绝对值”并不是真正重要的事情,而是值相对于查询其他结果是否合理。在两个结果集中,前两个得分相等,而另一个则显著较低。

这里改变的主要原因将是idf(逆文档频率)评分因子。这旨在更加重视整个索引中出现较少的术语,认为像“the”这样的常见术语作为搜索结果比像“geronimo”这样的不太常见的术语不那么有趣。

在你的情况下,在剩余的语料库可用的情况下,你最好的结果和第三个结果之间的比率略微缩小,因此“白色”和“mega”是更常见的(因此,不太有趣)术语比查询中的其他一些术语。


另外注意:您可以使用Lucene的IndexSearcher.explain方法获取有关文档得分原因的详细信息:

System.out.println(searcher.explain(query, docNumber).toString());

你的回答没有解释为什么两个具有相同单词(顺序不同)的文档得分不同。这对我来说非常令人惊讶,看起来像是一个错误。 - Codo
你的5000个文档中包含什么类型的数据,其他文档是否类似?在语料库中,“白色”似乎比“纸张”更独特(“纸张”几乎出现在所有文档中?),这就是为什么“纸张”的IDF分数使得你的第三个文档得分较低。但请检查每个匹配文档的“searcher.explain(query, docNumber)”并确认。 - Rushik
@Codo - 这似乎很奇怪,但这个问题并没有呈现出这种情况。如果你遇到了这种行为并且发现很难解释,请提出你自己的问题。 - femtoRgon
1
@femtoRgon 你确定吗?这个问题不是很具体。但是由于他/她明确提到了相同单词顺序不同的情况,这可能是他/她想要解释的内容。 - Codo
@Codo - 刚刚仔细看了一下,你是对的,第二个结果集看起来确实是那样的。不过,我强烈怀疑这是在输入问题时犯的错误。我已经在问题上进行了评论,请求澄清。 - femtoRgon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接