什麼是字面文本相似度?
在NLP領域如何判斷兩個文本的相似性是一個基礎性的任務,而文本的相似性往往可以理解爲兩個方面:字面相似性和語義相似性。怎麼理解這個內容呢?就比如說白龍馬
和赤兔馬
,乍一看,”WC,這不就是馬麼有啥區別?一個是白的一個是紅的,也沒啥!“,但是究其本質白龍馬
是龍啊,赤兔馬
就是一匹普通的坐騎。
上面的例子可以認爲是“形似韻不同”的詮釋,還有些是“形不似,神似”,等等。而字面文本相似度就是解決無論語義怎樣,只要兩個文本長得差不多,那就相似。
什麼是SimHash?
說SimHash不難會想到Hash。
Hash對於一個瞭解數據結構的人來說並不陌生,可以認爲是數據的一個“唯一性”id,但是並不唯一,這與hash算法和hash函數的選取有關,有可能出現衝突等問題。扯遠了~
一個文本採用Hash後的編碼可以代表這個文本,但是將整個文本的內容進行hash後,很少有兩個hash文本很類似,也就是說,文本間的hash碼會有很大的差異。也就無法度量文本間的字面相似度。
SimHash是Google發表於2007的論文《Detecting Near-Duplicates for Web Crawling》。設計的初衷是用於搜索引擎的網頁去重的工作。
其實還有一個作用,就是用於判斷兩篇文章是否有抄襲。如果最後的SimHash的值相差不多,很可能文章就有抄襲。
SimHash還有一種叫法,稱爲局部Hash。同時得到的SimHash值通過計算海明距離就可以得到文本間的距離差值。
SimHash計算流程
其中SimHash可以劃分爲5個步驟:
- 分詞
- 對詞做Hash
- 針對每個詞做加權
- 以句子(文本)爲單位進行合併
- 降維
下圖可以很形象的描繪整個算法的計算流程:
step1:分詞
分詞不必多說,是將文本中的句子分爲詞的列表,當然也可以以字爲單位進行。按照下面的句子作爲示例進行介紹。
今天天氣很好 ==> 今天/天氣/很好
step2:Hash
針對詞表中的每個詞彙做hash處理,生成一個32位(或者64位)長度的二進制數。Python中可以輸出字符串所對應的二進制數來代替,不夠的可以補0處理。下面以8位二進制數作爲例子來介紹
hash(今天)=01000101
step3:加權
每個詞彙的重要程度不同,我們可以爲每個詞彙設置不同的權重,從而加以區分。加權的操作就是針對每個詞彙的二進制碼的每一位乘上對應的權值,原先爲0的位置設置爲負值。
weight(今天) = 3
weight_hash(今天) = [-3,3,-3,-3,-3 ,3,-3,3]
step4:合併
將文本中的每個詞彙進行求和處理(對位求和),即可得到文本的hash表示。假設如下所示爲句子的合併後的hash碼
merged_hash = [6,9,-1,0,7,-10,2,3]
step5:降維
其實這一步並不是降維的操作,而是將合併後的hash碼進行歸一化(規範化)的處理。即大於0的位置置爲1,其它位置爲0。
sim_hash = [1, 1, 0, 0, 1, 0, 1, 1]
文本相似度計算
文本的相似度計算有多種方法,但是對於SimHash常用的方法爲海明距離。因爲SimHash碼並不能反映一個文本的具體含義,它僅僅可以理解爲一個文本的唯一性id,所以這就是爲什麼將SimHash又稱爲文本的指紋。
海明距離(Hamming distance)在信息編碼中,兩個合法代碼對應位置上編碼不同的位數稱爲碼距,又稱爲海明距離。
比如:
vec1 = [1,0,1,1,0,0]
vec2 = [1,1,1,0,0,1]
hamming_distance(vec1, vec2) = 3
可以從上邊的例子中瞭解到,第二位,第四位,第六位的編碼不同,一共三個,所以碼距爲3
計算海明距離也有快速的方法,比如說利用向量的異或
hamming_vec = ve1 xor vec2 = [0, 1, 0, 1, 0, 1]
至此我們就可以對任意兩個文本計算它的SimHash的距離了。當定義了最小的碼距之後就可以確定哪些文本是相似的。
超大規模數據集計算
當文本的數量較少的時候輪訓一遍數據集可以很快的找到想要的結果。但是如果語料中的文本規模有100億個怎麼辦呢?加設備!好那要是有100萬億個文本怎麼辦呢?加設備是不夠的的,需要利用鴿巢原理。對文本SimHash的向量維度進行切分,切分的個數爲最小碼距加1。並以這些切分後的結果進行建立倒排索引,加速計算。