我试图了解 fieldNorm 是如何计算的(在索引时),然后在查询时使用(并且显然是重新计算的)。

在所有示例中,我都使用没有停用词的 StandardAnalyzer。

在索引内容时调试 DefaultSimilaritycomputeNorm 方法,我注意到它返回 2 个特定文档:

  • 0.5 用于文档 A(其字段中有 4 个标记)
  • 0.70710677 用于文档 B(其字段中有 2 个标记)

  • 它通过使用以下公式来做到这一点:
    state.getBoost() * ((float) (1.0 / Math.sqrt(numTerms)));
    

    boost 始终为 1

    之后,当我查询这些文档时,我看到在查询解释中我得到
  • 文档 A 的 0.5 = fieldNorm(field=titre, doc=0)
  • 文档 B
  • 0.625 = fieldNorm(field=titre, doc=1)
    这已经很奇怪了(对我来说,我确定是我遗漏了什么)。为什么我得到的字段规范值与索引时计算的值不同?这是“查询规范化”的作用吗?如果是这样,它是如何工作的?

    然而,这或多或少是可以的,因为两个查询时 fieldNorm 给出的顺序与索引时计算的顺序相同(在这两种情况下,具有较短值的字段具有较高的 fieldNorm)

    然后,我创建了自己的 Similarity 类,在其中实现了 computeNorms 方法,如下所示:
    public float computeNorm(String pField, FieldInvertState state) {
        norm = (float) (state.getBoost() + (1.0d / Math.sqrt(state.getLength())));
        return norm;
    }
    

    在索引时,我现在得到:
  • 1.5 用于文档 A(其字段中有 4 个标记)
  • 1.7071068 用于文档 B(其字段中有 2 个标记)

  • 但是现在,当我查询这些文档时,我可以看到它们都具有与解释函数报告的相同的字段规范:
  • 文档 A 的 1.5 = fieldNorm(field=titre, doc=0)
  • 文档 B
  • 1.5 = fieldNorm(field=titre, doc=1)
    对我来说,这现在真的很奇怪,如果我在索引时使用明显良好的相似性来计算 fieldNorm,这给了我与 token 数量成正比的适当值,后来在查询时,所有这些都丢失了,查询 sais 两个文档具有相同的字段规范?

    所以我的问题是:
  • 为什么Similarity 的computeNorm 方法报告的索引时间fieldNorm 与查询解释报告的不一样?
  • 为什么对于在索引时获得的两个不同的 fieldNorm 值(通过相似性计算规范)我在查询时得到相同的 fieldNorm 值?

  • == 更新

    好的,我在 Lucene's docs 中找到了一些东西,它澄清了我的一些问题,但不是全部:

    然而,结果范数值在存储之前被编码为单个字节。在搜索时,从索引目录中读取规范字节值并解码回浮点规范值。这种编码/解码在减小索引大小的同时,也带来了精度损失的代价——不保证 decode(encode(x)) = x。例如,解码(编码(0.89))= 0.75。

    有多少精度损失?我们应该在不同的值之间设置一个最小的差距,以便即使在重新计算精度损失之后它们仍然保持不同?

    最佳答案

    encodeNormValue 的文档描述了编码步骤(这是精度丢失的地方),特别是值的最终表示:



    最相关的部分是,尾数只有 3 位,这意味着精度大约是一位有效的十进制数字。

    关于基本原理的一个重要说明是在您的引用结束后的几句话,Lucene 文档说:

    关于Lucene fieldNorm 相似度计算与查询时值的差异,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/15135872/

    10-15 16:15