搜索引擎(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
的得分,而後求和,做爲這個文檔對於查詢詞的相關性得分。