minHash最小哈希原理
前言
在數據挖掘中,一個最基本的問題就是比較兩個集合的相似度。通常通過遍歷這兩個集合中的所有元素,統計這兩個集合中相同元素的個數,來表示集合的相似度;這一步也可以看成特征向量間相似度的計算(歐氏距離,余弦相似度)。當這兩個集合里的元素數量異常大(特征空間維數很大),同時又有很多個集合需要判斷兩兩間的相似度時,傳統方法會變得十分耗時,最小哈希(minHash)可以用來解決該問題。
Jaccard相似度
在本例中,我們僅探討集合的相似度,先來看Jaccard相似度。假設有兩個集合A,B,則
Jaccard(A, B)= |A ∩ B| / |A ∪ B|,我們舉一個例子:
在上述例子中,sim(A,B)=2/7。
minHash最小哈希
假設現在有4個集合,分別為S1,S2,S3,S4;其中,S1={a,d}, S2={c}, S3={b,d,e}, S4={a,c,d},所以全集U={a,b,c,d,e}。我們可以構造如下0-1矩陣:
為了得到各集合的最小哈希值,首先對矩陣進行隨機行打亂,則某集合(某一列)的最小哈希值就等於打亂后的這一列第一個值為1的行所在的行號。舉一個例子:
定義一個最小哈希函數h,用於模擬對矩陣進行隨機行打亂,打亂后的0-1矩陣為
如圖所示,h(S1)=2, h(S2)=4, h(S3)=0, h(S4)=2。
在經過隨機行打亂后,兩個集合的最小哈希值相等的概率等於這兩個集合的Jaccard相似度,證明如下:
現僅考慮集合S1和S2,那么這兩列所在的行有下面3種類型:
1、S1和S2的值都為1,記為X
2、只有一個值為1,另一個值為0,記為Y
3、S1和S2的值都為0,記為Z
S1和S2交集的元素個數為x,並集的元素個數為x+y,所以sim(S1,S2) = Jaccard(S1,S2) = x/(x+y)。接下來計算h(S1)=h(S2)的概率,經過隨機行打亂后,從上往下掃描,在碰到Y行之前碰到X行的概率為x/(x+y),即h(S1)=h(S2)的概率為x/(x+y)。
最小哈希簽名
---------------------------------------------------
最小簽名的計算
其實得到上面的簽名矩陣之后,我們就可以用簽名矩陣中列與列之間的相似度來計算集合間的Jaccard相似度了;但是這樣會帶來一個問題,就是當一個特征矩陣很大時(假設有上億行),那么對其進行行打亂是非常耗時,更要命的是還要進行多次行打亂。 為了解決這個問題,可以通過一些隨機哈希函數來模擬行打亂的效果。具體做法如下:
假設我們要進行n次行打亂,則為了模擬這個效果,我們選用n個隨機哈希函數h1,h2,h3…hn(注意,這里的h跟上面的h不是同一個哈希函數,只是為了方便,就不用其他字母了)。處理過程如下:
令SIG(i,c)表示簽名矩陣中第i個哈希函數在第c列上的元素。開始時,將所有的SIG(i,c)初始化為Inf(無窮大),然后對第r行進行如下處理:
1. 計算h1(r), h2(r)…hn(r);
2. 對於每一列c:
a) 如果c所在的第r行為0,則什么都不做;
b) 如果c所在的第r行為1,則對於每個i=1,2…n,將SIG(i,c)置為原來的SIG(i,c)和hi(r)之間的最小值。
(看不懂的直接看例子吧,這里講的比較晦澀)
例如,考慮上面的特征矩陣,將abcde換成對應的行號,在后面加上兩個哈希函數,其中h1(x)=(x+1) mod 5,h2(x) = (3*x+1) mod 5,注意這里x指的是行號:
接下來計算簽名矩陣。一開始時,全部初始化為Inf:
接着看特征矩陣中的第0行;這時S2和S3的值為0,所以無需改動;S1和S4的值為1,需改動。h1= 1,h2= 1。1比Inf小,所以需把S1和S4這兩個位置對應的值替換掉,替換后效果如下:
接着看第1行;只有S3的值為1;此時h1= 2,h2= 4;對S3那一列進行替換,得到:
接着看第2行;S2和S4的值為1;h1=3,h2=2;因為簽名矩陣S4那一列的兩個值都為1,比3和2小,所以只需替換S2那一列:
接着看第3行;S1,S3和S4的值都為1,h1=4, h2= 0;替換后效果如下:
接着看第4行;S3值為1,h1=0, h2= 3,最終效果如下:
這樣,所有的行都被遍歷一次了,最終得到的簽名矩陣如下:
基於這個簽名矩陣,我們就可以估計原始集合之間的Jaccard相似度了。由於S2和S4對應的列向量完全一樣,所以可以估計SIM(S1,S4)=1;同理可得SIM(S1,S3) = 0.5;
局部敏感哈希算法(LSH)
通過上面的方法處理過后,一篇文檔可以用一個很小的簽名矩陣來表示,節省下很多內存空間;但是,還有一個問題沒有解決,那就是如果有很多篇文檔,那么如果要找出相似度很高的文檔,其中一種辦法就是先計算出所有文檔的簽名矩陣,然后依次兩兩比較簽名矩陣的兩兩;這樣做的缺點是當文檔數量很多時,要比較的次數會非常大。那么我們可不可以只比較那些相似度可能會很高的文檔,而直接忽略過那些相似度很低的文檔。接下來我們就討論這個問題的解決方法。
首先,我們可以通過上面的方法得到一個簽名矩陣,然后把這個矩陣划分成b個行條(band),每個行條由r行組成。對於每個行條,存在一個哈希函數能夠將行條中的每r個整數組成的列向量(行條中的每一列)映射到某個桶中。可以對所有行條使用相同的哈希函數,但是對於每個行條我們都使用一個獨立的桶數組,因此即便是不同行條中的相同列向量,也不會被哈希到同一個桶中。這樣,只要兩個集合在某個行條中有落在相同桶的兩列,這兩個集合就被認為可能相似度比較高,作為后續計算的候選對;下面直接看一個例子:
例如,現在有一個12行簽名矩陣,把這個矩陣分為4個行條,每個行條有3行;為了方便,這里只寫出行條1的內容。
可以看出,行條1中第2列和第4列的內容都為[0,2,1],所以這兩列會落在行條1下的相同桶中,因此不過在剩下的3個行條中這兩列是否有落在相同桶中,這兩個集合都會成為候選對。在行條1中不相等的兩列還有另外的3次機會成為候選對,因為他們只需在剩下的3個行條中有一次相等即可。
經過上面的處理后,我們就找出了相似度可能會很高的一些候選對,接下來我們只需對這些候選隊進行比較就可以了,而直接忽略那些不是候選對的集合
---------------------------------------------------
那么,怎樣得到P( h(S1)=h(S2) )呢?我們僅需要進行N次哈希運算模擬N次隨機行打亂,然后統計|h(S1)=h(S2)|,就有 P=|h(S1)=h(S2)| / N 了。有了上一章節的證明,我們就可以通過多次進行最小哈希運算,來構造新的特征向量,也就是完成了降維,得到的新矩陣稱為最小哈希簽名矩陣。舉一個例子,假設進行2次最小哈希運算,h1(x)=(x+1) mod 5,h2(x) = (3*x+1) mod 5,可以得到簽名矩陣SIG:
計算得到sim(S1,S4)=1,sim(S1,S3)=0.5。當然本例數據量太小,簽名矩陣的估計值跟真實Jaccard誤差較大。
這里提供一種僅掃描一次就可以得到最小簽名矩陣的算法:
令SIG(i,c)表示簽名矩陣中第i個哈希函數在第c列上的元素。開始時,將所有的SIG(i,c)初始化為Inf(無窮大),然后對第r行進行如下處理:
1. 計算h1(r), h2(r)…hn(r);
2. 對於每一列c:
a) 如果c所在的第r行為0,則什么都不做;
b) 如果c所在的第r行為1,則對於每個i=1,2…n,將SIG(i,c)=min(SIG(i,c),hi(r))。
再看不懂的可以參考minHash(最小哈希)和LSH(局部敏感哈希)。
MinHash的應用
MinHash可以應用在推薦系統中,將上述0-1矩陣的橫軸看成商品,豎軸看成用戶,有成千上萬的用戶對有限的商品作出購買記錄,具體可以參考基於協同過濾,NMF和Baseline的推薦算法一文。MinHash也可以應用在自然語言處理的文本聚類中,將上述0-1矩陣的橫軸看成文檔,豎軸看成詞匯或n-gram。這里我提出一種基於依賴樹的同義詞聚類算法:
假設現有沒有語法錯誤的文本集,我們使用依賴樹工具得到上圖的邊,先用TF-IDF逆文檔頻率過濾得到我們想要聚類的詞匯,然后用倒排索引建立類似ESA的詞匯-概念向量,例如:
發展:nsubj(~,交通),advmod(~,比較),relcl(地方,~),mark(~,的)
發達:nsubj(~,交通),advmod(~,比較),relcl(地方,~),mark(~,的)
這樣,就有待聚類的詞匯有限,概念數量龐大的情形,應用minHash完成降維,再來聚類,具體可以參考從n-gram中文文本糾錯,到依存樹中文語法糾錯以及同義詞查找一文。
LSH局部敏感哈希
我們得到簽名矩陣后,對集合還是需要進行兩兩比較,假如集合數量也極度龐大的話,我們希望僅比較那些相似度可能很高的集合,而直接忽略那些相似度很低的集合,LSH就可以用來解決該問題。
LSH用到“桶”的概念,直接舉一個例子,現有一個12行的簽名矩陣,我們設置桶大小為3,則可分為4個桶,如下圖:
對於S2,我們僅需要尋找那些桶相同的集合來計算相似度,例如:
我們僅需要計算sim(S2, S3),sim(S2, S4),sim(S2, S5),因為這些集合出現過與S2桶相同的情況。再不懂可以看minHash(最小哈希)和LSH(局部敏感哈希)一文。
Reference
- 分類:數掘篇
- 字數:1656