1. 程式人生 > >LSH︱python實現局部敏感隨機投影森林——LSHForest/sklearn(一)

LSH︱python實現局部敏感隨機投影森林——LSHForest/sklearn(一)

公式 排序 矩陣 nal one 計算過程 結構 成了 sam

關於局部敏感哈希算法。之前用R語言實現過,可是由於在R中效能太低。於是放棄用LSH來做類似性檢索。學了python發現非常多模塊都能實現,並且通過隨機投影森林讓查詢數據更快。覺得能夠試試大規模應用在數據類似性檢索+去重的場景。

私覺得,文本的類似性能夠分為兩類:一類是機械類似性;一類是語義類似性。
機械類似性代表著,兩個文本內容上的相關程度。比方“你好嗎”和“你好”的類似性。純粹代表著內容上字符是否全然共現,應用場景在:文章去重;
語義類似性代表著,兩個文本語義上的類似程度。比方“蘋果”和“公司”的類似性。本篇不做這一討論

之前寫關於R語言實現的博客:
R語言實現︱局部敏感哈希算法(LSH)解決文本機械類似性的問題(一,基本原理)
R語言實現︱局部敏感哈希算法(LSH)解決文本機械類似性的問題(二。textreuse介紹)

機械類似性python版的四部曲:
LSH︱python實現局部敏感隨機投影森林——LSHForest/sklearn(一)
LSH︱python實現局部敏感哈希——LSHash(二)
類似性︱python+opencv實現pHash算法+hamming距離(simhash)(三)
LSH︱python實現MinHash-LSH及MinHash LSH Forest——datasketch(四)
.


一、隨機投影森林

本節參考:論文《基於隨機投影的場景文本圖像聚類方法研究》與博客 隨機投影森林-一種近似近期鄰方法(ANN)

1、隨機投影森林理論與實現偽代碼

當數據個數比較大的時候,線性搜索尋找KNN的時間開銷太大,並且須要讀取全部的數據在內存中,這是不現實的。因此,實際project上,使用近似近期鄰也就是ANN問題。
當中一種方法是利用隨機投影樹,對全部的數據進行劃分,將每次搜索與計算的點的數目減小到一個可接受的範圍,然後建立多個隨機投影樹構成隨機投影森林,將森林的綜合結果作為終於的結果。

?建立一棵隨機投影樹的過程大致例如以下(以二維空間為例):

  • 隨機選取一個從原點出發的向量
  • 與這個向量垂直的直線將平面內的點劃分為了兩部分
  • 將屬於這兩部分的點分別劃分給左子樹和右子樹

在數學計算上。是通過計算各個點與垂直向量的點積完畢這一步驟的,點積大於零的點劃分到左子樹,點積小於零的點劃分到右子樹。


註意一點。圖中不帶箭頭的直線是用於劃分左右子樹的根據,帶箭頭的向量是用於計算點積的。這樣,原有的點就劃分為了兩部分,圖比例如以下:
技術分享圖片
可是此時一個劃分結果內的點的數目還是比較多。因此繼續劃分。再次隨機選取一個向量。與該向量垂直的直線將全部點進行了劃分。圖比例如以下:
技術分享圖片
註意一點,此時的劃分是在上一次劃分的基礎上進行的。


?也就是說如今圖中的點已經被劃分成了四部分,相應於一棵深度為2。有四個葉節點的樹。

以此類推繼續劃分下去,直到每一個葉節點中點的數目都達到一個足夠小的數目。

註意這棵樹並非全然樹。

隨機投影森林的建立須要兩個參數。即單棵樹的深度 + 森林數量。


這兩個參數決定了數據集的分散程度以及隨機投影後得到的向量維數。

利用這棵樹對新的點進行近期鄰計算時,首先通過計算該點與每次劃分所用向量的點積。來找到其所屬於的葉節點,然後利用這個葉節點內的??這些點進行近期鄰算法的計算。


這個過程是一棵隨機投影樹的計算過程。利用相同的方法。建立多個隨機投影樹構成隨機森林,將森林的總和結果作為終於的結果。


.

2、相應拓展

