1. 程式人生 > >區域性敏感雜湊matlab程式碼解讀

區域性敏感雜湊matlab程式碼解讀

個人總結:這篇文章介紹了局部敏感雜湊演算法,區域性敏感雜湊是非監督的雜湊演算法。
演算法的輸入是實數域的特徵向量,輸出為一個binary vector。
利用雜湊函式將資料點對映到不同的桶中是一種保形對映,使得資料點 i 和資料點 j 在原始空間的相似度 s 與對映後的在同一個桶的概率呈現正相關。之所以這麼做,主要是避免exhausted search. 如果理想狀態,每個桶中的元素數目大致相同,那麼查詢時的運算量將從原來的資料樣本數目m個降低到m/k個,其中k為桶的數目。
由於輸出是二值向量,設其長度為L,每個雜湊值其實對應著一個桶,理想情況下每個桶中都有資料,k=2L
從原理上來說,程式碼實現是很簡單的,matlab的版本的程式碼可見

http://ttic.uchicago.edu/~gregory/download.html
這其實是一個比較完整的工具包

本文主要做關鍵部分的程式碼解析。

入口函式lsh

T1=lsh('lsh',20,24,size(patches,1),patches,'range',255);

第一個引數是使用的演算法的型別,包括兩種型別,分別是lsh和e2lsh
生成一個range的引數,得到的[0 0 ,…0; 255 255 ,….,255]這樣的形式

range = processRange(d,range);

這個函式是用來產生lsh函式的。

Is = lshfunc(type
,l,k,d,varargin{:});

