新手學信息檢索2:倒排表與存儲


這篇就說一個信息檢索里面理解最簡單的一個東西吧,它就叫做倒排表或者倒排索引。但是這只是個名字,我想大家都知道它是什么就行了,不必糾結於名稱。先說說倒排表張什么樣子吧!

倒排表以詞做索引,內容為包含該詞的文檔編號。對於上圖可知,文檔1、3、5、7、9包含詞"Cat",文檔2、5、8、10包含詞"Dog"。你可能問這么簡單的東西能干啥?其實他就是搜索引擎中的最關鍵的核心數據結構。那么搜索引擎如何根據用戶的查詢來找到相關的文檔呢?如果用戶查詢“Cat”,那么只要順着Cat鏈把文檔1、3、5、7、9返回給用戶就行了。如果用戶想得到同時包含“Cat”與“Dog“的文檔怎么辦?這個過程就類似於我們歸並排序時合並兩個有序段的過程,利用兩個指針分別指向Cat鏈的第一個元素和Dog鏈的第一個元素,之后通過比較兩個指針處文檔編號的大小進行相應的移動,找到兩個鏈中共同的文檔。整個過程的時間復雜度是線性的。上面介紹的是一種簡化版的倒排表,實際搜索引擎中使用的倒排表要比這個復雜一些,也有可能使用多個不同的倒排表來完成不同的搜索任務,但是本質上都差不多。

那么如何建立這個倒排表呢?其實大概過程很簡單:提出文檔中的每一個詞,並把該文檔編號插入到該詞索引的鏈上。這里的主要問題在於提取詞上,英文文檔詞與詞之間是由空格以及標點符號隔開的,但是漢語里面沒有這么明顯的分割標志,所以有一門學問叫做自然語言處理,里面專門研究了漢語的分詞方法,現在的大多數分詞方法都能夠達到90%多的正確率。

對於邏輯上這么容易理解倒排表,在實際上實現並不容易。除了分詞外,不容易就不容易在這個倒排表的規模將是相當之大,以至於一台計算機的內存空間甚至一台計算機的磁盤空間都不足以存下整個倒排表。實際中的搜索引擎索引的網頁數目可達到百億,以每個網頁平均1000個詞來計算,每個文檔編號按照4字節整數來存儲,那么粗略算下來倒排表也至少需要10000000000*1000*4B=40TB的存儲空間,那么對於怎么存儲這個倒排表確實是一個棘手的問題。

解決大數據存儲的形式有兩種,一種是分布式存儲,一種是壓縮。前一種方式很好理解,意思就是一台機器存不下的東西,那么就把它存在多台機器上,這里面也需要有一些復雜的技術。當然,如果倒排表存儲在多台機器上,對用戶的請求的處理也就變得復雜了一些。后一種壓縮技術就是把數據的規模變小,用更加緊湊的表現形式對源數據進行表示,達到內容不變但是存儲空間變小的目的,實際中我們常用的壓縮軟件就是這個道理。比如Linux下的Gzip利用的就是LZ77算法和哈夫曼曼編碼來對任意的二進制數據進行壓縮的。

分布式存儲本人不太了解,所以這些技術的介紹就得大家自己找找園子里的牛人的博客了。而數據壓縮相對比較簡單,本文就介紹一下倒排表的壓縮算法。有人會說,那用現成的壓縮工具對倒排表壓縮不就可以了嗎?雖然可以減少倒排表的存儲空間,但是這種平時用的壓縮工具壓縮出的數據的訪問效率比較低。

例如:“我是中國人”在Gzip壓縮之后,在壓縮數據中找是否有“中”字,你就不得不把所有的數據解壓之后才能得到。如果是在壓縮之后的倒排表中去找一個文檔,那么不得不把倒排表全部解壓,而這個解壓過程本身就是一個費時的過程,所以不划算。

現在回到倒排表的壓縮問題上來,解決一個問題必須理論聯系實際才能得到令人滿意的效果。

在介紹倒排表數據壓縮算法之前,先介紹幾個常用的技術,也能夠有效的減少倒排表的大小。

這里先從實際應用背景作為切入點來優化倒排表。這個系統是給人用的,那么一個人會輸入什么樣的查詢詞呢?