Wright等人 已將隨機投影的方法應用於視角變化的人臉識別,Nowak等人 採用隨機投影的方法學習視覺詞的類似度度量。Freund等人將隨機投影應用於手寫體識別上,取得了非常好的效果。
.

3、隨機投影森林構造向量+聚類

論文《基於隨機投影的場景文本圖像聚類方法研究》中,將每一個葉子節點當成一維特征,用葉子節點的特征點個數作為葉子節點的描寫敘述,最後得到測試圖像的特征向量。


有點類似word2vec之中的霍夫曼樹。

論文中的實驗結果:
技術分享圖片
當中。森林規模10棵。

  • 第一組實驗。使用sift局部特征描寫敘述。在不同的deep。樹深度下識別的準確率。當中F=(2 * R *
    P)/(R+P),大致來看深度deep=8來說。比較合理。
  • 第二組實驗,AP聚類和Kmeans聚類在不同深度的區別,實驗數據是google圖片集,局部特征描寫敘述使用ASIFT方法,用AP和Kmeans分別進行聚類。由於AP聚類算法的類別數由類似矩陣的對角線元素值決定,所以須要多次測試,終於以類似度矩陣的中值為類似度矩陣對角線上的元素值,用來控制聚類的類別數。得到的AP聚類各項評價指標值是多次實驗的平均值。

    而K-means
    聚類是多次實驗不同的叠代次數與類別數,以最好的聚類結果作為終於結果

技術分享圖片

  • 第三組實驗實驗數據是google圖片集。聚類算法使用AP聚類,用不同的局部特征描寫敘述法(ASIFT與SIFT)得到的聚類結果ASIFT局部特征描寫敘述得到的結果比SIFT方法在各項指標上都高10%以上。

由此可見。ASIFT比SIFT對自然場景下的文本區域圖像的局部特征描寫敘述更好更準確。這是由於SIFT僅僅是具有尺度和旋轉不變性。對於具有視角變化的相同文字卻無法得到匹配描寫敘述。而ASIFT不僅對圖像具有尺度旋轉不變性,還具有仿射不變性,這樣的特性對自然場景下的文本處理有更好的有用性。
具體的ASIFT與SIFT對照可見論文。


.


二、LSHForest/sklearn

LSHforest=LSH+隨機投影樹
在python的sklearn中有LSHForest能夠實現。

官方文檔在:sklearn.neighbors.LSHForest

1、主函數LSHForest

class sklearn.neighbors.LSHForest(n_estimators=10, radius=1.0, n_candidates=50, n_neighbors=5, min_hash_match=4, radius_cutoff_ratio=0.9, random_state=None)

隨機投影森林是近期鄰搜索方法的一種替代方法。


LSH森林數據結構使用已排序數組、二進制搜索和32位固定長度的哈希表達。

隨機投影計算距離是使用近似余弦距離。

n_estimators : int (default = 10)
樹的數量
min_hash_match : int (default = 4)
最小哈希搜索長度/個數。小於則停止
n_candidates : int (default = 10)
每一顆樹評估數量的最小值。反正至少每棵樹要評估幾次,雨露均沾
n_neighbors : int (default = 5)
檢索時。最小近鄰個數,就怕你忘記忘了設置檢索數量了 
radius : float, optinal (default = 1.0)
檢索時。近鄰個體的距離半徑
radius_cutoff_ratio : float, optional (default = 0.9)
檢索時,半徑的下限,相當於類似性概率小於某閾值時,停止搜索,或者最小哈希搜索長度小於4也停止
random_state : int, RandomState instance or None, optional (default=None)
隨機數生成器使用種子。默認沒有

附帶屬性:

hash_functions_ : list of GaussianRandomProjectionHash objects
哈希函數g(p,x),每一個樣本一個哈希化內容

trees_ : array, shape (n_estimators, n_samples)
Each tree (corresponding to a hash function) 
每棵樹相應一個哈希散列。且這個哈希散列是經過排序的。顯示的是哈希值。

n_estimators棵樹。n_samples個散列。

original_indices_ : array, shape (n_estimators, n_samples) 每棵樹相應一個哈希散列,哈希散列是經過排序的。顯示的是原數據序號index.

trees_ 和original_indices_ 就是兩種狀態,trees_ 是每棵經過排序樹的散列,original_indices_ 是每棵經過排序樹的序號Index.
.

