ElasticSearch之TF/IDF

2023-10-20 08:28:51 浏览数 (1)

摘要本文将了解一下ElasticSearch控制相关度分数的TF/IDF,和向量空间模型

代码语言:javascript复制
当我们使用es进行全文搜索的时候,es使用TF/IDF算法来计算scroe。
Term Frequency/Inverse Document Frequency简写(TF/IDF):词频/逆向文档频率
TF:词频,词条在当前文档中出现的有多频繁?越频繁的话,那么权重就越高。出现2次的
分数肯定比1次高。
IDF:逆向文档频率,词条在集合所有文档里出现的频率是多少?频次越高,权重 越低。
注意:这里的所有文档是指本地分片
假设我们现在有两个document
doc1:hello, today is very good
doc2:hi world, how are you
我们搜索hello world
TF: term frequency
找到hello在doc1中出现了几次,1次,会根据出现的次数给个分数
一个term在一个doc中,出现的次数越多,那么最后给的相关度评分就会越高
IDF:inversed document frequency
找到hello在所有的doc中出现的次数,1次
一个term在所有的doc中,出现的次数越多,那么最后给的相关度评分就会越低
length norm
hello搜索的那个field的长度,field长度越长,给的相关度评分越低; field长度越短,给的相关度评分越高
最后,会将hello这个term,对doc1的分数,综合TF,IDF,length norm,计算出来一个综合性的分数
hello world --> doc1 --> hello对doc1的分数,world对doc1的分数 --> 但是最后hello world query要对doc1有一个总的分数 --> vector space model

注意:

IDF:词条在集合所有文档里出现的频率是多少,这里的所有文档是指本地分片的所有文档,不是所有分片的所有文档,所以当index有多个share计算出来的记过就会不准确。

方案:

测试环境:可以通过设置只有一个share来处理问题,或者搜索请求中添加?search_type=dfs_query_then_fetch。dfs表示分布频度搜索(Distributed Frequency Search),它会告诉ES首先从每个分片中获取本地IDF,然后计算整个索引上的全局IDF。

生产环境:我们的文档被均匀地分布了,多个个分片上计算得到的IDF应该是相同的。现在想象一下如果含有foo的5份文档被保存在了分片1上,而只有1份含有foo的文档被保存在了分片2上。在这种情况下,词条foo在分片1上就是一个非常常见的词条(重要性很低),但是在分片2上,它是非常少见的词条(重要性很高)。因此,这些IDF的差异就会导致错误的结果。

实际情况下,这并不是一个问题。当你向索引中添加的文档越多,本地IDF和全局IDF之间的差异就会逐渐减小。考虑到真实的世界中的数据量,本地IDF很快就会变的正常。问题不是相关度,而是数据量太小了。

不要在生产环境中使用dfs_query_then_fetch。它真的是不必要的。性能太低。

代码语言:javascript复制
TF:词频
如果不在意词在某个字段中出现的频次,而只在意是否出现过,则可以在字段映射中禁用词频统计:
PUT /my_index
{
"mappings": {
"doc": {
"properties": {
"text": {
"type": "string",
"index_options": "docs"
}
}
}
}
}
将参数 index_options 设置为 docs 可以禁用词频统计及词频位置,这个映射的字段不会计算词的出现次数,对于短语或近似查询也不可用。要求精确查询的 not_analyzed 字符串字段会默认使用该设置。
代码语言:javascript复制
length norm:字段长度的归一值
字段长度的归一值对全文搜索非常重要, 许多其他字段不需要有归一值。无论文档是否
包括这个字段,索引中每个文档的每个 string 字段都大约占用 1 个 byte 的空
间。对于 not_analyzed 字符串字段的归一值默认是禁用的,而对于 analyzed 字
段也可以通过修改字段映射禁用归一值:
PUT /my_index
{
"mappings": {
"doc": {
"properties": {
"text": {
"type": "string",
"norms": { "enabled": false }
}
}
}
}
}
这个字段不会将字段长度归一值考虑在内,长字段和短字段会以相同长度计算评分。
对于有些应用场景如日志,归一值不是很有用,要关心的只是字段是否包含特殊的错误码
或者特定的浏览器唯一标识符。字段的长度对结果没有影响,禁用归一值可以节省大量内
存空间。

向量空间模型

代码语言:javascript复制
向量空间模型提供了一种多词条查询的比较方法。它的输出是一个代表了文档和查询之间匹配程度的分值。为了计算该分值,文档和查询都被表示成向量。
一个向量实际上就是一个包含了数值的一维数组,比如:
[1,2,5,22,3,8]
在向量空间模型中,向量中的每个数值都是由TF/IDF计算得到的一个词条的权重。
假设我们查询了"happy hippopotamus"。一个像happy这样的常见单词的权重是较
低的,然而像hippopotamus这样的罕见单词则拥有较高的权重。假设happy的权重为
2而hippopotamus的权重为5。我们可以使用坐标来表达这个简单的二维向量 - [2, 5]
一条从坐标(0, 0)到坐标(2, 5)的直线,如下所示:
代码语言:javascript复制
现在,假设我们有三份文档:
I am happy in summer.
After Christmas I’m a hippopotamus.
The happy hippopotamus helped Harry.
我们可以为每份文档创建一个类似的向量,它由每个查询词条的权重组成 - 也就是出现在文档中的词条happy和hippopotamus,然后将它绘制在坐标中,如下图:
文档1:(happy,____________) — [2,0]
文档2:( ___ ,hippopotamus) — [0,5]
文档3:(happy,hippopotamus) — [2,5]
向量的一个很棒的性质是它们能够被比较。通过测量查询向量和文档向量间的角度,我们可以给每份文档计算一个相关度分值。文档1和查询之间的角度较大,因此它的相关度较低。文档2和查询更靠近,所以它的相关度更高,而文档3和查询之间则是一个完美的匹配。

在实际中,只有二维向量(两个词的查询)可以在平面上表示,幸运的是, 线性代数 ——作为数学中处理向量的一个分支——为我们提供了计算两个多维向量间角度工具,这意味着可以使用如上同样的方式来解释多个词的查询。

参考 https://www.elastic.co/guide/cn/elasticsearch/guide/current/scoring-theory.html#tfidf

https://blog.csdn.net/wuzhiwei549/article/details/80407607

https://www.iteye.com/blog/study121007-2294453

0 人点赞