局部敏感哈希 LSH

来源:互联网 发布:python重构是什么意思 编辑:程序博客网 时间:2024/05/21 08:23

引言

局部敏感哈希(Locality-Sensitive Hashing, LSH)是用来解决高维检索问题的算法。想象一下,现在有数量庞大的数据点,每个点的维度可能几千或几万,给定一个点p,在这数据点集中寻找到可p最近的点或者最近的k个点。思路很清晰,我们必须要计算p到每个点的距离,根据计算结果排序,选择最近的点或者前k个点,距离可以用L1或L2泛数计算。这样的线性搜索,时间复杂度极高,效率地下。LSH就是为解决这类问题量身打造的,本文主要总结LSH作者论文,该网站上有大量LSH极其改进算法的paper链接以及实现代码。

算法思想及细节

由于前面提到的问题,直接线性计算Query Point和数据集中每个点的距离,时间复杂度高。自然而然的会想到对数据集进行降维,如PCA。但如果是以十万计或者更高维度,将维后依然会较高维度,如果数据集庞大,那线性计算也一点不简单。LSH作者从以下几个方面进行了思考:

  • 既然原空间计算难度大,能否找到另外一个空间,而在这个空间中计算简便?
  • 在进行检索的时候是不是一定要线性的计算所有数据集的距离?

针对这两点作者进行了思考尝试:

  • 首先,我们习惯了以欧式距离度量点的距离,而欧式距离涉及平方和开方运算,维度大时,必然会导致算法的低效。相比于欧式距离的计算,Hamming空间的距离计算就容易得多。
  • 其次,如果我们能在检索的时候只检索很少部分的数据集,那算法效率将会提高很多。而筛选点这个工作完全可以在空间变换的时候进行,我们只需要讲数据集中相似的点放在一起,在最后对Query Point进行检索的时候,只需要对那一群看起来相似的点进行计算就好了嘛!

空间转换

针对第一个问题,大牛想到的是进行空间转换,但这个空间不是瞎转的,得保证在原空间相近的点,在转换后依然相近,同样,之前距离较远的点,转换后依然较远。同时考虑到用Hamming距离代替欧式距离的计算。Hamming距离的计算公式是:

H(p1,p2)=i=0n|pi1pi2|

也就是相应维度上差的绝对值之和,像一种特殊情况,如果p1p2的各维度上不是1就是0,那么就相当于是相应位置上值不相同的个数。这便是针对第一步做的事情,讲原空间所有的点集坐标向量转换成二值向量。具体转换过程如下:

  • 计算出点集中坐标值最大的值C
  • 对数据集中每一个点p(x1,x2...xn)的每一个维度xi变换成一个长度为C的向量,向量的前xi个元素为1,其余为0
  • n个长度为C的向量链接成一个长度为Cn的向量

这样,就把数据集中的每一个点转换成了二值向量。在转换过程中,我们还需要进行一件事情,那就是对数据集中的点进行聚类,把相似的点放在一起,这里我们采用Hash的思想对数据进行分类。

哈希分类

定义一族哈希函数H,hH,且hi(p)=pi,即输出01序列的第i维的值,所以,H中之多有Cn个哈希函数。从H中挑选出j个哈希函数、k个桶,那个么对每一个点映射到不同桶的哈希值为:

gk(p)=(h1(p)...hj(p))
映射后,所有点就分配到不同的桶中了,如果一个桶的size满了,可以重新分配桶的空间,或者采用链接的方式链接在桶的后面,论文中,如果一个桶满了,那么就不放进桶中,因为有可能其他的哈希函数会讲这些点映射到其它的桶中。但这样还是会存在一个问题,Cn维的向量直接采用这种隐射的话会需要大量的桶,所以文中采用两层Hash,在上面的基础上得到了
V=(v1,v2...vj)
这时候采用普通Hash进行再次隐射,便能够解决这一问题。
h(V)=(a1v1+a2v2+...+ajvj)ModM
其中,a=(a1,a2...aj)[0,M]的随机数。文中讲述了M的取值。
M=αnB
n是数据集的数量,B是桶的最大Size,α是一个和内存利用率有关的参数。

检索

对Query Point(qp)进行检索时,我们需要对qp进行以上一样的操作,会隐射到j个点,然后计算qp与这j点的距离就能得出最近的点。

一些改进

通过LSH hash functions我们能够得到一个或多个hash table,每个桶内的数据之间是近邻的可能性很大。我们希望原本相邻的数据经过LSH hash后,都能够落入到相同的桶内,而不相邻的数据经过LSH hash后,都能够落入到不同的桶中。如果相邻的数据被投影到了不同的桶内,我们称为false negtive;如果不相邻的数据被投影到了相同的桶内,我们称为false positive。因此,我们在使用LSH中,我们希望能够尽量降低false negtive rate和false positive rate。

通常,为了能够增强LSH,即使得false negtive rate和/或false positive rate降低,我们有两个途径来实现:

  • 在一个hash table内使用更多的LSH hash function;
  • 建立多个hash table。

下面介绍一些常用的增强LSH的方法:

使用多个独立的Hash table

每个hash table由k个LSH hash function创建,每次选用k个LSH hash function(同属于一个LSH function family)就得到了一个hash table,重复多次,即可创建多个hash table。多个hash table的好处在于能够降低false positive rate。

AND 与操作

从同一个LSH function family中挑选出k个LSH function,H(X)=H(Y)有且仅当这kHi(X)=Hi(Y)都满足。也就是说只有当两个数据的这k个hash值都对应相同时,才会被投影到相同的桶内,只要有一个不满足就不会被投影到同一个桶内。AND与操作能够使得找到近邻数据的p1概率保持高概率的同时降低p2概率,即降低了false negtiverate。

OR 或操作

从同一个LSH function family中挑选出k个LSH function,H(X)=H(Y)有且仅当存在一个以上的Hi(X)=Hi(Y)。也就是说只要两个数据的这k个hash值中有一对以上相同时,就会被投影到相同的桶内,只有当这k个hash值都不相同时才不被投影到同一个桶内。OR或操作能够使得找到近邻数据的p1概率变的更大(越接近1)的同时保持p2概率较小,即降低了false positive rate。

AND和OR的级联

将与操作和或操作级联在一起,产生更多的hahs table,这样的好处在于能够使得p1更接近1,而p2更接近0。

除了上面介绍的增强LSH的方法外,有时候我们希望将多个LSH hash function得到的hash值组合起来,在此基础上得到新的hash值,这样做的好处在于减少了存储hash table的空间。下面介绍一些常用方法:

求模运算

newhashvalue=oldhashvalue

随机投影

假设通过k个LSH hash function得到了kh_1, h_2…, h_k$。那么新的hash值采用如下公式求得:

newhashvalue=h1r1+h2r2+...+hkrk
其中r1,r2,...,rk是一些随机数。

XOR异或

假设通过k个LSH hash function得到了k个hash值:h1,h2...,hk。那么新的hash值采用如下公式求得:

newhashvalue=h1 XOR h2 XOR h3...XORhk

参考文献

[1] http://web.mit.edu/andoni/www/LSH/index.html
[2] http://blog.csdn.net/icvpr/article/details/12342159
[3] http://lib.csdn.net/article/machinelearning/47115