對於英文來說,會不會有人去查”the”,”a”, “an”等等這些詞?顯然這些詞沒有什么實際意義。人們往往更喜歡查詢一些名字、動詞。所以倒排表沒有必為”the”,”a”, “an”這些詞建立索引,這樣倒排表的規模會明顯減少不小。”the”, “a”, “an”這種詞叫做停用詞。有人專門去研究什么樣的詞可以當成停用詞,以至於找到了幾百個可以認為是停用詞的詞。而這些詞我們會經常用到。大家可以隨便找一篇英文文章,把其中所有的停用詞去掉,之后看看還有多少個詞。所以不對停用詞做索引能夠有效減少倒排表的大小。漢語也有相應的停用詞表,所以這個技術對中文檢索也同樣適用。

還有一個技術叫做詞干化。這個技術貌似只對歐洲的文字才有作用,對漢字作用不大。這里也介紹一下。比如單詞“cars”是“car”的復數形式,但是對於我們來說這兩個詞的意思都一樣,所以把“cars”用“car”代替是一個好方法。“car”可以認為是“cars”的詞干。實際上一個詞的單復數、時態等都能夠進行詞干化。這種方法甚至比去除停用詞的效果還要好。大家也可以試試隨便找一篇文章,之后把所有的詞都詞干化看看還有多少個不同的詞。和上面的技術一樣,也有人專門研究英文單詞的詞干化,提供詞干化的服務。

以上兩個技術聯合起來用就可以很大程度上的減少倒排表的大小,雖然這兩個技術也對檢索帶來些負面的問題,但是始終認為利大於弊。

現在回到正題,如果利用了上面兩個方法后,倒排表還是很大怎么辦?那么壓縮算法開始上場了。這里我們不使用Gzip等復雜的算法,由於倒排表的形式簡單,所以對簡單的問題有着簡單的解決方法。

文檔數目必須用4字節整數表示,但是一個索引之中的文檔編號相鄰間隔不會太大,可以用少於4字節表示。例如,如果我們確定文檔編號間隔不會超過兩個字節表示的范圍,那么對於一個倒排表我們只把第一個文檔編號用4字節表示,其它的用2字節間隔來表示,這種方法可以使倒排表的大小為原來的一半。如本文一開始給的數字來說,倒排表大小可以由40TB變成20TB。上面給這個思想可行也很誘人,只不過還存在一個必須解決的問題:文檔間隔有可能和文檔編號是一個量級的,用兩字節無法表示。

下面介紹一種可變字節編碼方式:利用整數個字節對文檔間隔進行編碼。每個字節的第1個比特代表延續位,后7個比特代表編碼比特。如果第一個比特位1,代表這個字節是編碼的最后一個字節,否則不是最后一個字節。

例如:

5表示成可變字節編碼二進制形式為: 10000101

214577表示成可變字節編碼二進制形式為: 00001101 00001100 10110001。

對於第二個例子給出其還原方式:拿出每個字節的低七位組合得到一個21比特二進制數,這個數字就是214577。這種編碼方式的編解碼過程都非常簡單,而且時間開銷很小,相比於Gzip這種方法要好的多。

這種編碼方式出奇的簡單,以致於你認為它的實際效果能好到哪去。有人在權威的文檔集合(大概包含800000篇文檔)上構建倒排表,原始大小為250MB,而經過變長編碼得到的倒排表大小只有116MB。要解釋這種現象就可能需要Zipf定律了,這個定律大家可以有興趣查一查。

除了可變長編碼,還有諸如γ編碼等等,這些編碼方式操作存儲單元的粒度要比一個字節要小,所以編碼方式比可變長編碼復雜,能得到更好的理論壓縮效率和實際壓縮效率。但是同樣在這800000文檔機上構建倒排表,編碼使倒排表的大小變為101MB,僅僅比可變長編碼少13%。

假設通過詞干化,去除停用詞能夠使倒排表大小降低1/10,之后可變長編碼又使倒排表大小降低1/2,那么現在我們的倒排表大小就為18TB左右了。雖然這么大的數據還是得需要分布式存儲的方法,但是這樣可以減少對機器數量的需求,是比較節省成本的。而對於一個程序員來說,想要做一個單台機器索引幾百萬網頁的搜索引擎來說,也是一件可能的事了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM