【问题标题】:Elastic Search - Boosting/Scoring - Two words with different length弹性搜索 - 提升/评分 - 两个不同长度的词
【发布时间】:2014-12-03 01:39:33
【问题描述】:

使用查询“text”查询字段时,找到两个带有“text abcd”和“text ab”的文档,它们都得到相同的分数。

有没有办法增加'text ab'的分数,因为它更短?

【问题讨论】:

  • 如果您像GET /foos/foo/_search?search_type=dfs_query_then_fetch { "query": { "match": { "name": "foo" } } } 一样执行查询,您会得到什么结果? (意思是加?search_type=dfs_query_then_fetch后)
  • 我明白你在说什么,它是关于在分片之间共享的文档。但我的问题是 - 我们得到了 'foo abcd' 和 'foo ab',有没有办法根据字段规范或函数得分将 'foo ab' 得分高于 'foo abcd'。
  • 有点复杂。因为你没有提供太多细节(我知道你相信你有,但是搜索不止一个方面:-)),我会给你一些想法:scoring with scripts你可以使用一些更低的水平的东西来计算/操纵评分,token count 如果您可能想按字段中的术语数“排序”。
  • ab 和 abcd 都对分数没有贡献,因为这两个标记都与您的文本不匹配(因此它们都排名 0)。您将获得两个查询的“文本”标记的完全匹配。根本不考虑不匹配标记的长度。我看不到这样做的简单方法。另外,我真的不明白你为什么要这样开始。或许你可以解释一下你到底想要什么?
  • 我只是在探索相关性的可能选项。好的,函数 score 可用于使用 groovy 脚本来操作分数。谢谢

标签: java indexing lucene elasticsearch


【解决方案1】:

这似乎是基于对 lucene 评分的长度的误解。将 tokens 视为索引文本的原子单元而不是字符是很有用的。 lucene 在评分中考虑的长度是字段中标记的数量。您指示的两个字段都恰好有两个标记。它们的长度相同,因此它们的长度范数也相等,并且不会影响相对得分。

如果您有一个包含三个术语的字段,您实际上会看到长度对分数的影响:

  • 字段:“文本 ab”--lengthnorm = 1/√2 = 0.7
  • 字段:“文本 abcd”--lengthnorm = 1/√2 = 0.7
  • 字段:“text abc def ghi”--lengthnorm = 1/√4 = 0.5

该标准乘以分数,因此最后列出的文档的分数会低一些。


如果您不赞成以术语而不是字符来考虑内容的想法:

由于您正在考虑的长度适用于字符,因此实现这一点肯定会有些违反规定。不过,您在考虑规范方面走在正确的轨道上。这绝对应该在索引时进行预处理并作为标准存储。

您需要在自定义相似性类中实现这一点。我假设我们喜欢DefaultSimilarity 的其余部分,因此您可以扩展它,并覆盖LengthNorm 以使其变得简单。你可以很容易地利用字段偏移来获得:

public class MySimilarity extends DefaultSimilarity {
    @Override
    public float lengthNorm(FieldInvertState state) {
        return state.getBoost() * ((float) (1.0 / Math.sqrt(state.getOffset())));
    }
}

你有它。给定文档和查询的测试运行显示:

  • 字段:“文本 ab”——总分 = 0.18579213
  • 字段:“文本 abcd”——总分 = 0.18579213
  • 字段:“文本 abcdefghi”——总分 = 0.1486337

所以,你可以从我添加的较长文档中看到它正在工作,那么为什么“text ab”和“text abcd”仍然具有相同的分数?

规范以超压缩形式存储在单个字节中。它们只有一个 3 位尾数,这使它们的精度略低于 1 个十进制数字。因此,考虑到压缩方案,仅添加这两个字符的差异是不够的。当谈到这种提升时,普遍的看法是:“只有大的差异才重要”(参见the DefaultSimilarity documentation


所以,“谁在乎在搜索时节省一些内存?细微的差异对我来说很重要!”,我听到你说。

好的,您需要覆盖encodeNormdecodeNorm。由于这些在DefaultSimilarity 中是最终的,因此您需要扩展TFIDFSimilarity。我只是从复制DefaultSimilarity 的源代码开始。最后你可以使用这样的东西:

public class MySimilarity extends TFIDFSimilarity {

    public MySimilarity() {}

    @Override
    public float coord(int overlap, int maxOverlap) {
        return overlap / (float)maxOverlap;
    }

    @Override
    public float queryNorm(float sumOfSquaredWeights) {
        return (float)(1.0 / Math.sqrt(sumOfSquaredWeights));
    }

    //Since length norms are generally going to leave us with results less than one, multiply
    //by a sufficiently large number to not lose all our precision when casting to long
    private static final float NORM_ADJUSTMENT = Integer.MAX_VALUE;

    @Override
    public final long encodeNormValue(float f) {
        return (long) (f * NORM_ADJUSTMENT);
    }

    @Override
    public final float decodeNormValue(long norm) {
        System.out.println(norm);
        return ((float) norm) / NORM_ADJUSTMENT;
    }

    @Override
    public float lengthNorm(FieldInvertState state) {
        return state.getBoost() * ((float) (1.0 / Math.sqrt(state.getOffset())));
    }

    @Override
    public float tf(float freq) {
        return (float)Math.sqrt(freq);
    }

    @Override
    public float sloppyFreq(int distance) {
        return 1.0f / (distance + 1);
    }

    @Override
    public float scorePayload(int doc, int start, int end, BytesRef payload) {
        return 1;
    }

    @Override
    public float idf(long docFreq, long numDocs) {
        return (float)(Math.log(numDocs/(double)(docFreq+1)) + 1.0);
    }

    @Override
    public String toString() {
        return "DefaultSimilarity";
    }
}

现在我得到了:

  • 字段:“文本 ab”——总分 = 0.2518424
  • 字段:“文本 abcd”——总分 = 0.22525471
  • 字段:“文本 abcdefghi”——总分 = 0.1839197

【讨论】:

  • 感谢您的回复和知识分享。
  • 感谢您的解释。
猜你喜欢
  • 1970-01-01
  • 2020-09-05
  • 1970-01-01
  • 1970-01-01
  • 2018-01-12
  • 1970-01-01
  • 2021-11-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多