全文检索功能实现javascript的相关评分功能

全文检索功能实现javascript的相关评分功能
全文搜索,不像大多数机器学习领域中的其他问题,是Web程序经常在日常工作中遇到的问题,客户可能会要求你提供一个搜索框的地方,然后你将写一个搜索功能,类似于在标题%:%的SQL语句查询首先,这是没有问题,直到有一天,客户发现你告诉你的搜索是错误的!

事实上,当然,在搜索没有错误,但搜索的结果是不是客户想要的,普通用户是不知道如何使一个精确的匹配,使搜索结果很差。为了解决这个问题,你决定使用全文搜索。有点枯燥的学习后,你打开MySQL全文索引和使用更高级的查询语法,如匹配()和()。
好吧,问题,花的尽头!当数据库很小的时候,就可以了。

但是当你得到更多的数据,你会发现你的数据库是越来越slower.mysql是不是一个很好的完整的文本搜索工具。所以你决定使用Elasticsearch,重构代码,并部署全文本搜索集群由Lucene。你会发现它的效果很好,速度快,准确的。

然后你不禁会想:为什么Lucene如此乐观

本文(主要介绍TF-IDF,霍加皮BM-25和广义相关评分)和下一篇文章(主要指标)会告诉你后面的全文检索的基本概念。

关联

每个搜索查询,可以很方便地确定每个文档的相关性得分。当用户搜索,我们可以通过相关的分数而不是使用文档的时候出现的那种。这样,最相关的文档将被放在首位,不管多久之前创建的(当然,有时也对文件的创建时间有关)。

有很多方法可以计算词之间的相关性,但我们应该从最简单的、基于统计的方法开始,这种方法不需要理解语言本身,而是通过统计、词的使用、匹配和特定词在文档中的流行来确定相关的分数。
该算法不在乎词是名词或动词,他们不在乎的话的含义。它唯一关心的是常见的词是什么,这些都是罕见的词。如果一个搜索语句包含常用词和生僻词,你应该有更好的成绩含有生僻字文件和减少常用词的重量。

该算法称为霍加皮BM25。它包含单词频率的两个基本概念(词频)称为频率(TF)和逆文档频率(逆文档频率)-(IDF)。把它们放在一起,称为TF-IDF,这是一个统计的措施,用来表示一个字(词)在文档更重要。

TF-IDF

词频TF,短,是一个非常简单的时代某一特定的词的数量出现在文档中。你可以将这个值在文档的总字数,得到一个分数。例如,在一个文件中有100个字,的出现8次,then'the的TF是8或8或100或8%(取决于你想表达的话)。

反文档的频率,短于以色列国防军,是比较复杂的:一个词越罕见,它的值越高,它由包含该单词的文件总数除以总文件数,然后得到的数字是用对数获得的。

如果你把这两个数相乘(TF * IDF),你将得到一个词在文档中的权重。重量定义是:这个词有多罕见,它在文档中出现的频率有多高

你可以使用这个概念用于文档搜索查询,查询,每个查询中的关键词,计算TF-IDF得分并将它们添加到对方得分最高的是文档最符合的查询语句。

酷!

霍加皮BM25

上述算法是一种可行的算法,但还不够完善,给出了一种基于统计的相关分数算法,并对其进行进一步改进。

霍加皮BM25是迄今为止最先进的排序算法之一(所以叫Elasticsearch)。霍加皮BM25 TF-IDF的基础上加上两个可调参数。K1和B分别表示词频饱和(术语频率饱和)和字段长度规范。这个鬼是什么

为了使词的频率饱和一个直观的理解,想象两篇文章几乎相同长度的关于棒球的。此外,我们假设所有的文件(不包括这两篇)没有太多相关的棒球内容,所以话棒球将具有较高的IDF -这是非常罕见的,重要的。这两篇文章讨论棒球,花了很多时间谈论它,但其中一人用棒球超过另一个。所以,在这种情况下,是一条真的不如其他人吗因为22个文件都是关于棒球的,所以棒球这个词是40或80次,实际上,这是30次被认为是最高的!
这是词的频率饱和。原始TF-IDF算法没有概念的饱和,所以80次棒球文件是两倍,40倍。有时,在这一点上,我们想要的东西,但是有时候我们不想要它。

此外,霍加皮BM25也有K1的参数,它是用来调整饱和度的变化率,K1参数值一般在1.2和2之间,值越低,越饱和的过程。(意思是两个以上文件有相同的分数,因为他们都含有大量的棒球。)

