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

關於局部敏感哈希算法。以前用R語言實現過,但是因爲在R中效能過低。因而放棄用LSH來作相似性檢索。學了python發現很是多模塊都能實現,而且經過隨機投影森林讓查詢數據更快。以爲可以試試大規模應用在數據相似性檢索+去重的場景。css

私以爲,文本的相似性可以分爲兩類:一類是機械相似性;一類是語義相似性。
機械相似性表明着,兩個文本內容上的相關程度。比方「你好嗎」和「你好」的相似性。純粹表明着內容上字符是否全然共現,應用場景在:文章去重;
語義相似性表明着,兩個文本語義上的相似程度。比方「蘋果」和「公司」的相似性。本篇不作這一討論html

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

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


1、隨機投影森林

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

一、隨機投影森林理論與實現僞代碼

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

​創建一棵隨機投影樹的過程大體例如如下(以二維空間爲例):markdown

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

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


注意一點。圖中不帶箭頭的直線是用於劃分左右子樹的根據,帶箭頭的向量是用於計算點積的。這樣,原有的點就劃分爲了兩部分,圖比例如如下:
這裏寫圖片描寫敘述
但是此時一個劃分結果內的點的數目仍是比較多。所以繼續劃分。再次隨機選取一個向量。與該向量垂直的直線將所有點進行了劃分。圖比例如如下:
這裏寫圖片描寫敘述
注意一點,此時的劃分是在上一次劃分的基礎上進行的。app


​也就是說現在圖中的點已經被劃分紅了四部分,相應於一棵深度爲2。有四個葉節點的樹。dom

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

注意這棵樹並不是全然樹。

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


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

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


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


.

二、相應拓展

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

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

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


有點相似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對照可見論文。


.


2、LSHForest/sklearn

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

官方文檔在:sklearn.neighbors.LSHForest

一、主函數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.
.

二、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.
重設部分參數
.

三、案例一則

>>> 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減。


.

四、案例二則

來源於:用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)
相關文章
相關標籤/搜索