何为搜索?
举个简单的例子,如图所示,当我们想要买衣服的时候,我们可能会随手打开淘宝,在输入框输入“衣服”两个字,然后点击搜索按钮,淘宝网就会返回匹配的内容给用户浏览,这个过程其实就是搜索。
我们做个抽象和简化,搜索系统就是如下图所示的样子,用户输入查询条件,搜索引擎查找到条件匹配的内容后,将内容展示出来。
当然,有同学会立马有疑问,这个不就是一个查询吗,使用传统的关系型数据库也能满足需求,那么搜索和查询到底有什么区别呢?
还是以上述在淘宝搜索“衣服”为例,如果我们采用关系数据库存存储,那么我们可以等同于如下查询语法:
select * from item where name like '%衣服%'
很显然,这种情况下,name字段是没法走索引的,需要扫全表,查询会非常的慢。那有没有更简单的方法呢,可能会说能不能加个衣服的分类或者标签,可是如果新增一个商品品类怎么办呢?要无限制加下去吗?如何能更简单高效的处理全文检索呢?
答案就是搜索,通过词法语法分析、分词、构建词典、构建倒排表、压缩优化等操作构建一个反向索引(倒排索引),查询时通过词典快速获取结果,从而达到高效全文检索的目的。
分词
分词就是对一段文本,通过规则或者算法分成多个单词。只有分词后有这个单词,搜索才能搜到,分词的正确性非常重要。分词粒度太大,搜索召回率就会偏低,分词粒度太小,准确率就会降低。如何恰到好处的分词,是搜索引擎需要做的第一步。
正确性&粒度
●分词正确性
○“中央饭店”,这句话如何分词?
○“中-央饭-店” [错误语义]
○“中央-饭店” [正确语义]
●分词的粒度
○“中华人民共和国”,这句话如何分词?
○“中华人民-共和国”,[搜索 中华 无结果]
○“中华-人民-共和国”,[搜索 共和 无结果]
○“中-华-人-民-共-和-国”,[搜索其中任意字都有结果]
分词的粒度并不是越小越好,粒度越小准确率越低,比如搜索 “中午” 也会出现上条结果,而且粒度越小,索引词典越大,搜索效率也会下降。如何准确的分词,涉及到 NLP领域,此处不细说,仅列举几个常见的分词器:
●IK:实现中英文单词的切分,可自定义词库,支持热更新分词词典
●jieba:支持分词和词性标注,支持繁体分词,自定义词典,并行分词等
●Hanlp:由一系列模型与算法组成的Java工具包,目标是普及自然语言处理在生产环境中的应用
●THUAC:中文分词和词性标注
停用词
很多语句中的词都是没有意义的,比如 “的”,“在” 等副词、谓词,英文中的 “a”,“the”等,在搜索看来都是无意义的,所以在分词构建索引时都会去除掉来节省索引空间,这种词叫停用词 (StopWord)。通常可以通过文档集频率和维护停用词表的方式来判断停用词。
词项处理
词项处理,是指在原本的词项上在做一些额外的处理,比如归一化、词形归并、词干还原等操作,以提高搜索的效果。并不是所有的需求和业务都要词项处理,需要根据场景来判断。
1.归一化
●9月30日 - 9/30 [中英文]
●color - colour [通假词]
●开心 - 高兴 [同义词扩展范畴]
这样查询9/30也能得到9月30日的结果
2.词形归并(Lemmatization)
针对英语同一个词有不同的形态,可以做词形归并成一个,如:
●am, are, is -> be
●car, cars, car's, cars' -> car
3.词干还原(Stemming)
通常指的就粗略的去除单词两端词缀的启发式过程
●automate(s), automatic, automation -> automat.
●可可爱爱 -> 可爱[中文重叠词还原]
英文的常见词干还原算法,比如Porter算法。
倒排索引
倒排索引是什么?简单一点讲,就是单词到文档id的反向映射关系
简单示例:
对以下三个文档进行分词(去掉停用词)构造倒排索引后的结果如下图所示
我们模拟下倒排索引的查询过程,比如查询包含“搜索引擎”的文档:
1、通过倒排索引获得“搜索引擎”对应的文档id列表,有1,3
2、通过正排索引查询1和3的完整内容
3、返回最终结果
倒排索引主要由两部分组成:
●单词词典(Term Dictionary)--内存
●倒排列表(Posting List)--磁盘
词典
单词词典是文档集合中所有单词的集合,它是保存索引的最小单位,其中记录着指向倒排列表的指针
单词词典的实现一般采用B+树,当然也可以采用LSM (Log Structured Merge Tree)、FST(Finite State Transducer)、SkipList等数据结构,我们以B+树为例,那么上述文档的词典结构如下:
倒排列表
倒排列表记录了单词对应的文档集合,由倒排索引项(Posting)组成,倒排索引项主要包含如下信息:
●文档id,用于获取原始信息
●单词频率(TF,Term Frequency),记录该单词在该文档中出现的次数,用于后续相关性算分
●位置(Posting),记录单词在文档中的分词位置(多个),用于做词语搜索(Phrase Query)
●偏移(Offset),记录单词在文档的开始和结束位置,用于高亮显示
以“搜索引擎”这个单词为例,其倒排列表如下:
上述词典和倒排列表整合在一起后的结构如下:
正排索引
既然有了倒排索引,那么必然就有正排索引,那正排索引是什么呢?上文中也有描述,搜索过程可以简化为:通过倒排索引拿到文档ID后再通过正排索引拿到文档信息(单词),也就是文档id到单词的正向映射关系
以上述文档1和文档3为例,分词(去掉停用词)后的正向索引如下:
正排索引除了用于倒排索引的正向查询外,主要还用于文档的排序、聚合、统计等。如果聚合查询里有带过滤条件或检索条件,先由倒排索引完成搜索,确定文档范围,再由正排索引提取field(单词),最后做聚合计算。
算分排序
●搜索的相关性算分,实际上是描述了一个文档和查询语句匹配的程度,这个程度量化后就变成了分数_score
●打分的本质是排序,需要把最符合用户需求(打分越高)的文档排在前面,常见的打分算法有经典的TF-IDF、BM25等,当然也可以采用插件的方式,来自定义打分排序,此处不再细说
TF-IDF
TF(词频)-IDF(逆文档频率) 是公认的信息检索领域最重要的发明,除了在信息检索领域,在文献分类,和其他相关领域也有着非常广泛的应用。其中 TF 表示某个 Term(单词) 在 Document 里出现的频次,越高说明越重要;DF 表示在全部 Document 里,共有多少个 Document 出现了这个单词,DF 越大,说明这个单词很常见,并不重要,越小反而说明它越重要,IDF 是 DF 的倒数(取log), IDF 越大,表示这个单词越重要。评分公式如下:
BM25
上面解释了 TF 和 IDF,那么 TF 和 IDF 谁更重要呢,怎么计算最终的相关性得分呢?这就衍生了BM25。
BM25算法,一句话概况其主要思想:对Query进行语素解析,生成语素qi;然后,对于每个搜索结果D,计算每个语素qi与D的相关性得分,最后,将qi相对于D的相关性得分进行加权求和,从而得到Query与D的相关性得分。
BM25算法的一般性公式如下:
其中,Q表示Query,qi表示Q解析之后的一个语素(对中文而言,分词后的单词可看成语素qi);d表示一个搜索结果文档;Wi表示语素qi的权重;R(qi,d)表示语素qi与文档d的相关性得分。
其中 Wi 通常使用 IDF 来表达,R 使用 TF 来表达;综上,BM25算法的相关性得分公式可总结为:
BM25 通过使用不同的语素分析方法、语素权重判定方法,以及语素与文档的相关性判定方法,可以衍生出不同的搜索相关性得分计算方法,这就为我们自定义排序算法提供了较大的灵活性。
搜索框架选型
通过对国内外搜索技术的调研,我们总结出目前主流的搜索框架(简化版)大部分都是先用dump宽表merge所有数据源的数据,然后再全量转储到搜索引擎,search端传入用户搜索条件后,从搜索引擎进行召回、排序,最后进行结果展示,如下图左边部分所示。结合业务自身特点,可在主流搜索框架上做一些定制调整,比如国际卖家商品搜索去除了嵌套子文档,因此可以省掉dump宽表存储,采用局部更新的方式;同时search端可以忽略相关性算分,只需文本召回即可,如下图右半部分所示。