simHash

好文章!simhash講得很透徹,而且應用時候,怎麼建索引的方法也有介紹。

原文鏈接:http://grunt1223.iteye.com/blog/964564



在工作學習中,我往往感嘆數學奇蹟般的解決一些貌似不可能完成的任務,並且十分希望將這種喜悅分享給大家,就好比說:“老婆,出來看上帝”…… 


隨着信息爆炸時代的來臨,互聯網上充斥着着大量的近重複信息,有效地識別它們是一個很有意義的課題。例如,對於搜索引擎的爬蟲系統來說,收錄重複的網頁是毫無意義的,只會造成存儲和計算資源的浪費;同時,展示重複的信息對於用戶來說也並不是最好的體驗。造成網頁近重複的可能原因主要包括: 
  • 鏡像網站
  • 內容複製
  • 嵌入廣告
  • 計數改變
  • 少量修改


一個簡化的爬蟲系統架構如下圖所示: 




事實上,傳統比較兩個文本相似性的方法,大多是將文本分詞之後,轉化爲特徵向量距離的度量,比如常見的歐氏距離、海明距離或者餘弦角度等等。兩兩比較固然能很好地適應,但這種方法的一個最大的缺點就是,無法將其擴展到海量數據。例如,試想像Google那種收錄了數以幾十億互聯網信息的大型搜索引擎,每天都會通過爬蟲的方式爲自己的索引庫新增的數百萬網頁,如果待收錄每一條數據都去和網頁庫裏面的每條記錄算一下餘弦角度,其計算量是相當恐怖的。 

我們考慮採用爲每一個web文檔通過hash的方式生成一個指紋(fingerprint)。傳統的加密式hash,比如md5,其設計的目的是爲了讓整個分佈儘可能地均勻,輸入內容哪怕只有輕微變化,hash就會發生很大地變化。我們理想當中的哈希函數,需要對幾乎相同的輸入內容,產生相同或者相近的hashcode,換句話說,hashcode的相似程度要能直接反映輸入內容的相似程度。很明顯,前面所說的md5等傳統hash無法滿足我們的需求。 

simhash是locality sensitive hash(局部敏感哈希)的一種,最早由Moses Charikar在《similarity estimation techniques from rounding algorithms》一文中提出。Google就是基於此算法實現網頁文件查重的。我們假設有以下三段文本: 

the cat sat on the matthe cat sat on a matwe all scream for ice cream

使用傳統hash可能會產生如下的結果: 
引用
irb(main):006:0> p1 = 'the cat sat on the mat' 
irb(main):005:0> p2 = 'the cat sat on a mat' 
irb(main):007:0> p3 = 'we all scream for ice cream' 
irb(main):007:0> p1.hash 
=> 415542861 
irb(main):007:0> p2.hash 
=> 668720516 
irb(main):007:0> p3.hash 
=> 767429688


使用simhash會應該產生類似如下的結果: 
引用
irb(main):003:0> p1.simhash 
=> 851459198 
00110010110000000011110001111110 

irb(main):004:0> p2.simhash 
=> 847263864 
00110010100000000011100001111000 

irb(main):002:0> p3.simhash 
=> 984968088 
00111010101101010110101110011000


海明距離的定義,爲兩個二進制串中不同位的數量。上述三個文本的simhash結果,其兩兩之間的海明距離爲(p1,p2)=4,(p1,p3)=16以及(p2,p3)=12。事實上,這正好符合文本之間的相似度,p1和p2間的相似度要遠大於與p3的。 

如何實現這種hash算法呢?以上述三個文本爲例,整個過程可以分爲以下六步: 
1、選擇simhash的位數,請綜合考慮存儲成本以及數據集的大小,比如說32位 
2、將simhash的各位初始化爲0 
3、提取原始文本中的特徵,一般採用各種分詞的方式。比如對於"the cat sat on the mat",採用兩兩分詞的方式得到如下結果:{"th", "he", "e ", " c", "ca", "at", "t ", " s", "sa", " o", "on", "n ", " t", " m", "ma"} 
4、使用傳統的32位hash函數計算各個word的hashcode,比如:"th".hash = -502157718 
,"he".hash = -369049682,…… 
5、對各word的hashcode的每一位,如果該位爲1,則simhash相應位的值加1;否則減1 
6、對最後得到的32位的simhash,如果該位大於1,則設爲1;否則設爲0 

整個過程可以參考下圖: 



按照Charikar在論文中闡述的,64位simhash,海明距離在3以內的文本都可以認爲是近重複文本。當然,具體數值需要結合具體業務以及經驗值來確定。 
  
使用上述方法產生的simhash可以用來比較兩個文本之間的相似度。問題是,如何將其擴展到海量數據的近重複檢測中去呢?譬如說對於64位的待查詢文本的simhash code來說,如何在海量的樣本庫(>1M)中查詢與其海明距離在3以內的記錄呢?下面在引入simhash的索引結構之前,先提供兩種常規的思路。第一種是方案是查找待查詢文本的64位simhash code的所有3位以內變化的組合,大約需要四萬多次的查詢,參考下圖: 



另一種方案是預生成庫中所有樣本simhash code的3位變化以內的組合,大約需要佔據4萬多倍的原始空間,參考下圖: 



顯然,上述兩種方法,或者時間複雜度,或者空間複雜度,其一無法滿足實際的需求。我們需要一種方法,其時間複雜度優於前者,空間複雜度優於後者。 

假設我們要尋找海明距離3以內的數值,根據抽屜原理,只要我們將整個64位的二進制串劃分爲4塊,無論如何,匹配的兩個simhash code之間至少有一塊區域是完全相同的,如下圖所示: 



由於我們無法事先得知完全相同的是哪一塊區域,因此我們必須採用存儲多份table的方式。在本例的情況下,我們需要存儲4份table,並將64位的simhash code等分成4份;對於每一個輸入的code,我們通過精確匹配的方式,查找前16位相同的記錄作爲候選記錄,如下圖所示: 



讓我們來總結一下上述算法的實質: 
1、將64位的二進制串等分成四塊 
2、調整上述64位二進制,將任意一塊作爲前16位,總共有四種組合,生成四份table 
3、採用精確匹配的方式查找前16位 
4、如果樣本庫中存有2^34(差不多10億)的哈希指紋,則每個table返回2^(34-16)=262144個候選結果,大大減少了海明距離的計算成本 

我們可以將這種方法拓展成多種配置,不過,請記住,table的數量與每個table返回的結果呈此消彼長的關係,也就是說,時間效率與空間效率不可兼得,參看下圖: 



事實上,這就是Google每天所做的,用來識別獲取的網頁是否與它龐大的、數以十億計的網頁庫是否重複。另外,simhash還可以用於信息聚類、文件壓縮等。 

也許,讀到這裏,你已經感受到數學的魅力了。
發佈了147 篇原創文章 · 獲贊 12 · 訪問量 25萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章