1. 程式人生 > >全文檢索引擎SOLR—–全文檢索基本原理

全文檢索引擎SOLR—–全文檢索基本原理

場景:小時候我們都使用過新華字典,媽媽叫你翻開第38頁,找到“坑爹”所在的位置,此時你會怎麼查呢?毫無疑問,你的眼睛會從38頁的第一個字開始從頭至尾地掃描,直到找到“坑爹”二字為止。這種搜尋方法叫做順序掃描法。對於少量的資料,使用順序掃描是夠用的。但是媽媽叫你查出坑爹的“坑”字在哪一頁時,你要是從第一頁的第一個字逐個的掃描下去,那你真的是被坑了。此時你就需要用到索引。索引記錄了“坑”字在哪一頁,你只需在索引中找到“坑”字,然後找到對應的頁碼,答案就出來了。因為在索引中查詢“坑”字是非常快的,因為你知道它的偏旁,因此也就可迅速定位到這個字。

那麼新華字典的目錄(索引表)是怎麼編寫而成的呢?首先對於新華字典這本書來說,除去目錄後,這本書就是一堆沒有結構的資料集。但是聰明的人類善於思考總結,發現每個字都會對應到一個頁碼,比如“坑”字就在第38頁,“爹”字在第90頁。於是他們就從中提取這些資訊,構造成一個有結構的資料。類似資料庫中的表結構:

word    page_no
---------------
坑        38
爹        90
...       ...

這樣就形成了一個完整的目錄(索引庫),查詢的時候就非常方便了。對於全文檢索也是類似的原理,它可以歸結為兩個過程:1.索引建立(Indexing)2. 搜尋索引(Search)。那麼索引到底是如何建立的呢?索引裡面存放的又是什麼東西呢?搜尋的的時候又是如何去查詢索引的呢?帶著這一系列問題繼續往下看。

索引

Solr/Lucene採用的是一種反向索引,所謂反向索引:就是從關鍵字到文件的對映過程,儲存這種對映這種資訊的索引稱為反向索引

inverted_index_thumb.jpg

  • 左邊儲存的是字串序列
  • 右邊是字串的文件(Document)編號連結串列,稱為倒排表(Posting List)

欄位串列表和文件編號連結串列兩者構成了一個字典。現在想搜尋”lucene”,那麼索引直接告訴我們,包含有”lucene”的文件有:2,3,10,35,92,而無需在整個文件庫中逐個查詢。如果是想搜既包含”lucene”又包含”solr”的文件,那麼與之對應的兩個倒排表去交集即可獲得:3、10、35、92。

索引建立

假設有如下兩個原始文件:
文件一:Students should be allowed to go out with their friends, but not allowed to drink beer.
文件二:My friend Jerry went to school to see his students but found them drunk which is not allowed.
建立過程大概分為如下步驟:
index-build

一:把原始文件交給分片語件(Tokenizer)
分片語件(Tokenizer)會做以下幾件事情(這個過程稱為:Tokenize),處理得到的結果是詞彙單元(Token)

  1. 將文件分成一個一個單獨的單詞
  2. 去除標點符號
  3. 去除停詞(stop word)
    • 所謂停詞(Stop word)就是一種語言中沒有具體含義,因而大多數情況下不會作為搜尋的關鍵詞,這樣一來建立索引時能減少索引的大小。英語中停詞(Stop word)如:”the”、”a”、”this”,中文有:”的,得”等。不同語種的分片語件(Tokenizer),都有自己的停詞(stop word)集合。經過分詞(Tokenizer)後得到的結果稱為詞彙單元(Token)。上例子中,便得到以下詞彙單元(Token)
      "Students","allowed","go","their","friends","allowed","drink","beer","My","friend","Jerry","went","school","see","his","students","found","them","drunk","allowed"

二:詞彙單元(Token)傳給語言處理元件(Linguistic Processor)
語言處理元件(linguistic processor)主要是對得到的詞元(Token)做一些語言相關的處理。對於英語,語言處理元件(Linguistic Processor)一般做以下幾點:

  1. 變為小寫(Lowercase)。
  2. 將單詞縮減為詞根形式,如”cars”到”car”等。這種操作稱為:stemming。
  3. 將單詞轉變為詞根形式,如”drove”到”drive”等。這種操作稱為:lemmatization。

語言處理元件(linguistic processor)處理得到的結果稱為詞(Term),例子中經過語言處理後得到的詞(Term)如下:

"student","allow","go","their","friend","allow","drink","beer","my","friend","jerry","go","school","see","his","student","find","them","drink","allow"。

