最近想要学习有关语义搜索的算法知识,听大神讲解了解到lucene全文搜索库,查阅了官网的说明和一些博客,将个人理解到的全文检索方法整理在下面。


一、首先,为什么需要全文搜索?

首先介绍两种数据分类,根据搜索内容格式不同,一般将搜索数据分为两类:

1. 结构化数据:指具有固定格式或有限长度的数据,如数据库、元数据等。针对结构化数据的搜索,例如对数据库的搜索,可以使用SQL语句。再如对元数据的搜索,例如windows中对文件名、类型和修改时间进行搜索等。

2. 非结构化数据(全文数据):指不定长或没有固定格式的数据,例如邮件、word文档等。对非结构化数据的搜索,例如windows中对文件内容的搜索、Linux中grep命令,以及使用Google或百度来搜索内容都属于对全文数据的搜索。

对结构化的数据,可以使用搜索算法按照结构较快地进行检索。

但是对非结构化的数据,由于没有特定结构,因此在数据量比较小时,可以使用顺序扫描法,一个文件一个文件地找,找到包含检索字符串的文件。如windows文件搜索,Linux中grep命令。这样的方法对于数据量较小时比较直接,但是对于数据量较大的文件检索效率较低。

因此有大神想到了一种办法,将非结构化的数据部分转化为结构化的数据,再对字符串进行检索,这样检索的效率就可以加快!那么,怎样将非结构化数据转化为部分结构化的数据呢?这个就是我们下面要提到的全文检索算法啦!

由于已知的信息是文件中有哪些字符串,待求的问题是哪些文件包含字符串,因此需要保存字符串到文件的映射,所以也将这种索引称为反向索引。


二、其次,啥叫全文检索?

全文检索:将非结构化数据按照规则提取信息,重新组织,使其有一定结构(提取出用于重新组织的信息即为索引),对有一定结构的数据利用搜索算法加快检索速度。例如字典中将拼音作为索引进行排序。

全文搜索的缺点:需要创建索引和搜索索引,对于数据量小的情况速度不一定比顺序检索快。优点:每次检索不需要重复创建索引的过程,一次创建索引,可以多次使用。


三、全文搜索是怎么实现的?

简而言之:先建立索引(indexing),再根据索引进行搜索(Search)。(啥,就一句话?哈哈不急,下面了解一下这句话的详细步骤吧)

1. 下面首先了解一下索引是怎么创建的吧(啥?索引是啥,可以吃嘛?)。首先了解一下什么是索引:

索引图示:

全文检索 架构 es 全文检索原理_搜索

上面这张图中,左边是待查找的字符串,称为词典,右边是每个字符串指向的文件链表,称为倒排表。

2. 索引是怎么创建的呢?(咦,这个标题怎么和上面一样?)

要把索引创建好,主要分3步:

第一步:将文档传递给分词组件(Tokenizer),由分词组件根据标点符号和停词(没有语义的词,通常不会作为检索关键词,可以作为单词的分隔符,例如英文中a或the等),将文档分为一个个单独的单词。经过分词后得到的结果称为词元(Token)。

第二步:将得到的词元传给语言处理组件进行语言处理(将单词处理为词根,如将英文去除复数后缀s,形容词去除nal(可以根据固定算法进行缩减),以及将时态转换为现在式(根据字典知识)。语言处理组件处理后的结果称为词(Term)。

第三步:将得到的词(Term)传递给索引组件(Indexer),索引组件先将得到的词创建为字符串与文档ID的映射词典,接着对字符串按字母顺序排序,合并相同的字符串,最后形成一张文档倒排链表(如下所示):其中Document Frequency表示几个文档中包含这个单词,Frequency表示编号为Document ID的文档包含了几个Term单词。

全文检索 架构 es 全文检索原理_数据_02

3. 创建好索引以后,怎么用索引来搜索呢?

要想根据索引查找到文件名,主要也分3步:

第一步:用户输入查询语句(有一定语法:AND/OR/NOT);对查询语句进行词法分析(区分单词与关键字AND/OR/NOT)、语法分析(根据查询到的单词与关键词形成如下的语法树)及语言处理(将不同形式的单词转换为词根);

第二步:搜素索引,得到符合条件的文档;根据相关性对结果进行排序。

第三步:计算相关性:对相关性进行打分,分数越高越靠前。这里需要找到哪些词对文档的关系最重要(关键词),再判断两个文档间关键词的相似度。找到词对于文档的重要性就是计算词的权重。词的权重表示词再文档中的重要程度,权重越大的单词在计算文档的相关性中作用越大。

第四步:根据词的权重来进行结果排序:运用向量空间模型算法(VSM)通过词之间的关系得到文档相关性。

4. 没啦?上面那个向量xx算法,这是个啥?还有,词的权重是咋计算出来的呢?

首先,计算词的权重:

全文检索 架构 es 全文检索原理_搜索_03

咋上来就是公式,好好说人话!

上面的公式里,tf表示词在一个文档中出现次数tf(Term Frequency)越大,词越重要;出现该次的文档数df(Document Frequency)越大,词越不重要。

接下来,将查询文档所有词放在一个向量中(Document Vector),将查询语句所有词放在一个向量中(Query Vector),将两个向量维数取并集(并集维数为N),产生一个N维空间,每个词是一维,通过向量夹角的余弦值计算两个向量之间的夹角,余弦值越小,说明两向量之间夹角越小,说明查询结果的打分越高,相关性越大。具体相关性打分公式如下:

全文检索 架构 es 全文检索原理_数据_04


四、结语

用官网文档中的流程图来总结上面的步骤(此图参照http://www.lucene.com.cn/about.htm中文章《开放源代码的全文检索引擎Lucene》):

全文检索 架构 es 全文检索原理_结构化_05

具体文字的流程是下面这样的:

  1. 建立索引的过程:
  1. 有一系列被索引文件
  2. 被索引文件经过语法分析和语言处理形成一系列词(Term)
  3. 经过索引创建形成词典和反向索引表
  4. 通过索引存储将索引写入硬盘
  1. 搜索过程:
  1. 用户输入查询语句。
  2. 对查询语句经过语法分析和语言分析得到一系列词(Term)
  3. 通过语法分析得到一个查询树
  4. 通过索引存储将索引读入到内存
  5. 利用查询树搜索索引,从而得到每个词(Term)的文档链表,对文档链表进行交,差,并得到结果文档
  6. 将搜索到的结果文档对查询的相关性进行排序
  7. 返回查询结果给用户。