2、LSHForeast相關函數

  • fit(X[, y])

Fit the LSH forest on the data.
數據加載投影樹

  • get_params([deep])

Get parameters for this estimator.
獲取樹裏面的相關參數

  • kneighbors(X, n_neighbors=None, return_distance=True)

檢索函數,n_neighbors代表所需近鄰數。 不設置的話則返回初始化設置的數量。return_distance,是否打印/返回特定cos距離的樣本。
返回兩個array。一個是距離array。一個是概率array

  • kneighbors_graph([X, n_neighbors, mode])

Computes the (weighted) graph of k-Neighbors for points in X
數量檢索圖,n_neighbors代表所需近鄰數, 不設置的話則返回初始化設置的數量,mode=’connectivity’默認

  • partial_fit(X[, y])

加入數據到樹裏面,最好是批量導入。

  • radius_neighbors(X[, radius, return_distance])

Finds the neighbors within a given radius of a point or points.
半徑檢索。在給定的區間半徑內尋找近鄰,radius為半徑長度。return_distance代表是否打印出內容。

  • radius_neighbors_graph([X, radius, mode])

Computes the (weighted) graph of Neighbors for points in X
半徑檢索圖

  • set_params(**params)

Set the parameters of this estimator.
重設部分參數
.

3、案例一則

>>> from sklearn.neighbors import LSHForest


>>> X_train = [[5, 5, 2], [21, 5, 5], [1, 1, 1], [8, 9, 1], [6, 10, 2]]
>>> X_test = [[9, 1, 6], [3, 1, 10], [7, 10, 3]]

>>> lshf = LSHForest(random_state=42)
>>> lshf.fit(X_train)  
LSHForest(min_hash_match=4, n_candidates=50, n_estimators=10,
          n_neighbors=5, radius=1.0, radius_cutoff_ratio=0.9,
          random_state=42)

>>> distances, indices = lshf.kneighbors(X_test, n_neighbors=2)

>>> distances                                        
array([[ 0.069...,  0.149...],
       [ 0.229...,  0.481...],
       [ 0.004...,  0.014...]])

>>> indices
array([[1, 2],
       [2, 0],
       [4, 0]])

LSHForest(random_state=42)樹的初始化,
lshf.fit(X_train)開始把數據加載初始化的樹;
lshf.kneighbors(X_test, n_neighbors=2)。找出X_test每一個元素的前2個(n_neighbors)類似內容。


當中。這個是cos距離,不是類似性,假設要直觀,能夠被1減。


.

4、案例二則

來源於:用docsim/doc2vec/LSH比較兩個文檔之間的類似度

# 使用lsh來處理  
tfidf_vectorizer = TfidfVectorizer(min_df=3, max_features=None, ngram_range=(1, 2), use_idf=1, smooth_idf=1,sublinear_tf=1)  
train_documents = []  
for item_text in raw_documents:  
    item_str = util_words_cut.get_class_words_with_space(item_text)  
    train_documents.append(item_str)  
x_train = tfidf_vectorizer.fit_transform(train_documents)  

test_data_1 = ‘你好。我想問一下我想離婚他不想離,孩子他說不要,是六個月就自己主動生效離婚‘  
test_cut_raw_1 = util_words_cut.get_class_words_with_space(test_data_1)  
x_test = tfidf_vectorizer.transform([test_cut_raw_1])  

lshf = LSHForest(random_state=42)  
lshf.fit(x_train.toarray())  

distances, indices = lshf.kneighbors(x_test.toarray(), n_neighbors=3)  
print(distances)  
print(indices)  

一般lsh比較適合做短文本的比較

.


相關拓展:

相關屬性獲得

# 屬性
lshf.trees_ 
# 每棵樹,排序散列的哈希值
lshf.hash_functions_ 
# 每棵樹的hash公式
lshf.original_indices_
# 每棵樹,排序散列的序號index

近期鄰檢索的圖:kneighbors_graph

lshf.kneighbors_graph(X_test, n_neighbors=5, mode=‘connectivity‘)

新增數據到樹裏面:

partial_fit(X_test)

LSH︱python實現局部敏感隨機投影森林——LSHForest/sklearn(一)