搜索引擎(Search Engine)是指根据必定的策略、运用计算机技术从互联网上搜集信息,在对信息进行组织和处理后,为用户提供检索服务。在平常生活中,能够看到 Google 等 Web 检索网站,还有邮件检索和专利检索等各类应用程序。git
在本身写一个搜索引擎以前,须要先了解基本的原理和概念。好比分词,倒排索引,BM25 算法等。能够跟一下 Coursea 的公开课「Text Retrieval and Search Engines」,「自制搜索引擎」这本书从源码级别分析了搜索引擎的基本原理。github
搜索引擎工做步骤分为这几步:算法
整个项目存放在 Git@OSC和 https://github.com/Huangtuzhi/just-search-engine。mongodb
搜索引擎由下列 4 个组件构成。api
对应地,在项目结构中服务器
├── just-search-engine │ ├── documentManager.py # 文档管理器 │ ├── indexer.py # 索引构建器 │ ├── indexSearcher.py # 索引检索器 │ ├── README.md │ ├── seedbase # 抓取 WIKI 的 set 对象持久化 │ ├── wiki-postings # 构建的倒排索引 map 对象持久化 │ ├── text │ │ └── wiki-result # 对全部 MongoDB 中的文档统计的结果
咱们把抓取的一个网页叫作文档。以 Wikipedia 的网页做为数据源,这些网页数据都是非结构性的,很适合用 MongoDB 来进行存储。app
MongoDB 须要依赖 mongodb-server 和 pymongo。安装好以后启动服务器post
mongod --dbpath =/opt/mongodb-data --logpath=/opt/mongodb-data/mongodb.log
MongoDB 中有这些字段。网站
posting = { "DocID": 22223, "url": "https://en.wikipedia.org/wiki/Trout", "content": "Trout is the common name for a number of species of freshwater fish", "keyword": "Trout" }
content 存储网页的文字内容。DocID 自增,用来惟一标记文档,和倒排索引中的 DocID 对应,后面在 MongoDB 里也是用这个字段来检索出文字内容。搜索引擎
构建倒排索引只须要两步
{ "search":[[1, 3],[2, 1]], "engine":[[2, 1]] }
上面的索引表示:search 这个词在文档 1 中出现 3 次,在文档 2 中出现 1 次。
索引构建完毕以后,使用 pickle 库将索引持久化到二级存储中。
检索 Query 通常是多个词元组成,如搜索 popular literature
,须要把它分红两个词元 popular
和 literature
来处理。
假设 popular
在倒排索引中的 postings 为 [100, 200, 300]。
literature
在倒排索引中的 postings 为 [233, 300, 400]。
则包含两个词的文档为 300,能够将这个文档返回给用户。
这是在索引中检索一个词元的实现,它返回包含这个词元的 DocID 列表。
def retrive_word(self, word): # 找出 DocID 对应的 url manager = documentManager() collection = manager.connect_mongo() id_list = [] for word in self.word_dictionary[word]: url = collection.find_one({"DocID": int(word[0])})["url"] id_list.append(int(word[0])) return id_list
搜索引擎检索出文档以后,须要选择和查询最相关的文档返回给用户,所以须要对文档进行评估。通常有下列方法:
看一下 TF-IDF 的计算
def caculate_TFIDF(self, word): score_dictionary = {} for posting in self.word_dictionary[word]: DocID = posting[0] freq = posting[1] idf = math.log(float(100) / len(self.word_dictionary[word])) tf = 1 + math.log(int(freq)) if freq > 0 else 0 tfidf_score = tf * idf score_dictionary[DocID] = tfidf_score score = sorted(score_dictionary.iteritems(), key=lambda d:d[1], \ reverse = True) print score
idf 是文档总数和该词元出现过文档总数的商。TF-IDF 做为衡量“词元在文档集合中是否特殊”的一个指标。
将算得的 TF-IDF 分数存储在字典中,最后按值进行排序。
TF-IDF 指标获得的是单个查询词的得分,若查询为一句话,可使用 Okapi BM25 做为评分标准。
BM25 计算公式以下
c(w,q) 表明在 Query 中出现某个词元的计数。通常看成 1 处理。
c(w,d) 表明在一个 Document 中出现某个词元的计数。
k(1-b+b|d|/avdl) 是对文档长度进行归一化的处理。
当查询词为 popular literature
,它会分别计算文档中 popular
和 literature
的得分,而后求和,做为这个文档对于查询词的相关性得分。