字段长度的长度减少(字段长度归一化)减少了对文件的长度对整个文档的平均长度。这是非常有用的一个单一的字段集(单场集合),像我们这样的,统一一个不同长度的文件相同的比较条件。两字段设置(如如题,身体)更有意义,它也可以把标题和正文字段相同的比较条件,字段长度的长度减少表达B,它的值是0和1之间,1意味着所有的还原和非还原0。

算法

在霍加皮BM25在维基百科你可以了解霍加皮算法公式。因为都知道每个项目的公式是什么,这是很容易理解的。所以我们不提公式和直接进入代码:

BM25.Tokenize = function (text) {
文本=文本
toLowerCase()。
替换
替换
修剪()
分裂()
。地图(功能(一){ return Stemmer(一)});

stopstems / /过滤出来
var out = };
对于(var i = 0,len = text.length;我< len;i++){
如果(stopstems.indexof(文本{我})= 1){
Out.push(文{我});
}
}

回来了;
};
我们定义了一个简单的静态方法,标记(),为了分析字符串为标记的数组。这样,我们写下所有的令牌(为了减少熵)。我们用Porter Stemmer算法来减少熵量和提高匹配度(行走和走匹配是相同的),我们也过滤掉不用的话(很普通的话)减少在更近一步的熵。以前我写的东西太深的概念,如果我解释这段请原谅我。

bm25.prototype.adddocument =功能(DOC){
如果(typeof doc.id = = = 'undefined){ throw new Error(1000,ID是一个必需的属性文件。');};
如果(typeof doc.body = = = 'undefined){ throw new Error(1001,'body是一个必需的属性文件。');};

标记化的单词列表 / /原
VaR令牌= BM25。标记(DOC。体);

将持有唯一的术语及其计数/频率。
无功_terms = { };

docobj最终将被添加到/文档数据库
无功docobj = {编号:doc.id,令牌:令牌,身体:身体}医生;

术语数量
docobj.termcount = tokens.length;

增量 / / totaldocuments
这totaldocuments + +;

调整 / / averagedocumentlength
this.totaldocumenttermlength = docobj.termcount;
this.averagedocumentlength = this.totaldocumenttermlength / this.totaldocuments;

术语频率/计算
获取术语数
对于(var i = 0,len = tokens.length;我< len;i++){
变量=令牌{ };
如果(!_terms {词}){
_terms { } = { {术语
计数:0,
频率:0
};
};
_terms {词}。计数+ +;
}

循环计算词频。
我们还将更新反向文档频率。
VAR键= object.keys(_terms);
对于(var i = 0,len = keys.length;我< len;i++){
var =键{ };
此文档的频率。
_terms {词}。或_terms {词}。计数/ docobj.termcount;

文档频率初始化
如果(!这个术语{ { } }){
这个术语。
n:0,这个术语的文档数出现,唯一
以色列国防军:0
};
}

这个术语;
};

文档频率倒数
这是速度较慢的所以如果你想大批量的文件索引,
评论这跑那/曾经在你的adddocuments结束运行
如果每次只索引一个或两个文档,可以把这个放在。
( / /本。updateidf);

docobj文件 / /添加DB
docobj.terms = _terms;
这个文件{ docobj ID = docobj };
};
这是adddocument()这个方法会奇迹般地出现。我们基本上建立和维护两个相似的数据结构:this.documents。和this.terms。
this.documentsis是保存所有文件数据库。它保存了所有原始文档、长度信息和文档列表,并保留了文档中所有单词和单词的个数和出现频率。(是的,非常快,只需要O(1)时间表查询)回答下列问题:在文档# 3,多少次did'walk'appear

我们也用了另一个数据结构,this.terms.it表示在语料库中的所有单词。这个数据结构,我们可以回答以下问题在O(1)时间:多少文件已经出现在word'walk '他们的身份证是什么

最后,我们记录每个文档的长度,并记录整个文档中文档的平均长度。
请注意,在上面的代码中,IDF初始化0,和updateidf()方法的注解。这是因为这种方法运行很慢,只需要在指标设置一次运行。由于操作就可以满足需求,不需要跑5000次。注释第一,然后运行大批量索引操作后,你可以节省很多时间。下面是这个函数的代码:

bm25.prototype.updateidf =函数(){
VAR键= object.keys(这个术语);
对于(var i = 0,len = keys.length;我< len;i++){
var =键{ };
VaR数=(this.totaldocuments -这方面的术语。{ }。n + 0.5);
var名称=(这方面的术语。{ }。n + 0.5);
这学期,{ }。IDF = math.max(math.log10(Num /表演),0.01);
}
};
这是一个非常简单的功能,而是因为它需要遍历所有的话在整个语料库和更新所有的价值的话,这导致其缓慢的工作。这种方法的实施,采用逆文档频率的标准公式(你可以在维基百科找到这个公式)——文件总数除以包含Word文件的数量,并且得到的对数得到。我把返回值超过0的一些修改

bm25.prototype.search =功能(查询){

var = BM25词标记(查询);
var结果{ };

依次查看每个文档。有更好的方法用倒排索引来完成。
VAR键= object.keys(这个文件。);
对于(var j = 0,ndocs = keys.length;J < ndocs;j++){
键= { };
一个文档的关联度是一个TF的总和。
对于每个查询术语。计算
这个文件{id} _score = 0;

计算每个查询的分数
对于(var i = 0,len = queryterms.length;我< len;i++){
无功queryterm =词{我};

我们从未见过这个术语,所以以色列国防军将是0。
我们可以跳过整个名词术语,它不会给分数增加任何东西。
不在任何文件中。
如果(typeof这个术语queryterm } = { 'undefined){
继续;
}

这个术语不在文档中,所以TF部分是0,这个
术语对搜索结果没有贡献。
如果(typeof这个文件{id},{ queryterm } = 'undefined){
继续;
}

这个术语在文件中,让我们来。
整个学期是:
/ / *(TF * IDF(K1 + 1))/(TF + K1×(1-B + B * doclength / avgdoclength))

IDF是预先计算整个 / / docset。
VaR IDF =这个术语{ queryterm }。IDF;
TF部分。 / /分子
VaR Num =这个文件{id},{ queryterm }。数*(this.k1 + 1);
TF部分。 / / denomerator
VaR标准=这个文件{id},{ queryterm }。计数
+(this.k1 *(1 - B +(本。B *这个文件{id}。termcount /本。averagedocumentlength))));

将这个查询项添加到
这个文件{id}。_score = IDF num /表演;
}

如果(!IsNaN(这个文件{id}。_score)这个文件{id}。_score > 0){
Results.push(这个文件{id});
}
}

results.sort(功能(A,B){ return b._score - a._score;});
返回results.slice(0, 10);
};
最后,搜索()方法遍历所有文件,给出了每个文档的BM25评分,然后把它在一个大到小的顺序。当然,它是遍历每个搜索过程的主体文件是不明智的。解决这个问题,第二部分(逆向指标和性能)。
上面的代码注释得很好。要点如下:计算BM25评分每个文档的每个单词。单词的IDF得分已经被预先计算,只需要查询时使用。这个词的频率也预计为该文档的属性的一部分,那么只需要四个简单的操作。最后,添加一个临时变量_score每个文件,然后做一个降序基于评分和返回的前10个结果。

示例、源代码、注意事项和下一步计划

有很多方法可以优化上面的示例,我们将在全文搜索的第二部分中介绍它们,欢迎继续。希望我能在几周内完成它。下面是要提到的下一个项目。

反向索引与快速搜索
快速索引
更好的搜索结果
这个演示,我编了一个小维基百科爬虫爬到相当(85000)维基百科对文章的第一部分。因为所有的85k文件索引大约需要90秒左右,我剪了一半我的电脑,不想浪费你的笔记本电仅为一个简单的全文本呈现。

因为索引是一个沉重的模块化CPU操作,我认为它是一个网络工作者。索引在后端线程上运行。这里你可以找到完整的源代码。你会找到一个算法和我停止文字列表源代码参考。至于代码许可,它仍然是免费的,为了教育目的,不是为了任何商业目的。

最后是演示。一旦索引完成,尝试找出随机的事物和短语,维基百科会知道。注意索引只有40000个部分,所以你可能需要尝试一些新的主题。

免责声明:本网信息来自于互联网,目的在于传递更多信息,并不代表本网赞同其观点。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,并请自行核实相关内容。本站不承担此类作品侵权行为的直接责任及连带责任。如若本网有任何内容侵犯您的权益,请及时联系我们,本站将会在24小时内处理完毕。
相关文章
返回顶部