zoukankan      html  css  js  c++  java
  • Elasticsearch搜索之explain评分分析

         

    Lucene的IndexSearcher提供一个explain方法,能够解释Document的Score是怎么得来的,具体每一部分的得分都可以详细地打印出来。这里用一个中文实例来纯手工验算一遍Lucene的评分算法,并且结合Lucene的源码做一个解释。

    首先是测试用例,我使用“北京东路”来检索一个含有address域的文档。

    然后是是输出,注意它有缩进,代表一个个的层级,下面以测试环境数据作为举例:

    {
            "value" : 0.7271681,
            "description" : "max of:",
            "details" : [ {
              "value" : 0.7271681,
              "description" : "sum of:",
              "details" : [ {
                "value" : 0.43069553,
                "description" : "weight(address:北京 in 787) [PerFieldSimilarity], result of:",
                "details" : [ {
                  "value" : 0.43069553,
                  "description" : "score(doc=787,freq=1.0), product of:",
                  "details" : [ {
                    "value" : 0.34374008,
                    "description" : "queryWeight, product of:",
                    "details" : [ {
                      "value" : 5.0118747,
                      "description" : "idf(docFreq=2104, maxDocs=116302)"
                    }, {
                      "value" : 0.06858513,
                      "description" : "queryNorm"
                    } ]
                  }, {
                    "value" : 1.2529687,
                    "description" : "fieldWeight in 787, product of:",
                    "details" : [ {
                      "value" : 1.0,
                      "description" : "tf(freq=1.0), with freq of:",
                      "details" : [ {
                        "value" : 1.0,
                        "description" : "termFreq=1.0"
                      } ]
                    }, {
                      "value" : 5.0118747,
                      "description" : "idf(docFreq=2104, maxDocs=116302)"
                    }, {
                      "value" : 0.25,
                      "description" : "fieldNorm(doc=787)"
                    } ]
                  } ]
                } ]
              }, {
                "value" : 0.29647252,
                "description" : "weight(address:东路 in 787) [PerFieldSimilarity], result of:",
                "details" : [ {
                  "value" : 0.29647252,
                  "description" : "score(doc=787,freq=1.0), product of:",
                  "details" : [ {
                    "value" : 0.2851919,
                    "description" : "queryWeight, product of:",
                    "details" : [ {
                      "value" : 4.158218,
                      "description" : "idf(docFreq=4942, maxDocs=116302)"
                    }, {
                      "value" : 0.06858513,
                      "description" : "queryNorm"
                    } ]
                  }, {
                    "value" : 1.0395545,
                    "description" : "fieldWeight in 787, product of:",
                    "details" : [ {
                      "value" : 1.0,
                      "description" : "tf(freq=1.0), with freq of:",
                      "details" : [ {
                        "value" : 1.0,
                        "description" : "termFreq=1.0"
                      } ]
                    }, {
                      "value" : 4.158218,
                      "description" : "idf(docFreq=4942, maxDocs=116302)"
                    }, {
                      "value" : 0.25,
                      "description" : "fieldNorm(doc=787)"
                    } ]
                  } ]
                } ]
              } ]
            } ]
          }

    这个看起来可真是头疼,尝试解释一下:

    首先,需要学习Lucene的评分计算公式——

    分值计算方式为查询语句q中每个项t与文档d的匹配分值之和,当然还有权重的因素。其中每一项的意思如下表所示:

    表3.5

    评分公式中的因子

    评分因子

    描 述

    tf(t in d)

    项频率因子——文档(d)中出现项(t)的频率

    idf(t)

    项在倒排文档中出现的频率:它被用来衡量项的“唯一”性.出现频率较高的term具有较低的idf,出现较少的term具有较高的idf

    boost(t.field in d)

    域和文档的加权,在索引期间设置.你可以用该方法 对某个域或文档进行静态单独加权

    lengthNorm(t.field in d)

    域的归一化(Normalization)值,表示域中包含的项数量.该值在索引期间计算,并保存在索引norm中.对于该因子,更短的域(或更少的语汇单元)能获得更大的加权

    coord(q,d)

    协调因子(Coordination factor),基于文档中包含查询的项个数.该因子会对包含更多搜索项的文档进行类似AND的加权

    queryNorm(q)

    每个査询的归一化值,指毎个查询项权重的平方和

    总匹配分值的计算

    具体到上面的测试来讲,地址字段address匹配了二个词条,先分别计算每个词条对应的分值,然后相加,最后结果= ("北京") 0.43069553+ (“东路”)0.29647252=0.7271681 (结果舍入)。

    查询语句在某个field匹配分值计算

    这个0.43069553是如何来的呢?这是词条“北京”在field中的分值=查询权重queryWeight * 域权重fieldWeight  即   0.34374008*1.2529687=0.43069553。

     同埋“东路”这个词条在field中的分值=查询权重queryWeight * 域权重fieldWeight  即   0.2851919*1.0395545=0.29647252

    queryWeight的计算

    queryWeight的计算可以在TermQuery$TermWeight.normalize(float)方法中看到计算的实现:

    public void normalize(float queryNorm) {

                  this.queryNorm = queryNorm;

                 //原来queryWeight 为idf*t.getBoost(),现在为queryNorm*idf*t.getBoost()。

                queryWeight *= queryNorm;

                value = queryWeight * idf;

    }

    其实默认情况下,queryWeight = idf * queryNorm,因为Lucene中默认的boost = 1.0。

    以“北京”这个词条为例,查询权重queryWeight = idf * queryNorm,即 0.34374008 = 5.0118747*0.06858513。

    idf的计算

    idf是项在倒排文档中出现的频率,计算方式为

        /** Implemented as <code>log(numDocs/(docFreq+1)) + 1</code>. */

       @Overrid

       public float idf(long docFreq, long numDocs) {

            return (float)(Math.log(numDocs/(double)(docFreq+1)) + 1.0);

      }

     docFreq是根据指定关键字进行检索,检索到的Document的数量,我们测试“北京”词条的docFreq=2104;

     numDocs是指索引文件中总共的Document的数量,对应explain结果中的maxDocs,我们测试的maxDocs=116302。

     用计算器验证一下,没有错误,这里就不啰嗦了。

    fieldWeight的计算

    fieldWeight = tf * idf * fieldNorm

    tf和idf的计算参考前面的,fieldNorm的计算在索引的时候确定了,此时直接从索引文件中读取,这个方法并没有给出直接的计算。

    如果使用DefaultSimilarity的话,它实际上就是lengthNorm,域越长的话Norm越小,在org/apache/lucene/search/similarities/DefaultSimilarity.java里面有关于它的计算:

     public float lengthNorm(FieldInvertState state) {

            final int numTerms;

            if (discountOverlaps)

               numTerms = state.getLength() - state.getNumOverlap();

            else

               numTerms = state.getLength();

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

     }

    这个我就不再验算了,每个域的Terms数量开方求倒数乘以该域的boost得出最终的结果。

  • 相关阅读:
    注册事件为何无效,没有触发? AUTOPOSTBACK已经设置为TRUE
    不使用web服务实现文本框自动完成扩展
    实现一个搜索功能思路
    QT练习3:QT数字和滚动条关联
    Qt 显示图片 放大 缩小 移动
    QT 的信号与槽机制介绍
    一个有很多QT免费应用例程的网站
    yafeilinux.com的开源项目非常好的东西
    vector的用法
    C语言实现获取LINUX当前时间
  • 原文地址:https://www.cnblogs.com/clonen/p/6674955.html
Copyright © 2011-2022 走看看