读书笔记《elasticsearch-blueprints》关联度和评分
由于我们处于大数据时代,因此仅首先显示最相关的结果非常重要。这意味着即使有数以百万计的项目符合我们的要求,我们通常也只有耐心地通过最有限数量的项目。例如,当我们在 Google 中搜索某些内容时,它会抛出类似 234,324,000,000 个结果,其中大多数人甚至不会进入第二页。我们通常用第一页或前 10 个结果来约束自己。如果前 10 个结果不相关,我们假设下一个或剩余的文档比当前文档更不相关并且也跳过它们。 Internet 浏览的这种性质为相关性和评分算法的改进开辟了巨大的空间,尤其是在大数据领域,因为我们必须处理大量的文档,其中大多数文档在各自的上下文中很重要。
对电子商务网站的价值评分和相关性是无价的。电子商务网站的核心成功取决于他们可以将多少客户转化为买家。一种好的策略是了解用户的行为模式,并用他们正在寻找的产品来吸引他们。地理位置信息也可用于为他们提供更有意义的结果。因此,电子商务企业将一切都归功于良好的相关性算法,该算法可以向用户展示更有意义的结果并将其转化为买家。
现在,找到相关或有意义的结果的良好评分算法是什么?它可以是很多东西。它可以是用户的查询与特定文档的匹配程度。所以在新闻数据库中,这很有意义。如果一个人在印度搜索旋风,那么有关旋风和印度的新闻将比仅有关旋风的新闻更有意义。在另一种情况下,根据地理位置排序的结果可能是有意义的。例如,如果我搜索餐馆,那么我所在地区附近的餐馆对我来说比半个地球以外的餐馆更有意义。在某些情况下,我可能想了解客户对一组产品的行为。我可能想随机排列物品并观察一种模式,在这种模式下,客户可能会以任何放置的顺序偏爱物品。在这种情况下,我可能想使用随机排序算法。
Elasticsearch 为您提供的评分和相关性支持远远领先于时代。您可以根据统计词频-逆文档频率(TF-IDF)、自定义编写的脚本、过滤器、数字衰减、日期甚至位置和随机性。更好的是,您可以将所有这些组合在一起以构建适合您要求的自定义相关性算法。
评分是 过程,我们确定查询与文档的匹配程度。这可以基于查询与其匹配的文本的相关性,也可以基于我们感兴趣的逻辑。例如,在像 Zomato 这样的网站上进行餐厅搜索时,餐厅离得越近,匹配就越好。在另一个例子中,在新闻搜索中,最近的新闻和特定主题的组合会提供更好的匹配。
我们将在本章后面部分介绍如何在评分中引入自定义逻辑。在本节中,我们将讨论如何根据查询文本对文本进行评分。
确定针对文本字段的查询得分的三个主要因素如下:
词频:让我们假设我们正在搜索单词
elephant
,我们得到如下结果:Document-1:
大象有两种,非洲象和印度象。
Document-2:
大象、牛和狗在街上游荡
。
哪个更适合搭配?显然,Document-1 将是一个更好的匹配。这归因于“大象”一词在 Document-1 中的 比在 Document-2 中出现的次数多。这实际上意味着大象这个词在 Document-1 中比在 Document-2 中更重要。
逆文档频率:假设我们有一个书籍数据库。里面可以有各种书名。现在,如果我搜索
Java book
,如果我处理令牌java
和book
一样吗?很可能不是因为java
非常以我们的搜索为中心,而是令牌book
不太重要,因为它会非常重要在整个索引中通用。让我们再举一个例子:
Document-1:
Packt 编写的 Python 书籍
Document-2:
Packt 的 Java
Document-3:
Ruby 书
假设,我正在搜索字符串
Java book
。现在,令牌java
存在于 Document-2 中,令牌book
存在于 Document-1 中。给予 Document-1 和 Document-2 同等的权重是否有意义?好吧,当然不是。这是因为book
是整个索引中非常常见的标记,处理标记java
和 < code class="literal">book 一样。因此,我们使用数学模型来计算这个区分因子。它被称为逆文档频率(IDF)。IDF 计算令牌的常见程度并在搜索期间降低其权重。因此,稀有术语比常见术语更受青睐。
字段长度:文档字符串的字段长度也很重要。 假设字符串越短,匹配越好是安全的。
让我们看一个例子:
Document-1:
Facebook 是最好的社交媒体网站之一
Document-2:
Facebook、Orkut、G+ 是一些最好的社交媒体网站
当 我们搜索词
facebook
时,这些文档中哪一个更匹配? Document-1 将是一个更好的匹配。这是因为字段长度越短,我们就越能假设匹配的词与该文档具有更好的相关性。
为查询计算的每个文档的默认分数是词频、逆文档频率和字段长度标准化的函数。
当我们 获得查询结果时,我们通常会获得匹配的文档及其计算得分。该分数定义了查询与该文档之间的匹配程度。如前所述,可以使用 TF-IDF 算法或根据用户要求使用一些自定义分数来计算此值。在许多情况下,我们需要了解这个值是如何计算的,否则整个评分过程可能看起来像一个黑匣子。在 这种情况下,explain 标志可以帮助我们。
启用解释标志为每个文档提供了一个简洁的 JSON 文件,简要说明了用于计算分数的不同函数、其输入值和输出。最后,它简要解释了它们是如何结合起来产生最终得分或结果的。
要启用该标志,请应用以下查询:
上述查询的结果如下:
此文档来自的节点值和分片值。如果我们不使用说明标志,则此值不可用。
下面显示了如何组合词频、逆文档频率和字段归一化的分数。
词频计算:
场归一化计算:
2014年,世界目睹了一种名为埃博拉的危险流行病的爆发。当埃博拉病毒首次爆发时,世界各地的科学家都开始研究这种疾病。他们对疾病的名称和名称都不是很确定,但他们知道一种疾病已经出现,他们也知道它的一些症状。美国的一位生物技术科学家想了解这种疾病的爆发并想了解它的模式。出于这个原因,他从报告该疾病的多家非洲医院获得了医疗记录。他知道这种新出生的疾病的症状,并想查看医疗记录,以便找到最有可能包含这种疾病症状的记录,我们现在将其称为埃博拉病毒。
标题:提供描述标题的非结构化单行文本。
描述:详细分析疾病、症状、患者、尝试的药物以及患者的当前状况。该字段也是非结构化的,但可以保证该字段很好地捕捉到疾病的症状。
PatientPastHealthIndex:这是一个结构化字段。它给出了一个介于 1 和 10 之间的指数。这个数字表示患者过去的健康状况。如果患者过去非常健康并且去医院的次数较少,那么他将得到 10。如果患者非常不健康,过去曾被诊断出患有疾病,并且曾多次入院,那么他的指数就是1。在做这个指数的时候,还考虑了糖、胆固醇和其他不健康的辅助疾病的存在。
LocationPastHealthIndex:这表示一个表示区域健康程度的索引。如果是报告疾病多、住院人数较多的地区,则指数为 1。如果是非常健康且住院人数较少的地区,则指数为 10。
PatientsWithSimilarSymptom:该数字表示医院中报告类似症状的患者数量。
日期:这是报告该患者疾病的日期。
Location:这代表医院的经纬度。
科学家想尝试各种文档排序机制,看看哪种方案更适合。以下是他想到的各种方案,以帮助他对记录进行排序,使出现新疾病的可能性更大的记录首先出现。
问题:科学家注意到,出现较多的症状被标记在标题中,不太突出的症状被包含在描述中。因此,当他搜索症状时,他想同时搜索标题和 description 中的术语。此外,他认为标题上的匹配应该比描述中的匹配更优先。
如何解决:这个可以使用匹配查询或 功能得分查询。然而,最快最简单的方法是使用 multi_match
查询。我们可以在多个字段上运行匹配查询,并为每个字段提供匹配首选项:
询问:
在前面的代码中,我们通过将 title
字段的优先级提高了 10 倍于 content 字段,从而提高了它的重要性,即赋予它 a^10
表达式:
回复:
由于 nausea
在标题和内容字段中都出现了,因此该文档首先出现:
虽然 nausea
出现在第一个文档中,因为标题字段的长度较大,为第一个文档计算更多相关性:
由于标题字段中不存在令牌 Nausea,因此该文档的相关性降低:
问题:为了开始他的工作,科学家认为可以选择查看最近的 医学期刊会帮助他评估当前的情况。因此,他想搜索某些术语,例如恶心、呕吐等,这些术语更像是埃博拉病毒的症状,并查看包含这些术语的最新期刊。
如何解决这个问题:function
查询最适合这个问题。我们可以在查询部分提及查询,然后使用字段函数将日期字段标记为评分机制。稍后,我们可以将查询给出的分数替换为 date 函数给出的分数,以便返回文档的顺序基于 期刊是。
以下代码段用于发出请求:
包括日期的值作为分数:
将查询生成的分数替换为我们的函数计算的分数:
以下是对上述请求的响应:
分数 |
时间 |
标题 |
---|---|---|
1.41481804E12 |
2014-11-01 05:01:01 |
发现发烧样疾病。治疗无效。 |
1.41480362E12 |
2014-11-01 01:01:02 |
坦桑尼亚不明疾病症状。 |
1.4121325E12 |
2014-10-01 03:02:05 |
恶心和呕吐严重。 |
1.3912201E12 |
2014-02-01 02:01:01 |
患者出现恶心和头痛,然后咳嗽。 |
问题:接下来,科学家觉得他需要最新的文档和最高的文档 成为埃博拉病例的概率。他觉得埃博拉病毒是一种致命的疾病,它甚至可以感染一个健康的人。所以,如果一个健康指数高的人患了某种疾病,他还是要查看那个人的病历。他还想要最新的此类文件,但新近不应取代健康人被感染的想法并获得更多优先权。
如何解决:在这里,由于我们需要排序方案中的新近度概念,我们根据日期而不是根据日期对医学期刊进行排序整个时间戳。这意味着 21 日出版的医学期刊将比 20 日出版的医学期刊具有更高的优先级。但是,同一天的两本医学期刊不会有相同的优先级。同一天,如果我们可以根据PersonHealthIndex
对期刊进行排序,这将把健康人受到影响的概念带入方程式。因此,通过将这两者结合起来,我们可以将新近度的概念和健康人被感染的概念带到记录的排序中。为此,我们也可以使用函数查询。我们将使用脚本函数从日期字段中提取日期(而不是时间)信息。然后我们可以使用字段函数来提取 PersonHealthIndex
。现在,将这两个值相加或相乘可能无法为您提供准确的总分值。这是因为时间戳值远高于 PersonHealthIndex
。因此,更好的办法是将时间戳记纪元值乘以 10,即 PersonHealthIndex
的范围,然后将其添加到 PersonHealthIndex
。为此,我们可以使用函数查询的添加
boostType
参数。
请求:
我们使用脚本得分函数来获得表示一年中某一天的得分。我们需要将 从中剔除出小时/分钟/秒的感知。因此,我们将 epoch 乘以一天的时间,得到一个仅衡量一天中时间的分数:
接下来,我们需要计算一个表示患者过去健康状况的分数:
然后,我们计算每个函数给出的分数:
我们用查询给出的分数替换函数的分数:
响应:
_分数 |
日期 |
患者过去健康指数 |
标题 |
---|---|---|---|
16384.0 |
2014-11-01 01:01:02 |
9 |
坦桑尼亚不明疾病症状。 |
16378.0 |
2014-11-01 05:01:01 |
3 |
发现发烧样疾病。治疗无效。 |
16350。 |
2014-10-01 03:02:05 |
6 |
恶心和呕吐严重。 |
16105.0 |
2014-02-01 02:01:01 |
3 |
患者出现恶心和头痛,然后咳嗽。 |
问题:很明显,还有另一种疾病与埃博拉病毒有相似的症状。这种疾病被误解或错误地诊断为埃博拉病毒。这位科学家发现了这种被错误诊断的疾病 的不同症状,并希望单独对这些症状进行负面促进。他还想加强那些更有可能被诊断为埃博拉病毒的症状。因此,他想出了一组他想加强的症状和另一组他想消极加强的症状。
如何解决:最好的查询候选是 boost 查询。在 boost 查询中,我们可以分配一组 查询,其 boost 值被赋予正 boost 和另一组查询,其 boost 值被赋予负促进。因此,对阳性部分中阳性症状的标题和描述的匹配查询以及对阴性部分中阴性症状的标题和描述的匹配查询应该可以解决问题。
请求:
我们负面地增强头痛和咳嗽这两个词,因为这些症状可能表明它不是埃博拉病毒,而是其他一些疾病:
应用 2 的负值提升,这是一个必填字段:
上述代码的响应如下:
问题:这位科学家看到有大量的病历,于是从一所著名的医学院任命了一组实习生来研究这些病历。他不想做任何特定的评分,因为他担心这可能会导致结果有偏差或对实习生的判断产生偏见。在搜索期刊并显示最佳结果后,他想看看实习生打开他们认为更可疑的医学期刊的模式。由于没有具体的顺序,这些 实习生翻阅了所有期刊的标题,并选择开设某些期刊进行进一步研究。这位科学家认为,以这种方式研究的文件可能对他的研究更重要。因此,他想为不同的实习生展示不同的文件顺序。这意味着,如果实习生基于某些关键字进行搜索,那么对于同样基于同一组关键字进行搜索的不同实习生,结果的顺序将不同。因此,科学家希望每次搜索的结果排序不同,以便了解实习生的学习模式。
如何解决:函数分数查询中提供的随机函数最适合这个问题。这个 函数每次使用都会生成一个随机分数。因此,对于每个搜索命中,将完全不同的顺序作为分数。我们将文档的查询分数乘以这个值,以便搜索查询对结果的顺序有一些影响。我们尊重查询与文档的紧密匹配,但也保持随机性。
请求:
此 请求计算随机分数。因此,我们可以在每个请求中实现不同的顺序:
我们将此随机分数与查询计算的分数相乘,以便查询对结果排序的影响最小:
响应:
对于每个查询,此查询的响应会有所不同。因此,我们可以观察到不同查询的顺序略有不同:
文件顺序 |
第一个请求 |
第二个请求 |
第三个请求 |
第四个请求 |
---|---|---|---|---|
#1 |
文件A |
文件D |
文件B |
文件A |
#2 |
文件C |
文件C |
文件D |
文件C |
#3 |
文件B |
文件B |
文件C |
文件B |
#4 |
文件D |
文件A |
文件A |
文件D |
问题:通过根据日期对医学期刊进行排序并检查患者的密度 谁报告了埃博拉病毒,科学家可以准确地找到埃博拉病毒的起源区域。他搜索了提到埃博拉病毒症状的医学期刊,并根据日期对其进行了排序。从而找到了最初报告埃博拉病毒的地区。现在,这位科学家觉得在这里可以找到受埃博拉病毒影响时间最长的患者。如果能找到这样的患者,他可以研究埃博拉病毒的长期影响以及他们死亡的平均时间。所以现在,他想搜索离某个特定地点最近的医学期刊,以及那些可能出现埃博拉病毒症状的期刊,最终会感染患者。最终的症状是鼻子、嘴巴和其他孔道出血。
如何解决:即使在这里,我们友好的函数查询也能帮上忙。我们可以 使用查询部分来过滤出现埃博拉症状的期刊,然后我们可以使用位置衰减函数来决定排序。
查询:
响应:
_分数 |
地理位置 |
标题 |
---|---|---|
0.9150084 |
2.226,2.319 |
坦桑尼亚不明疾病症状。 |
0.7876279 |
2.2242,2.333 |
患者出现恶心和头痛,随后咳嗽。 |
0.7828477 |
2.227,2.3329 |
发现发烧样疾病。治疗无效。 |
0.7680406 |
2.224,2.335 |
恶心和呕吐严重。 |
问题:科学家现在想看看埃博拉病毒对已经被证明不健康的人的影响 的地方,因为他们的病史很差。他认为,埃博拉病毒爆发周围 10 公里的任何距离都应被视为具有同等的相关性。超出此范围的任何距离都应按距离顺序具有较小的相关性。也就是说,距离埃博拉疫情越远,相关性越小。现在,报告健康史不佳的医院医疗记录应该被赋予更大的相关性。
如何解决:函数分数查询是解决这个问题的最佳选择。我们将为每个相关性评分逻辑编织 两个函数,并使用乘法运算将它们组合起来。现在,该分数将替换为 为查询生成的分数以实现功能。为了实现距离相关的逻辑,我们可以在位置字段上使用衰减函数。衰减函数中有一个偏移属性,它控制相关性计算开始衰减的点。我们可以将其设置为 10 公里。 LocationHealthIndex
上的另一个衰减函数可用于枚举第二个相关逻辑。
请求:
响应:
分数 |
地点 |
位置健康指数 |
标题 |
---|---|---|---|
0.057570856 |
2.224,2.335 |
2 |
恶心和呕吐严重。 |
1.5258789E-5 |
2.226,2.319 |
4 |
坦桑尼亚不明疾病症状。 |
1.4221428E-5 |
2.2242,2.333 |
4 |
患者出现恶心和头痛,然后咳嗽。 |
3.843742E-25 |
2.227,2.3329 |
9 |
发现发烧样疾病。治愈不起作用。 |
问题:根据医学期刊提供的各种医学指标,即Location
、Person
和 SimilarDiseaseReported
,科学家发现了一个公式可以更好地
在医学杂志上预测埃博拉病毒感染的变化。他根据这三个参数制定了一个复杂的数学公式。等式如下:
_score = PatientPastHealthIndex + 10 /LocationPastHealthIndex
如何解决:在函数得分查询中,我们使用脚本函数类型来计算上述公式。
请求:
分数 |
患者健康指数 |
位置健康指数 |
标题 |
---|---|---|---|
11 |
9 |
4 |
坦桑尼亚不明疾病症状。 |
11 |
6 |
2 |
恶心和呕吐严重。 |
5 |
3 |
4 |
患者出现恶心和头痛,然后咳嗽。 |
4 |
3 |
9 |
发现发烧样疾病。治愈不起作用。 |
在本章中,您了解了 Elasticsearch 实现您自己的评分逻辑的强大功能和灵活性。这种灵活性可用于多种用途,主要用于电子商务行业。在这个平台上可以构建很多知识产权,这将是许多业务逻辑的核心。
让我们回顾一下如何进行评分。可分为以下几种:
基于查询的分数:这是 Elasticsearch 在文本匹配上返回的分数。这是根据词频、逆文档频率和字段长度归一化计算得出的。在本节中,您了解到您还可以执行以下操作:
您可以使用匹配查询中的多字段选项来提升某些字段的匹配度
您可以在 bool 查询中提升某些查询而不是其他查询
根据用户需求自定义评分:使用函数查询,我们可以实现各种自行提供评分的函数。稍后,可以将各种函数返回的分数汇总在一起,以获得自定义分数函数发出的最终分数。这些功能是:
基于过滤器的分数:这是基于过滤器的计算分数。如果过滤器匹配,您需要将分数值作为 X 发出。
数值衰减分数:只要有参考点,数值越接近分数越高。在这里,用户必须给出一个参考点。与参考点的距离越大,相关性越小。数值可以是地理点、日期、数字,甚至是 IP。
基于随机值的分数:在这里,为不同的调用随机计算分数。
最后,在函数查询中,我们可以选择将这两个评分逻辑返回的分数组合在一起。
在下一章中,我们将看到如何使用 Elasticsearch 的文档链接或关系特性。但是,您需要记住,在分布式网络中实现关系文档非常困难。大多数 NoSQL 解决方案都没有此选项,但 Elasticsearch 具有可在很大程度上支持此功能的大量功能集。