自制簡單搜索引擎

搜索引擎(Search Engine)是指根據必定的策略、運用計算機技術從互聯網上搜集信息,在對信息進行組織和處理後,爲用戶提供檢索服務。在平常生活中,能夠看到 Google 等 Web 檢索網站,還有郵件檢索和專利檢索等各類應用程序。git

在本身寫一個搜索引擎以前,須要先了解基本的原理和概念。好比分詞,倒排索引,BM25 算法等。能夠跟一下 Coursea 的公開課「Text Retrieval and Search Engines」「自制搜索引擎」這本書從源碼級別分析了搜索引擎的基本原理。github

搜索引擎工做步驟分爲這幾步:算法

  • 爬蟲模塊 Crawler 在網頁上抓取感興趣的網頁數據存儲爲 Cached pages
  • 索引構造器 Indexer 對 Cached pages 處理生成倒排索引(Inverted Index)
  • 對查詢詞 Query 在倒排索引中查找對應的文檔 Document
  • 計算 Query 和 Document 的關聯度,返回給用戶 TopN 個結果
  • 根據用戶點擊 TopN 的行爲去修正用戶查詢的 Query,造成反饋閉環。

整個項目存放在 Git@OSChttps://github.com/Huangtuzhi/just-search-enginemongodb

組成組件

搜索引擎由下列 4 個組件構成。api

輸入圖片說明

  • 文檔管理器(Document Manager)
  • 索引構建器(Indexer)
  • 索引管理器(Index Manager)
  • 索引檢索器(Index Searcher)

對應地,在項目結構中服務器

├── 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 裏也是用這個字段來檢索出文字內容。搜索引擎

構建倒排索引

構建倒排索引只須要兩步

  • 對全部文檔進行詞頻統計,將 <word, DocID, Freq> 寫入到二級存儲(文本)中
  • 將文檔按照 word 進行合併,構成倒排索引。使用字典存儲索引,word 做爲 key,[DocID, Freq] 組成的 list 做爲 value。
{
    "search":[[1, 3],[2, 1]],
    "engine":[[2, 1]]
}

上面的索引表示:search 這個詞在文檔 1 中出現 3 次,在文檔 2 中出現 1 次。

索引構建完畢以後,使用 pickle 庫將索引持久化到二級存儲中。

檢索文檔

檢索 Query 通常是多個詞元組成,如搜索 popular literature,須要把它分紅兩個詞元 popularliterature 來處理。

假設 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

搜索引擎檢索出文檔以後,須要選擇和查詢最相關的文檔返回給用戶,所以須要對文檔進行評估。通常有下列方法:

  • TF-IDF 詞頻-逆文檔頻率
  • 餘弦類似度
  • Okapi BM25

看一下 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 分數存儲在字典中,最後按值進行排序。

文檔排名——計算 Okapi BM25

TF-IDF 指標獲得的是單個查詢詞的得分,若查詢爲一句話,可使用 Okapi BM25 做爲評分標準。

BM25 計算公式以下

輸入圖片說明

c(w,q) 表明在 Query 中出現某個詞元的計數。通常看成 1 處理。

c(w,d) 表明在一個 Document 中出現某個詞元的計數。

k(1-b+b|d|/avdl) 是對文檔長度進行歸一化的處理。

當查詢詞爲 popular literature,它會分別計算文檔中 popularliterature 的得分,而後求和,做爲這個文檔對於查詢詞的相關性得分。

參考

https://www.coursera.org/course/textretrieval

自制搜索引擎

http://fuzhii.com/2016/01/08/develop-search-engine/

相關文章
相關標籤/搜索