l表示函式的個數,k表示一個函式中的位數,d表示資料的維度。

   for j=1:l
     % select random dimensions
     I(j).d = include(unidrnd(length(include),1,k)); % 均勻分佈的,隨機選中k維
     % for each dimension select a threshold
     % hash key = [[ x(:,d)' >= t ]]
     t = unifrnd(0,1,1,k).*(range(2,I(j).d)-range(1
,I(j).d)); %每一維都隨機選中一個閾值位於0~255之間 I(j).t = range(1,I(j).d)+t; I(j).k = k; end

這裡hash函式就是一個簡單 閾值函式,將原始的400維的資料,隨機選出k=24維,變為0到1,後文會有進一步說明。l為總共生成的雜湊函式的數目,這裡取值為20。
產生Is的變數的內容如下:
這裡寫圖片描述
d是選擇的維度下標,t是維度的閾值。

T = lshprep(type,Is,b);

T這個變數儲存了雜湊查詢雜湊值以及索引資訊。

  T(j).type = type;
  T(j).Args = varargin;
  T(j).I = Is(j);
  T(j).B = B;
  T(j).count = 0;
  T(j).buckets = [];
  % prepare T's table
  T(j).Index = {};
  T(j).verbose=1;

  % set up secondary hash table for buckets
  % max. index can be obtained by running lshhash on max. bucket
  T(j).bhash = cell(lshhash(ones(1,k)*255),1); % lshhash是一個計算hash值的函式,將24維的二值向量對映為一個雜湊值

隨後的函式,將資料放入桶中,對T中變數進行賦值。

  T = lshins(T,x,ind);

這個函式中有一些關鍵的處理,其中

  buck = findbucket(T(j).type,x,T(j).I);%這是一個將資料轉化為二值向量的函式

它裡面的主要採用了矩陣的比較,本質上就是用剛才生成的閾值函式做了一個二值化。
其中v是一個59500*24維的二值矩陣,每一行表示一個數據樣本。

 v = x(I.d,:)' <= repmat(I.t,size(x,2),1);
 v = uint8(v+128);

但注意,輸出的d維二值向量每一維並不是[0, 1],而在區間[128 129],這可能是要用於後文二次雜湊的計算方便。為了後文方便說明,我們用雜湊向量來簡稱這個二值向量。

這裡一個桶buck對應著一個雜湊向量,但是桶的數目非常多,直接來進行比較是很費時間的。

  [uniqBuck,ib,bID] = unique(buck,'rows');
  keys = lshhash(uniqBuck);%返回每個桶的雜湊key

例如,對j=1這個雜湊函式而言,總共有14615個不同的桶(新分配空間為14615*24),如果要查詢一個桶就需要14615次比較非常費時。作者的優化方案是進行二次雜湊,讓多個雜湊向量對映為一個整型的hash-key值,用lshhash函式完成此功能。

  % allocate space for new buckets -- possibly excessive
  T(j).buckets=[T(j).buckets; zeros(length(ib),T(j).I.k,'uint8')];

對每一個單獨的雜湊key值ib(b)

    % find which data go to bucket uniqBuck(b)
    thisBucket = find(bID==bID(ib(b)));

    % find out if this bucket already has anything
    % first, which bucket is it? 該hash函式T(j)下的,對應於雜湊key值keys(b)的桶是否已經存在
    ihash = T(j).bhash{keys(b)}; % possible matching buckets
    if (isempty(ihash)) % nothing matches
      isb = [];
    else % may or may not match
      isb = ihash(find(all(bsxfun(@eq,uniqBuck(b,:),T(j).buckets(ihash,:)),2)));
    end

其中

      isb = ihash(find(all(bsxfun(@eq,uniqBuck(b,:),T(j).buckets(ihash,:)),2)));

是一種非常有效的寫法,bsxfun(@eq ,a,b)這種形式會得到兩個向量之間的逐位比較,它matlab內部的實現是通過迴圈來實現的。通過all在水平方向上進行判別,就相當於比較兩個向量是否相等。這一步是比較在T(j).bhash中存放的雜湊向量中是否已經存在當前的獲得的雜湊向量,即是否已經記錄了當前的桶,這樣我們就可以分情況討論是往這個桶裡新增新的資料,還是要先建立一個桶再新增新的資料。

  if (~isempty(isb)) % 如果isb不為空,那麼即該bucket已經存在
      % adding to an existing bucket.
      oldcount=length(T(j).Index{isb}); % # elements in the bucket prior
                                        % to addition 新增前桶中元素的數目,主要是方便統計
      newIndex = [T(j).Index{isb}  ind(thisBucket)];
    else
      % creating new bucket
      newBuckets=newBuckets+1;
      oldcount=0;
      isb = oldBuckets+newBuckets;
      T(j).buckets(isb,:)=uniqBuck(b,:);%為什麼用128 129表示
      T(j).bhash{keys(b)} = [T(j).bhash{keys(b)}; isb];%根據hash-key值來對映桶序號
      newIndex = ind(thisBucket);%該桶中存放的元素的下標
    end

隨後完成資訊的更新

    % if there is a bound on bucket capacity, and the bucket is full,
    % keep a random subset of B elements (note: we do this rather than
    % simply skip the new elements since that could introduce bias
    % towards older elements.)
    % There is still a bias since older elements have more chances to get
    % thrown out.
    if (length(newIndex) > T(j).B)
      rp=randperm(length(newIndex));
      newIndex = newIndex(rp(1:T(j).B));% 如果超過的了桶的容量限制,那麼隨機選定T(j).B個數據
    end
    % ready to put this into the table
    T(j).Index{isb}= newIndex;%重新為屬於該桶的資料下標賦值
    % update distinct element count
    T(j).count = T(j).count + length(newIndex)-oldcount;
    %新數目減去老數目為改變數,注意如果以前桶中有元素,是通過追加的方式新增上去的,在追加後再與T(j).B進行比較。作者這麼做,就是為了保證桶中元素不會因為滿了而傾向於保持老元素,新元素就加不進去了,所以先追加後然後再隨機選擇指定數目保留下來。當然這樣做還是會造成桶中舊的元素更容易被扔掉這一情形。

執行分析

執行lsh函式會得到:

Table 5 adding 13852 buckets (now 13852)
Table 5: 59500 elements
12619 distinct buckets
Table 6 adding 12619 buckets (now 12619)
Table 6: 59500 elements
11936 distinct buckets
Table 7 adding 11936 buckets (now 11936)
Table 7: 59500 elements
15997 distinct buckets

引數檢視 lshstats

examine statistics of LSH data structure

[mi,ma,me]=lshstats(T,B,xref,xtst,minNN)

例如;

lshstats(T1(1:5),'test',patches,patches(:,1:1000),2);

輸出為
Table 1: 59500 in 13404 bkts, med 1, max 4288, avg 813.19
Table 2: 59500 in 12661 bkts, med 1, max 2646, avg 544.55
Table 3: 59500 in 16147 bkts, med 1, max 4057, avg 751.01
Table 4: 59500 in 11627 bkts, med 1, max 4989, avg 864.60
Table 5: 59500 in 13630 bkts, med 1, max 3528, avg 601.55

這表示table1有13404 個桶,平均容量是每個桶1個數據,最大容量為4288,期望容量為813.19

Running test…10% 20% 30% 40% 50% 60% 70% 80% 90% 100%
# of comparisons: mean 980.14, max 8122, failures: 54

這裡使用了5個雜湊函式,它的含義是對前1000個樣本進行查詢,平均每次查詢需要比較980個樣本,但是同時失敗次數為54次

如果增加雜湊函式的數目,會得到不同的結果,根據參考文獻中的分析,如果增加雜湊函式的數目,那麼會需要更長的查詢時間,但是同時recall將會增加,例如這裡我們用全部的20個雜湊函式來做實驗。

 lshstats(T1,'test',patches,patches(:,1:1000),2);

得到結果
Running test…10% 20% 30% 40% 50% 60% 70% 80% 90% 100%
# of comparisons: mean 2957.24, max 13120, failures: 2
可以發現平均查詢所需的時間變長了,但是recall相應的變高的(幾乎沒有錯誤)。

lshlookup

下面是查詢第50個樣本,在這之前,首先增加二值向量的長度,即引用文獻中的b的長度,這會減少平均每個桶中的元素數目

lshstats(T2(1:10),'test',patches,patches(:,1:1000),2);

Table 1: 59500 in 33066 bkts, med 1, max 1829, avg 146.51
Table 2: 59500 in 34018 bkts, med 1, max 1638, avg 160.95
Table 3: 59500 in 34077 bkts, med 1, max 1386, avg 156.09
Table 4: 59500 in 35716 bkts, med 1, max 2813, avg 210.50
Table 5: 59500 in 34492 bkts, med 1, max 1470, avg 194.75
Table 6: 59500 in 34659 bkts, med 1, max 1543, avg 156.86
Table 7: 59500 in 33033 bkts, med 1, max 1232, avg 146.30
Table 8: 59500 in 33923 bkts, med 1, max 1955, avg 152.32
Table 9: 59500 in 34032 bkts, med 1, max 1718, avg 176.25
Table 10: 59500 in 32402 bkts, med 1, max 2862, avg 226.41

注意avg變小了

tic; [nnlsh,numcand]=lshlookup(patches(:,50),patches,T2,'k',11,'distfun','lpnorm','distargs',{1});toc

演算法執行結果結果實現檢索一個數據所需的時間:

時間已過 0.030697 秒。

下面來解析這個函式的實現
需要完成的任務是找到所有match這個query的tables。
步驟1 用雜湊函式T(j)獲取查詢x0的對映的50維(維度為雜湊函式中隨機選定的位數的長度,即b)二值向量,由於加了128,所以範圍是在[128,129]。

  buck = findbucket(T(j).type,x0,T(j).I); 

步驟2 將該向量轉化成雜湊key,這一步不是一一對映,而是多對一的對映,主要目的是為了提升向量的檢索速度。

 key = lshhash(buck);

步驟3 根據雜湊key值獲取所有的雜湊向量,一個雜湊key值對應著多個bucket

 ihash = T(j).bhash{key}; % possible matching buckets

步驟4 進一步查詢到該雜湊向量,即找到對應的桶

 if (~isempty(ihash)) % nothing matches
    b = ihash(find(all(bsxfun(@eq,buck,T(j).buckets(ihash,:)),2)));
    if (~isempty(b))
      iNN = [iNN T(j).Index{b}]; %把該桶中的資料union起來,因為不同的雜湊函式會有不同的結果
    end
  end

步驟5
去除重複資料

[iNN,iu]=unique(iNN);
cand = length(iNN);

步驟6
這一步主要是將相似列表中的資料做個排序返回。用於CBIR檢索很合適。

if (~isempty(iNN))

  if (strcmp(sel,'best'))

    D=feval(distfun,x0,Xsel(x,iNN),distargs{:});% 即比較這些桶中的最近鄰資料和query的距離
    [dist,sortind]=sort(D);
    ind = find(dist(1:min(k,length(dist)))<=r);%返回小於指定距離的下標,基於iNN
    iNN=iNN(sortind(ind));% 返回相似資料,這就完成了檢索

  else % random

    rp=randperm(cand);
    choose=[];
    for i=1:length(rp)
      d = feval(distfun,x0,Xsel(x,iNN(rp(i))),distargs{:});
      if (d <= r) 
    choose = [choose iNN(rp(i))];
    if (length(choose) == k)
      break;
    end
      end
    end
    iNN = choose;
  end

end

相關推薦

區域性敏感matlab程式碼解讀

個人總結:這篇文章介紹了局部敏感雜湊演算法,區域性敏感雜湊是非監督的雜湊演算法。 演算法的輸入是實數域的特徵向量,輸出為一個binary vector。 利用雜湊函式將資料點對映到不同的桶中是一種保形對映,使得資料點 i 和資料點 j 在原始空間的相似度

【機器學習】使用Python中的區域性敏感(LSH)構建推薦引擎

學習如何使用LSH在Python中構建推薦引擎; 一種可以處理數十億行的演算法 你會學到: 在本教程結束時,讀者可以學習如何: 通過建立帶狀皰疹來檢查和準備LSH的資料 選擇LSH的引數 為LSH建立Minhash 使用LSH Query推薦會議論文 使用LSH

LSH Locality-Sensitive Hashing 區域性敏感演算法總結

http://www.cppblog.com/Files/humanchao/LSH(Locality%20Sensitive%20Hashing).zip參考文獻:Website:Paper:[1] Approximate nearest neighbor: towards removing the cur

[Algorithm] 區域性敏感演算法(Locality Sensitive Hashing)

  然後看第一列的第一個是1的行是第幾行,是第2行,同理再看二三四列,分別是1,2,1,因此這四列(四個document)在這個置換下,被雜湊成了2,1,2,1,就是右圖中的藍色部分,也就相當於每個document現在是1維。再通過另外兩個置換然後再hash,又得到右邊的另外兩行,於是最終結果是每個docum

區域性敏感演算法

3、計算相似性。使得兩個不一樣的bands被雜湊到不同的bucket中,這樣一來就有:如果兩個document的bands中,至少有一個share了同一個bucket,那這兩個document就是candidate pair,也就是很有可能是相似的。(找相似:同一個籃子裡面的就是有可能相似的樣本框;如果兩個籃

LSH︱python實現區域性敏感——LSHash(二)

關於區域性敏感雜湊演算法,之前用R語言實現過,但是由於在R中效能太低,於是放棄用LSH來做相似性檢索。學了Python發現很多模組都能實現,而且通過隨機投影森林讓查詢資料更快,覺得可以試試大規模應用在資料相似性檢索+去重的場景。 私認為,文字的相似性可以

區域性敏感(原始LSH)python實現

最近短期計劃是學習一下Python,最好的學習方式當然是實踐了,今天用Python實現了下lsh演算法,程式碼比較簡陋。。。(2016.1.17) origionalLSH.py: import random class Bucket: feature

最近鄰和K近鄰及其優化演算法LSH(區域性敏感,Locality Sensitive Hashing) Kd-Tree

引言 在處理大量高維資料時,如何快速地找到最相似的資料是一個比較難的問題。如果是低維的小量資料,線性查詢(Linear Search)就可以解決,但面對海量的高維資料集如果採用線性查詢將會非常耗時。因此,為了解決該問題通常採用些優化演算法。稱之為近似最近鄰查詢

區域性敏感(Locality Sensitive Hashing)和MinHash介紹與例項

在實際應用中,我們所面對的資料是海量的,並且有著很高的維度。在對資料的各種操作中,查詢操作是最常見的一種,這裡的查詢是指輸入一個數據,查詢與其相似的資料,那麼怎樣快速地從海量高維資料中,找到與某個資料最相似的資料,成為了一個難點和問題。 低維的小資料集,可通過線性查詢來解決

LSHash(區域性敏感)

原載:http://cool.sinaapp.com 作者:junGle 原文網址:http://1.cool.sinaapp.com/?p=911 前文所說的minhash,比較的是jaccard相似度,基於兩兩來比較的話,如果文件數很大,比如:100w文件,根據排

區域性敏感Locality Sensitive Hashing歸總

說到Hash,大家都很熟悉,是一種典型的Key-Value結構,最常見的演算法莫過於MD5。其設計思想是使Key集合中的任意關鍵字能夠儘可能均勻的變換到Value空間中,不同的Key對應不同的Value,即使Key值只有輕微變化,Value值也會發生很大地變化。這樣特性

LSH(區域性敏感演算法)實現文字的相似性比對

# @Time : 2017/10/19 10:09 # @Author : Jalin Hu # @File : main.py # @Software: PyCharm import os import jieba import collections import random from

區域性敏感演算法的實現

近來由於工作需要,需要將字串的相似度的計算速度進行提升。之前曾採用最長公共子序列、編輯距離等演算法實現過,但總滿足不了實時比較的效能及速度需求。前些天由同事推薦區域性敏感雜湊演算法,便嘗試了一把,結果發現速度還不錯,本著記錄與分享的精神,簡單總結下實現的過程及思路。 【Sh

從NLP任務中文字向量的降維問題,引出LSH(Locality Sensitive Hash 區域性敏感)演算法及其思想的討論

1. 引言 - 近似近鄰搜尋被提出所在的時代背景和挑戰 0x1:從NN(Neighbor Search)說起 ANN的前身技術是NN(Neighbor Search),簡單地說,最近鄰檢索就是根據資料的相似性,從資料集中尋找與目標資料最相似的專案,而這種相似性通常會被量化到空間上資料之間的距離,例如歐幾里

區域性敏感LSH(Locality-Sensitive Hashing)——海量資料相似性查詢技術

一、 前言     最近在工作中需要對海量資料進行相似性查詢,即對微博全量使用者進行關注相似度計算,計算得到每個使用者關注相似度最高的TOP-N個使用者,首先想到的是利用簡單的協同過濾,先定義相似性度量(cos,Pearson,Jaccard),然後利用通過兩兩計算相似度,計算top-n進行篩選,這種方法的時

學習程式碼

雜湊方法 公佈程式碼的: AGH: Hashing with Graphs [Paper] [Code]BPBC: Learning Binary Codes for High-Dimensional Data Using Bilinear Projections [P

LSH 位置敏感演算法

前言 LSH 用於近似查詢,聚類分類,壓縮等領域。 漢明距離 漢明距離是以理查德·衛斯里·漢明的名字命名的。在資訊理論中,兩個等長字串之間的漢明距離是兩個字串對應位置的不同字元的個數。換句話說,它就是將一個字串變換成另外一個字串所需要替換的字元個數。例如:10111

幾種常見函式程式碼實現

幾種常見雜湊函式的C程式碼實現 雜湊演算法將任意長度的二進位制值對映為較短的固定長度的二進位制值,這個小的二進位制值稱為雜湊值。雜湊值是一段資料唯一且極其緊湊的數值表示形式。如果雜湊一段明文而且哪怕只更改該段落的一個字母,隨後的雜湊都將產生不同的值。要找

PostgreSQL 11 新特性解讀:支援並行連線(Parallel Hash Joins)"

PostgreSQL 11 版本在並行方面得到增強,例如支援並行建立索引(Parallel Index Build)、並行雜湊連線(Parallel Hash Join)、並行 CREATE TABLE .. AS等,上篇部落格介紹了並行建立索引,本文介紹並行 Hash Join。 測試環境準備 建立大表

https是如何加密的 (知道了原理之後,希望自己能用程式碼實現一下,還有用於對個人資訊和公鑰進行加密的演算法,有時間也去查一下)

由於http協議是明文傳輸資料,資料的安全性沒有保障。為了改進這種明文傳輸協議,https誕生了。   https是在應用層和傳輸層之間,增加了一層ssl加密。對於加密,請往下看:   1、對稱加密   每次在傳送資料之前,伺服器先生成一把金鑰,