• ElasticSearch7.3学习(二十四)----相关度评分机制详解


    1、算法介绍

    relevance score(相关性分数) 算法,简单来说,就是计算出,一个索引中的文本,与搜索文本,他们之间的关联匹配程度。Elasticsearch使用的是 term frequency/inverse document frequency算法,简称为TF/IDF算法。TF词频(Term Frequency),IDF逆向文件频率(Inverse Document Frequency)

    1.1 Term frequency

    搜索文本中的各个词条在field文本中出现了多少次,出现次数越多,就越相关。

    数学公司并不重要,看下面例子就清楚了

    1. 搜索请求:阿莫西林
    2. doc1:阿莫西林胶囊是什么。。。阿莫西林胶囊能做什么。。。。阿莫西林胶囊结构
    3. doc2:本药店有阿莫西林胶囊、红霉素胶囊、青霉素胶囊。。。

    很容易发现对于阿莫西林关键词来说在doc1中出现的次数大于doc2的,所以doc1的优先级高于doc2

    1.2 Inverse document frequency

    搜索文本中的各个词条在整个索引的所有文档中出现了多少次,出现的次数越多,就越不相关.

    首先看下面内容

    1. 搜索请求:阿莫西林胶囊
    2. doc1:A市健康大药房简介。本药店有阿莫西林胶囊、红霉素胶囊、青霉素胶囊。。。
    3. doc2:B市民生大药房简介。本药店有阿莫西林胶囊、红霉素胶囊、青霉素胶囊。。。
    4. doc3:C市未来大药房简介。本药店有阿莫西林胶囊、红霉素胶囊、青霉素胶囊。。。

    可以看到,对于关键词阿莫西林来说,所有的doc里面都包含这个关键词,那说明这个关键词不是那么重要,说明这个关键词所占的权重很低。再看下面内容

    1. 搜索请求:A市 阿莫西林胶囊
    2. doc1A市健康大药房简介。本药店有阿莫西林胶囊、红霉素胶囊、青霉素胶囊。。。
    3. doc2B市民生大药房简介。本药店有阿莫西林胶囊、红霉素胶囊、青霉素胶囊。。。
    4. doc3C市未来大药房简介。本药店有阿莫西林胶囊、红霉素胶囊、青霉素胶囊。。。

    再加上A市这个关键词,这样的话只有doc1里面才存在,这样的话权重才高,所以可以得出结论:整个索引库中出现的词的频率越小,那么相关度权重越高。

    1.3 Field-length norm

    除了上面两个因素影响相关度评分的计算之外,还有一个就是字段长度也会影响评分的计算。具体来说就是,field的长度越长,相关度越弱

    1. 搜索请求:A市 阿莫西林胶囊
    2. doc1:{"title":"A市健康大药房简介。","content":"本药店有、红霉素胶囊、青霉素胶囊。。。(一万字)"}
    3. doc2:{"title":"B市民生大药房简介。","content":"本药店有阿莫西林胶囊、红霉素胶囊、青霉素胶囊。。。(一万字)"}

    两个文档均只有一个字段被命中。为啥doc1>doc2,因为title字段的长度小于content的字段,几个字就命中相比于一万字才命中,当然几个字就命中的排在前面

    2、 _score是如何被计算出来的

    步骤如下:

    1. 对用户输入的关键词分词
    2. 每个分词分别计算对每个匹配文档的TF和IDF值
    3. 综合每个分词的TF/IDF值,利用公式计算每个文档总分
    4. 最后按照score降序返回

    可以举个例子来看一下。这里使用explain关键字来解释排序的过程。

    首先创建索引

    1. PUT /book/
    2. {
    3. "settings": {
    4. "number_of_shards": 1,
    5. "number_of_replicas": 0
    6. },
    7. "mappings": {
    8. "properties": {
    9. "name": {
    10. "type": "text",
    11. "analyzer": "ik_max_word",
    12. "search_analyzer": "ik_smart"
    13. },
    14. "description": {
    15. "type": "text",
    16. "analyzer": "ik_max_word",
    17. "search_analyzer": "ik_smart"
    18. },
    19. "studymodel": {
    20. "type": "keyword"
    21. },
    22. "price": {
    23. "type": "double"
    24. },
    25. "timestamp": {
    26. "type": "date",
    27. "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
    28. },
    29. "pic": {
    30. "type": "text",
    31. "index": false
    32. }
    33. }
    34. }
    35. }

    接着添加测试数据

    1. PUT /book/_doc/1
    2. {
    3. "name": "Bootstrap开发",
    4. "description": "Bootstrap是一个非常流行的开发框架。此开发框架可以帮助不擅长css页面开发的程序人员轻松的实现一个css,不受浏览器限制的精美界面css效果。",
    5. "studymodel": "201002",
    6. "price": 38.6,
    7. "timestamp": "2019-08-25 19:11:35",
    8. "pic": "group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg",
    9. "tags": [
    10. "bootstrap",
    11. "dev"
    12. ]
    13. }
    14. PUT /book/_doc/2
    15. {
    16. "name": "java编程思想",
    17. "description": "java语言是世界第一编程语言,在软件开发领域使用人数最多。",
    18. "studymodel": "201001",
    19. "price": 68.6,
    20. "timestamp": "2019-08-25 19:11:35",
    21. "pic": "group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg",
    22. "tags": [
    23. "java",
    24. "dev"
    25. ]
    26. }
    27. PUT /book/_doc/3
    28. {
    29. "name": "spring开发基础",
    30. "description": "spring 在java领域非常流行,java程序员都在用。",
    31. "studymodel": "201001",
    32. "price": 88.6,
    33. "timestamp": "2019-08-24 19:11:35",
    34. "pic": "group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg",
    35. "tags": [
    36. "spring",
    37. "java"
    38. ]
    39. }

    然后在使用如下命令查看_score的计算

    1. GET /book/_search?explain=true
    2. {
    3. "query": {
    4. "match": {
    5. "description": "java程序员"
    6. }
    7. }
    8. }

    返回的内容太多,这里只展示第一条的数据的内容

    查看代码

    1. {
    2. "_shard" : "[book][0]",
    3. "_node" : "Alyo4fMoSyCi6eo7A6t_XA",
    4. "_index" : "book",
    5. "_type" : "_doc",
    6. "_id" : "3",
    7. "_score" : 1.9788694,
    8. "_source" : {
    9. "name" : "spring开发基础",
    10. "description" : "spring 在java领域非常流行,java程序员都在用。",
    11. "studymodel" : "201001",
    12. "price" : 88.6,
    13. "timestamp" : "2019-08-24 19:11:35",
    14. "pic" : "group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg",
    15. "tags" : [
    16. "spring",
    17. "java"
    18. ]
    19. },
    20. "_explanation" : {
    21. "value" : 1.9788694,
    22. "description" : "sum of:",
    23. "details" : [
    24. {
    25. "value" : 0.7502767,
    26. "description" : "weight(description:java in 0) [PerFieldSimilarity], result of:",
    27. "details" : [
    28. {
    29. "value" : 0.7502767,
    30. "description" : "score(freq=2.0), product of:",
    31. "details" : [
    32. {
    33. "value" : 2.2,
    34. "description" : "boost",
    35. "details" : [ ]
    36. },
    37. {
    38. "value" : 0.47000363,
    39. "description" : "idf, computed as log(1 + (N - n + 0.5) / (n + 0.5)) from:",
    40. "details" : [
    41. {
    42. "value" : 2,
    43. "description" : "n, number of documents containing term",
    44. "details" : [ ]
    45. },
    46. {
    47. "value" : 3,
    48. "description" : "N, total number of documents with field",
    49. "details" : [ ]
    50. }
    51. ]
    52. },
    53. {
    54. "value" : 0.7256004,
    55. "description" : "tf, computed as freq / (freq + k1 * (1 - b + b * dl / avgdl)) from:",
    56. "details" : [
    57. {
    58. "value" : 2.0,
    59. "description" : "freq, occurrences of term within document",
    60. "details" : [ ]
    61. },
    62. {
    63. "value" : 1.2,
    64. "description" : "k1, term saturation parameter",
    65. "details" : [ ]
    66. },
    67. {
    68. "value" : 0.75,
    69. "description" : "b, length normalization parameter",
    70. "details" : [ ]
    71. },
    72. {
    73. "value" : 12.0,
    74. "description" : "dl, length of field",
    75. "details" : [ ]
    76. },
    77. {
    78. "value" : 23.666666,
    79. "description" : "avgdl, average length of field",
    80. "details" : [ ]
    81. }
    82. ]
    83. }
    84. ]
    85. }
    86. ]
    87. },
    88. {
    89. "value" : 1.2285928,
    90. "description" : "weight(description:程序员 in 0) [PerFieldSimilarity], result of:",
    91. "details" : [
    92. {
    93. "value" : 1.2285928,
    94. "description" : "score(freq=1.0), product of:",
    95. "details" : [
    96. {
    97. "value" : 2.2,
    98. "description" : "boost",
    99. "details" : [ ]
    100. },
    101. {
    102. "value" : 0.98082924,
    103. "description" : "idf, computed as log(1 + (N - n + 0.5) / (n + 0.5)) from:",
    104. "details" : [
    105. {
    106. "value" : 1,
    107. "description" : "n, number of documents containing term",
    108. "details" : [ ]
    109. },
    110. {
    111. "value" : 3,
    112. "description" : "N, total number of documents with field",
    113. "details" : [ ]
    114. }
    115. ]
    116. },
    117. {
    118. "value" : 0.56936646,
    119. "description" : "tf, computed as freq / (freq + k1 * (1 - b + b * dl / avgdl)) from:",
    120. "details" : [
    121. {
    122. "value" : 1.0,
    123. "description" : "freq, occurrences of term within document",
    124. "details" : [ ]
    125. },
    126. {
    127. "value" : 1.2,
    128. "description" : "k1, term saturation parameter",
    129. "details" : [ ]
    130. },
    131. {
    132. "value" : 0.75,
    133. "description" : "b, length normalization parameter",
    134. "details" : [ ]
    135. },
    136. {
    137. "value" : 12.0,
    138. "description" : "dl, length of field",
    139. "details" : [ ]
    140. },
    141. {
    142. "value" : 23.666666,
    143. "description" : "avgdl, average length of field",
    144. "details" : [ ]
    145. }
    146. ]
    147. }
    148. ]
    149. }
    150. ]
    151. }
    152. ]
    153. }
    154. },

    对于上面的返回结果,我们先看第一部分,首先就是返回的数据

    接着就是对评分计算的解释,按照上面给出的4个步骤分析,首先对关键词分词,这里分为了java 程序员两个关键词,先来看看java关键词的解释

    可以看到计算java关键词的tf,idf的值,同理在下方也能看到计算程序员关键词的tf,idf的值。

    最后将两个关键词合并起来在计算整个doc的总分,即得到最终的_score值,如下所示。

    3、document判断是否被匹配

    测试判断一个文档能不能被搜索到,适用于生产环境

    例如

    1. GET /book/_explain/1
    2. {
    3. "query": {
    4.   "match": {
    5.     "description": "java程序员"
    6.   }
    7. }
    8. }

    返回

    可以看到对于id为1的doc,并不能匹配到该文档,再来试一下id为3的数据

    1. GET /book/_explain/3
    2. {
    3. "query": {
    4. "match": {
    5. "description": "java程序员"
    6. }
    7. }
    8. }

    返回

    可以看到能够被匹配到,并且能够根据内容来分析为什么该文档能够被匹配到。

  • 相关阅读:
    不清楚用电脑怎么图片转文字?来看看这三个方法吧
    liteIDE 解决go root报错 go: cannot find GOROOT directory: c:\go
    【云原生】Spring Cloud是什么?Spring Cloud版本介绍
    【MySQL】数据库事务的理解~
    《算法导论》15.3 动态规划原理(含有矩阵链问题的改良&&C++代码)
    githubssh配置
    NXP i.MX 8M Mini开发板(4核 ARM Cortex-A53)硬件原理图规格说明书
    软考高级信息系统项目管理师系列论文十四:论大型信息系统项目的进度管理
    调整SGA遇到ORA-00845错误
    .NET集成DeveloperSharp实现强大的AOP
  • 原文地址:https://blog.csdn.net/FaithWh/article/details/126899210