4

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

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

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

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

它通过使用以下公式来做到这一点:

state.getBoost() * ((float) (1.0 / Math.sqrt(numTerms)));

boost 始终为 1

之后,当我查询这些文档时,我看到在查询说明中我得到

  • 0.5 = fieldNorm(field=titre, doc=0)对于文件 A
  • 0.625 = fieldNorm(field=titre, doc=1)对于文件 B

这已经很奇怪了(对我来说,我敢肯定是我错过了一些东西)。为什么我得到的字段规范值与索引时计算的值不同?这是正在实施的“查询规范化”吗?如果是这样,它是如何工作的?

然而,这或多或少是可以的,因为两个查询时 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 个标记)

但是现在,当我查询这些文档时,我可以看到它们都具有与 explain 函数报告的相同的字段规范:

  • 1.5 = fieldNorm(field=titre, doc=0)对于文件 A
  • 1.5 = fieldNorm(field=titre, doc=1)对于文件 B

对我来说,这现在真的很奇怪,如果我在索引时使用一个明显很好的相似性来计算 fieldNorm,这给了我与令牌数量成正比的适当值,后来,在查询时,所有这些都丢失了,怎么办?查询说两个文档具有相同的字段规范?

所以我的问题是:

  • 为什么Similarity的computeNorm方法报告的索引时间fieldNorm与查询解释报告的索引时间不一样?
  • 为什么,对于在索引时获得的两个不同的 fieldNorm 值(通过相似性 computeNorm),我在查询时得到相同的 fieldNorm 值?

== 更新

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

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

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

4

1 回答 1

4

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

编码使用 3 位尾数、5 位指数和 15 处的零指数点,因此表示从大约 7x10^9 到 2x10^-9 的值,具有大约一位有效十进制数字的精度。零也被表示。负数四舍五入为零。太大而无法表示的值会向下舍入到最大的可表示值。太小而无法表示的正值被四舍五入到可表示的最小正值。

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

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

支持对规范值进行这种有损压缩的基本原理是,考虑到用户通过查询表达其真实信息需求的困难(和不准确性),只有大的差异才重要

于 2013-02-28T19:24:47.667 回答