TFIDF与BM25
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了TFIDF与BM25相关的知识,希望对你有一定的参考价值。
参考技术A 先复习一下 tfidf,tf是词频,即某个词 i 在 文章 j 中出现的频率。分母是文章中所有词的个数,分母是词 i 出现的次数。tf越高说明该词越重要,对于短文本匹配,每个词一般只出现一次,tf 的大小就取决于分母,即文章的长度。idf是逆文档频率,计算该词出现在所有文章中的频率,此时,分母是包含该关键字 i 的文章数,分子是所有文章数 N。用log相当于趋势不变,数值变小了。该词出现越多,分子越大,idf值越小,比如:"的" 经常出现,因此不是关键词。当词 i 在 文章 j 中完全不出现,分母为0,因此给分母加 1。
tf和idf的乘积就是词 i 在文章 j 中的重要性。
在搜索中,计算搜索串中的多个关键词 与 文章 j 的相似度:将各词的 tfidf 相加:
搜索之前,需要知道各个词在已知文章集中的分布。
BM25是基于TF-IDF的改进算法,BM 是Best Match最佳匹配的缩写,25指的是第25次算法迭代。
idf 部分只做了微调:
其中分母部分从所有文章中减去了包含 i 的文章,0.5用于平滑。
接下来,又对 tf 做了如下调整:
这里引入了超参数 k 和 b。
先看分母中的括号,Ld是文章长度,Lavg是所有文章的平均长度,当文章长度与平均长度一致时,括号里值为 1,相当于未乘系数;当文章比平均长度短时,括号里的值小于1,分母越小,上式结果越大,也就是说文章越短,每一个词越重要,这也与直觉一致。另外,长度的影响与b有关,b越大,影响越大,b的取值在0-1之间,当b为0时,完全不考虑长度的影响,b 一般取值为 0.75。
k 用于标准化词频的范围,将 tf 值压缩到 0~k+1 之间,其函数曲线如下:
其横轴为 tf,纵轴为 tfscore,分别针对 k=0,1,2,3,4 画图。当k=0时,tfscore为 1,不考虑词频的影响,而 k 越大词频越趋近于原始词频。因此,如果文章只包含短文本,或者无需关注词出现几次,则可将其设成 k=0。
有时还考虑到词 i 在搜索文本中的频率,上式扩展成:
其中td指被搜索文本,tq指搜索文本。
这样我们就可以细化的控制 tf 的占比,以及文章长度的影响,以适应各种不同情况下的搜索和匹配任务。注意设置参数k和b。
之前的BM25算法集成在gensim里,最新的版本没有了,如果想使用,可以从旧版本里抽出来。
tfidf.transform() 函数没有返回正确的值
【中文标题】tfidf.transform() 函数没有返回正确的值【英文标题】:tfidf.transform() function not returning correct values 【发布时间】:2017-08-22 19:21:46 【问题描述】:我正在尝试在某个文本语料库上拟合 tfidf 矢量化器,然后使用相同的矢量化器来查找新文本的 tfidf 值的总和。但是,总和值与预期不符。下面是例子:
text = ["I am new to python and R , how can anyone help me","why is no one able to crack the python code without help"]
tf= TfidfVectorizer(stop_words='english',ngram_range =(1,1))
tf.fit_transform(text)
zip(tf.get_feature_names(),tf.idf_)
[(u'able', 1.4054651081081644),
(u'code', 1.4054651081081644),
(u'crack', 1.4054651081081644),
(u'help', 1.0),
(u'new', 1.4054651081081644),
(u'python', 1.0)]
现在,当我尝试使用相同的 tf
新文本时:
new_text = "i am not able to code"
np.sum(tf.transform([new_text]))
1.4142135623730951
我预计输出在 2.80 左右。任何关于此处可能出现问题的建议都会非常有帮助。
【问题讨论】:
【参考方案1】:这是因为“l2 标准化”(TfidfVectorizer 中的默认值)。
如您所料,transform()
的第一个结果是:
array([[ 1.40546511, 1.40546511, 0. , 0. , 0. ,
0. ]])
但是现在规范化已经完成。在此,上面的向量被除法器除:
dividor = sqrt(sqr(1.40546511)+sqr(1.40546511)+sqr(0)+sqr(0)+sqr(0)+sqr(0))
= sqrt(1.975332175+1.975332175+0+0+0+0)
= 1.98762782
所以最终得到的数组是:
array([[ 0.70710678, 0.70710678, 0. , 0. , 0. ,
0. ]])
然后你应用 sum,它的结果是 = 1.4142135623730951
。
希望现在很清楚。 TfidfVectorizer的完整工作可以参考my answer here。
【讨论】:
明确一点,如果你不想要这个 L2-norm,你可以在TfidfVectorizer
构造函数中使用 norm=None
。见here。
感谢您的清晰解释。一个后续问题 - 为什么当我们在新文本上使用 tfidfs 时,而不是在我们最初将其与原始文本相匹配时,它会被规范化?
@Apoorv 原始数据用于从整个语料库中查找词条的idf。它不计算基于单个文档的 tf-idf。 tf-idf 是标准化的,而不是 idf。如果您对原始文档使用转换,您将获得标准化的结果。不要混淆 tf 和 idf。在您的情况下,您会看到 idf=tf-idf ,因为每个术语的 tf =1 。见scikit-learn.org/stable/modules/…
@Vivek Kumar 知道了。感谢您的快速帮助和参考。 :)以上是关于TFIDF与BM25的主要内容,如果未能解决你的问题,请参考以下文章