• Lucene 之又一奇葩设定:lengthNorm 导致文章越长,排名越后


    Lucene 哪怕4.7,其基于分页的搜索效率也是极高,几乎不用开多线程。

    在上一篇文章中,我通过用自定义 WordBreakFilter 解决了 CJKBigramFilter 建立过多无用 term 的问题。最终目的都是相对于只有StandardAnalyzer,可以大幅地改善结果排序,更加精确地命中汉语词组。

    但是最近又发现一个排序上的错误:以开心为关键词全文搜索《牛津英语词典》之时,词条“unrip” 竟然排在 “clap” 前面。

    生僻词 unrip :

    解释很短,完全没有“开心”二字,仅仅是恰好对上了三次“开”字而已

    unrip
    /ˌʌnˈrɪp/  
    verb
    (unripped, unripping)[with obj.]
    rare open by ripping
    〈罕〉撕开, 扯开:
    he carefully unripped one of the seams.
    
    他小心地拆开其中一条缝。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    常用词 clap:

    解释很长,且有“开心”二字(也是三次命中,但是是全命中啊!开心、开、心)。就因为解释内容篇幅长,总评分就被 lengthNorm 系数拉低了。

    ……
    ……
    ……
    Agnes clapped her hands in glee
    
    阿格尼丝开心地鼓掌
    ……
    ……
    ……
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    文档误我,说什么要会用searcher.explain(query, id),结果这打印出来的东西是给人看的?一大串数字,只有最终的分数对的上,其他都是蒙蔽。

    0.07699502 = (MATCH) product of:
          0.11549253 = (MATCH) sum of:
            0.07321933 = (MATCH) weight(content:in 1) [DefaultSimilarity], result of:
              0.07321933 = score(doc=1,freq=3.0 = termFreq=3.0
        ), product of:
                0.45505905 = queryWeight, product of:
                  0.5945349 = idf(docFreq=2, maxDocs=2)
                  0.76540345 = queryNorm
                0.16090071 = fieldWeight in 1, product of:
                  1.7320508 = tf(freq=3.0), with freq of:
                    3.0 = termFreq=3.0
                  0.5945349 = idf(docFreq=2, maxDocs=2)
                  0.15625 = fieldNorm(doc=1)
            0.0422732 = (MATCH) weight(content:in 1) [DefaultSimilarity], result of:
              0.0422732 = score(doc=1,freq=1.0 = termFreq=1.0
        ), product of:
                0.45505905 = queryWeight, product of:
                  0.5945349 = idf(docFreq=2, maxDocs=2)
                  0.76540345 = queryNorm
                0.092896074 = fieldWeight in 1, product of:
                  1.0 = tf(freq=1.0), with freq of:
                    1.0 = termFreq=1.0
                  0.5945349 = idf(docFreq=2, maxDocs=2)
                  0.15625 = fieldNorm(doc=1)
          0.6666667 = coord(2/3)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    然后百度Lucene评分原理,原来,文章越长、解析出的关键词越多,搜索的时候,评分就会相应地降低。

    所以需要继续魔改Lucene-core模块,计算 lengthNorm 的时候强制关键词数量为固定值。(DefaultSimilarity)

      @Override
      public float lengthNorm(FieldInvertState state) { // 我谢谢你
        int numTerms;
        if (discountOverlaps)
          numTerms = state.getLength() - state.getNumOverlap();
        else
          numTerms = state.getLength();
    	  numTerms = 10; // 固定值
       return state.getBoost() * ((float) (1.0 / Math.sqrt(numTerms)));
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    其实,我认为这个 lengthNorm 应该和总分数分开来算,不要简单得用乘法乘上去,太粗暴了!应该仅在总评分一致、命中关键词数量和频率都一致的情况下,进一步影响排名。不过这个功能太过复杂,暂时还是快刀斩乱麻,返回它一个固定的 lengthNorm 吧?

    其他参考资料:长度归一化/omitNorms=false 有什么好处? - 堆栈溢出

    lucene - 在单个字段上禁用长度规范化? - Stack Overflow

    深入理解Lucene默认打分算法

    lucene score explain 评分解释说明


    如果“恰巧”命中更多的单字会怎么样,unrip还会排在clap之前吗?还真会…

    不过要数量特别大才行,测试中,50个“开”字才堪堪打败一个连起来的“开心”二字。

    呵呵,图样图森破,sometimes naïve!

  • 相关阅读:
    ZOOM 校招 几道C语言小题
    计算机毕业设计ssm宠物托运网站8m8iz系统+程序+源码+lw+远程部署
    Spring Boot中的微信支付(小程序)
    Go对Json的支持[encoding/json]
    学习笔记-MongoDB(命令增删改查,聚合,权限管理,索引,java使用)
    功能强大的开源网络监控工具:LibreNMS,牛逼!
    无信号交叉口车辆通行控制研究
    【PyTorch深度学习项目实战100例】—— 基于PaddleOCR识别火车票信息 | 第65例
    服务器之Apollo单机部署(快速安装)
    SAP 销售订单审批状态参数设置
  • 原文地址:https://blog.csdn.net/sinat_27171121/article/details/126791013