- 1. TF-IDF原理
- 1.1 计算公式
- 1.2 示例说明
- 1.2.1 计算TF
- 1.2.2 计算IDF
- 1.2.3 TF-IDF计算
- 2. Elasticsearch打分机制
- 2.1 示例说明
- 2.2 计算 TF 值
- 2.3 计算 IDF 值
- 2.4 计算文档得分
- 2.5 增加新的文档测试得分
- 3. 案列
- 3.1 需求
- 3.2 准备数据
- 3.3 查询数据
Elasticsearch
的得分机制是一个基于词频和逆文档词频的公式,简称为
TF-IDF
公式,所以先来研究下
TF-IDF
原理。
1. TF-IDF原理
TF-IDF
的英文全称是:Term Frequency - Inverse Document Frequency
,中文名称词频-逆文档频率。
常用于文本挖掘,资讯检索等应用,在NLP
以及推荐等领域都是一个常用的指标,用于衡量字词的重要性。
比较直观的解释是,如果一个词本来出现的频率就很高,如the,那么它就几乎无法带给读者一些明确的信息。
一般地,以TF-IDF
衡量字词重要性时
- 某个字词在某个文档中出现的频率越高,那么该字词对该文档就有越大的重要性,它可能会是文章的关键词(词在单个文档中出现的频率,相对于当个文档!!!)
- 但若字词在词库中出现的频率越高,那么字词的重要性越低,如
the
。(相对于整个文档集合,也就是词库)
TF-IDF
即是两者相乘,词频乘以逆文档频率,如下:
T
F
−
I
D
F
=
T
F
∗
I
D
F
TF-IDF=TF*IDF
TF−IDF=TF∗IDF 下标i
,j
的含义:编号为j
的文档中的词语i
在该文档中的词频,即所占比例,n
为该词语的数量。如下:
换言之,就是词语出现的次数与文档中所有词总数的比值。
T
F
i
j
=
n
i
j
n
∗
j
TF_{ij} = \frac{n_{ij}}{n_{*j}}
TFij=n∗jnij N
表示文档总数,Ni
表示文档集中包含了词语i的文档数。
对分子分母加一是为了避免某些词语没有在文档中出现过,导致分母为零的情况。
IDF
针对某个词计算了它的逆文档频率,即包含该词语的文档比例的倒数(再取对数),若IDF
值越小,分母越大,说明这个词语在文档集中比较常见不具有鲜明的信息代表性,TF-IDF
的值就小。
总之TF-IDF
的值,通常希望它越大越好,大值代表性强。如下:
I
D
F
i
=
l
o
g
(
N
+
1
N
i
+
1
)
IDF_i=log (\frac{N+1}{N_i+1})
IDFi=log(Ni+1N+1)
有两个文档,即doc1
、doc2
,并去它们的并集
doc1 = "The cat sat on my bed"
doc2 = "The dog sat on my knees"
# 构建词库,union是并集操作
wordSet = set(doc1.split()).union(set(doc2.split()))
两个文档的并集如下:
{‘The’,‘bed’,‘cat’,‘dog’,‘knees’,‘my’,‘on’,‘sat’}
doc1
、doc2
两个文档对应的词在并集中的统计情况:
计算词频 TF
,对单个文档统计:
再理解一下,何为TF,表示单个单词占当前文档所有单词集合的比值。即1/6=0.16666666666…
catsatmyondogbedTheknees111101100.166666…0.166666…0.166666…0.166666…00.166666…0.166666…0 1.2.2 计算IDF逆文档频率IDF
,全局只有一份逆文档频率,对所有文档统计
N表示文档总数,
Ni`表示文档集中包含了词语i的文档数。
此时N=2,共有两个文档。Ni表示含有单词的文档个数。
catsatmyondogbedTheknees0.17609125…0.00.00.0…0.17609125…0.17609125…0.00.17609125… 1.2.3 TF-IDF计算最终计算:TF-IDF = TF * IDF
上面介绍了TF-IDF的原理,而ES的得分机制就是基于词频和逆文档词频的公式,即TF-IDF
公式。
s
c
o
r
e
(
q
,
d
)
=
c
o
o
r
d
(
q
,
d
)
⋅
q
u
e
r
y
N
o
r
m
(
q
)
⋅
∑
t
i
n
q
(
t
f
(
t
i
n
d
)
⋅
i
d
f
(
t
)
2
⋅
t
.
g
e
t
B
o
o
s
t
(
)
⋅
n
o
r
m
(
t
,
d
)
)
score(q,d) = coord(q,d)\cdot queryNorm(q)\cdot \sum_{t in q}(tf(t in d)\cdot idf(t){^2}\cdot t.getBoost()\cdot norm(t,d))
score(q,d)=coord(q,d)⋅queryNorm(q)⋅tinq∑(tf(tind)⋅idf(t)2⋅t.getBoost()⋅norm(t,d)) 公式中将查询作为输入,使用不同的手段来确定每一篇文档的得分,将每一个因素最后通过公式综合起来,返回该文档的最终得分。这个综合考量的过程,在ES中这种相关性称为得分。
考虑到查询内容和文档的关系比较复杂,所以公式中需要输入的参数和条件非常得多,但是其中比较重要的其实是TF-IDF
算法 ,再次解释一下。
TF
(词频)
Term Frequency : 搜索文本中的各个词条在查询文本中出现了多少次,次数越多,就越相关,得分会比较高
IDF
(逆文档频率)
Inverse Document Frequency : 搜索文本中的各个词条在整个索引的所有文档中出现了多少次,出现的次数越多,说明越不重要,也就越不相关,得分就比较低。
2.1 示例说明在查询语句的最后加上explain=true
,会把得分过程打印。
注:当前ElasticSearch
的scorpios
索引里,只有一个文档。
# 创建索引
PUT /scorpios
# 增加文档数据,此时索引中只有这一条数据
PUT /scorpios/_doc/1
{
"text":"hello"
}
# 增加分析参数
GET /scoripos/_search?explain=true
{
"query": {
"match": {
"text": "hello"
}
}
}
执行后,会发现打分机制中有 2 个重要阶段:计算 TF
值和 IDF
值
最后的分数为:
f r e q / ( f r e q + k 1 ∗ ( 1 − b + b ∗ d l / a v g d l ) ) freq/(freq + k1 * (1-b+b*dl/avgdl)) freq/(freq+k1∗(1−b+b∗dl/avgdl))
参数含义取值freq文档中出现词条的次数1.0k1术语饱和参数1.2(默认值)b长度规格参数(单词长度对于整个文档的影响程度)0.75(默认值)dl当前文中分解的字段长度1.0avgdl查询文档中分解字段数量/查询文档数量1.0TF(词频)1.0/(1+1.2 * (1-0.75+0.75 * 1.0/1.0))0.454545 2.3 计算 IDF 值l o g ( 1 + ( N − n + 0.5 ) / ( n + 0.5 ) ) log(1+(N -n +0.5)/(n + 0.5)) log(1+(N−n+0.5)/(n+0.5))
参数含义取值N包含查询字段的文档总数(不一定包含查询词条)1n包含查询词条的文档数1IDF(逆文档频率)log(1+(1-1+0.5)/(1+0.5))0.2875821注:这里的 log 是底数为e 的对数
2.4 计算文档得分b o o s t ∗ i d f ∗ t f boost * idf * tf boost∗idf∗tf
参数含义取值boost词条权重2.2(基础值)*查询权重(1)idf逆文档频率0.2876821tf词频0.454545score(得分)2.20.28768210.4545450.2876821 2.5 增加新的文档测试得分- 增加一个毫无关系的文档
# 增加文档
PUT /scorpios/_doc/2
{
"text" : "spark"
}
# 得分:0.6931741
GET /scorpios/_search
{
"query": {
"match": {
"text": "hello"
}
}
}
因为新文档无词条相关信息,所以匹配的文档数据得分就应该较高
- 增加一个一模一样的文档
# 增加文档
PUT /scorpios/_doc/2
{
"text" : "hello"
}
# 得分:0.18232156
GET /scorpios/_search
{
"query": {
"match": {
"text": "hello"
}
}
}
因为新文档含词条相关信息,且多个文件含有词条,所以显得不是很重要,得分会变低
- 增加一个含有词条,但是内容较多的文档
# 增加文档
PUT /scorpios/_doc/2
{
"text" : "hello elasticsearch"
}
# 得分:0.14874382
GET /scorpios/_search
{
"query": {
"match": {
"text": "hello"
}
}
}
因为新文档含词条相关信息,但只是其中一部分,所以查询文档的分数会变得更低一些。
3. 案列 3.1 需求查询文档标题中含有Hadoop
,Elasticsearch
,Spark
的内容,优先选择Spark
的内容
# 创建索引
PUT /test
# 准备数据
PUT /test/_doc/1001
{
"title" : "Hadoop is a Framework",
"content" : "Hadoop 是一个大数据基础框架"
}
PUT /test/_doc/1002
{
"title" : "Hive is a SQL Tools",
"content" : "Hive 是一个 SQL 工具"
}
PUT /test/_doc/1003
{
"title" : "Spark is a Framework",
"content" : "Spark 是一个分布式计算引擎"
}
3.3 查询数据
# 查询文档标题中含有“Hadoop”,“Elasticsearch”,“Spark”的内容
GET /test/_search?explain=true
{
"query": {
"bool": {
"should": [
{
"match": {
"title": {
"query": "Hadoop", "boost": 1
}
}
},
{
"match": {
"title": {
"query": "Hive", "boost": 1
}
}
},
{
"match": {
"title": {
"query": "Spark", "boost": 1
}
}
}
]
}
}
}
此时会发现,Spark
的结果并不会放置在最前面
此时可以更改 Spark
查询的权重参数 boost
,看看查询的结果有什么不同
# 查询文档标题中含有“Hadoop”,“Elasticsearch”,“Spark”的内容
GET /test/_search?explain=true
{
"query": {
"bool": {
"should": [
{
"match": {
"title": {
"query": "Hadoop", "boost": 1
}
}
},
{
"match": {
"title": {
"query": "Hive", "boost": 1
}
}
},
{
"match": {
"title": {
"query": "Spark", "boost": 2
}
}
}
]
}
}
}