經過語言處理後,搜尋drive時drove也能被搜尋出來。Stemming 和 lemmatization的異同:

  • 相同之處:
    1. Stemming和lemmatization都要使詞彙成為詞根形式。
  • 兩者的方式不同:
    1. Stemming採用的是”縮減”的方式:”cars”到”car”,”driving”到”drive”。
    2. Lemmatization採用的是”轉變”的方式:”drove”到”drove”,”driving”到”drive”。
  • 兩者的演算法不同:
    1. Stemming主要是採取某種固定的演算法來做這種縮減,如去除”s”,去除”ing”加”e”,將”ational”變為”ate”,將”tional”變為”tion”。
    2. Lemmatization主要是採用事先約定的格式儲存某種字典中。比如字典中有”driving”到”drive”,”drove”到”drive”,”am, is, are”到”be”的對映,做轉變時,按照字典中約定的方式轉換就可以了。
    3. Stemming和lemmatization不是互斥關係,是有交集的,有的詞利用這兩種方式都能達到相同的轉換。

三:得到的詞(Term)傳遞給索引元件(Indexer)

  1. 利用得到的詞(Term)建立一個字典
    Term    Document ID
    student     1
    allow       1
    go          1
    their       1
    friend      1
    allow       1
    drink       1
    beer        1
    my          2
    friend      2
    jerry       2
    go          2
    school      2
    see         2
    his         2
    student     2
    find        2
    them        2
    drink       2
    allow       2
  2. 對字典按字母順序排序:
    Term    Document ID
    allow       1
    allow       1
    allow       2
    beer        1
    drink       1
    drink       2
    find        2
    friend      1
    friend      2
    go          1
    go          2
    his         2
    jerry       2
    my          2
    school      2
    see         2
    student     1
    student     2
    their       1
    them        2
  3. 合併相同的詞(Term)成為文件倒排(Posting List)連結串列postlist
    • Document Frequency:文件頻次,表示多少文件出現過此詞(Term)
    • Frequency:詞頻,表示某個文件中該詞(Term)出現過幾次

對詞(Term) “allow”來講,總共有兩篇文件包含此詞(Term),詞(Term)後面的文件連結串列總共有兩個,第一個表示包含”allow”的第一篇文件,即1號文件,此文件中,”allow”出現了2次,第二個表示包含”allow”的第二個文件,是2號文件,此文件中,”allow”出現了1次

至此索引建立完成,搜尋”drive”時,”driving”,”drove”,”driven”也能夠被搜到。因為在索引中,”driving”,”drove”,”driven”都會經過語言處理而變成”drive”,在搜尋時,如果您輸入”driving”,輸入的查詢語句同樣經過分片語件和語言處理元件處理的步驟,變為查詢”drive”,從而可以搜尋到想要的文件。

搜尋步驟

搜尋”microsoft job”,使用者的目的是希望在微軟找一份工作,如果搜出來的結果是:”Microsoft does a good job at software industry…”,這就與使用者的期望偏離太遠了。如何進行合理有效的搜尋,搜尋出使用者最想要得結果呢?搜尋主要有如下步驟:

一:對查詢內容進行詞法分析、語法分析、語言處理

  1. 詞法分析:區分查詢內容中單詞和關鍵字,比如:english and janpan,”and”就是關鍵字,”english”和”janpan”是普通單詞。
  2. 根據查詢語法的語法規則形成一棵樹
    grammer_tree.jpg
  3. 語言處理,和建立索引時處理方式是一樣的。比如:leaned–>lean,driven–>drive

二:搜尋索引,得到符合語法樹的文件集合
三:根據查詢語句與文件的相關性,對結果進行排序

我們把查詢語句也看作是一個文件,對文件與文件之間的相關性(relevance)進行打分(scoring),分數高比較越相關,排名就越靠前。當然還可以人工影響打分,比如百度搜索,就不一定完全按照相關性來排名的。

如何評判文件之間的相關性?一個文件由多個(或者一個)詞(Term)組成,比如:”solr”, “toturial”,不同的詞可能重要性不一樣,比如solr就比toturial重要,如果一個文件出現了10次toturial,但只出現了一次solr,而另一文件solr出現了4次,toturial出現一次,那麼後者很有可能就是我們想要的搜的結果。這就引申出權重(Term weight)的概念。

權重表示該詞在文件中的重要程度,越重要的詞當然權重越高,因此在計算文件相關性時影響力就更大。通過詞之間的權重得到文件相關性的過程叫做空間向量模型演算法(Vector Space Model)

影響一個詞在文件中的重要性主要有兩個方面:

  • Term Frequencey(tf),Term在此文件中出現的頻率,ft越大表示越重要
  • Document Frequency(df),表示有多少文件中出現過這個Trem,df越大表示越不重要
    物以希為貴,大家都有的東西,自然就不那麼貴重了,只有你專有的東西表示這個東西很珍貴,權重的公式:

空間向量模型

文件中詞的權重看作一個向量

Document = {term1, term2, …… ,term N}
Document Vector = {weight1, weight2, …… ,weight N}

把欲要查詢的語句看作一個簡單的文件,也用向量表示:

Query = {term1, term 2, …… , term N}
Query Vector = {weight1, weight2, …… , weight N}

把搜尋出的文件向量及查詢向量放入N維度的空間中,每個詞表示一維:

夾角越小,表示越相似,相關性越大