位置敏感散列(LSH)是一组技术,可大大加速邻居搜索或近复制检测数据。例如,可以使用这些技术以令人印象深刻的速度过滤刮出刮网页的重复,或者从地理空间数据集执行附近点的近常数时间查找。
让我们快速看看其他类型的哈希函数,以获得作为哈希函数的鸟瞰图,以及LSH如何适合该世界。传统用于哈希函数的用途是哈希表。作为提醒,散列表中使用的散列函数旨在将一条数据映射到整数,该整数可以用于在哈希表中查找特定存储桶以检索或删除该元素。许多包含字符串键的容器,例如JavaScript对象或Python词典,基于哈希表。虽然哈希表可能无法保证恒定时间查找,但实际上它们有效地提供了它们。
还有其他类别的哈希功能。例如,SHA-1加密散列函数难以反转,如果您希望将某人的密码作为散列值将某人的密码存储为有用。像这些这样的散列函数称为加密散列函数。
它们将某种类型的输入(例如字符串或浮点)映射到离散值,例如整数。
它们的设计使得两个输入将导致基于输入的关键属性的散列输出。
以下是LSH的适合:位置敏感散列函数专门设计,使得哈希值冲突更有可能靠近一起的两个输入值,而不是对于相距远的输入。正如不同用例的安全散列函数的不同实现一样,对于不同数据类型的LSH函数的不同实现以及用于靠近的不同定义。我将使用条款邻居或附近地将我们认为“足够近”的积分在一起,以至于我们要注意到他们的相似性。在这篇文章中,我将简要概述LSH背后的关键想法,并通过称为随机投影的想法来查看一个简单的示例,我将在下面的第2节中定义。
掌握LSH后面的主要想法可能会更容易,与您可以联系的示例。这样我们就可以在潜入那些我们在下一节中使用的那些随机投影之前建立一些直觉。
假设你有一百万人来自美国的所有人都站在一个巨大的房间里。让你的工作让人在一起靠近自己的群体。想象一下,走到每个人的时间,要求他们的街道地址,将其映射到拉特/长对,然后写代码以找到地理集群,然后再次走到每个人并告诉他们如何找到他们的其余部分。我只是思考时间复杂性。
以下是解决此问题的更好方法:在海报板上写下每个美国邮政编码,并将其中的邮政编码从天花板上悬挂。然后告诉大家在他们居住的邮政编码下站在邮政编码下。
瞧!这更容易,对吗?这也是LSH背后的主要想法。我们正在采取任意数据类型(一个人,我们可以将其视为包括他们的街道地址的吨数据),并将数据映射到一组离散值(邮政编码)中,使得可能靠近在一起的人哈希相同的价值。换句话说,群集(具有相同邮政编码的人)很可能是邻居的组。
邮政编码方法的一个很好的好处是它是并行友好的。而不是要求沟通中心,每个人都可以直接向目的地行走,而无需进一步协调。鉴于结果(邻居集群)完全基于输入之间的关系,这有点令人惊讶。
此示例的另一个属性是它是近似的:有些人可能会彼此住在街上,但恰好具有不同的邮政编码,在这种情况下,它们在此处不会聚集在一起。正如我们将在下面那样看到的,即使它们相距很远,数据点也可以聚集在一起,虽然精心设计的LSH至少可以给出一些数学证据,这将是一个罕见的事件,以及一些实现设法保证这永远不会发生。
在本节中,我将究竟解释一个相对简单的LSH方法的工作原理,探索该LSH系统的一些关键参数,并查看原因这项技术比其他一些方法更快的数量级。
让我们从一个令人难以置信的简单数学函数开始,我们可以作为LSH对待。点定义\(h_1:{\ mathbb {r}} ^ 2 \ to {\ mathbb {z}} \ point \(x =(x_1,x_2)\中{\ mathbb {r}} ^ 2 \)经过
也就是说,\(h_1(x)\)是其中\(a \ le x_1. \)的最大整数\(a \),例如,\(h_1((3.2,-1.2))= 3. \)
让我们假设我们通过用半径4统一地采样来统一采样,随机选择点:
\ [\ mathcal c:= \ {(x,y):x ^ 2 + y ^ 2 \ le 4 ^ 2 \}。 \
假设我们想找到\(\ Mathcal C \)中的哪些点在一起。我们可以通过考虑点\(a,b \ in \ mathcal c \)来估计这种关系,当\(h_1(a)= h_1(b)中聚集在一起。\)介绍介绍的表示法\(a \ SIM b \)表示\(a \)和\(b \)位于同一群集中。用那个表示法,我们可以写下我们当前的哈希设置
你可以立即看到一些点距离但是聚集在一起,而其他点则相对较近但未陷入困境。也有一种感觉,任意选择该特定哈希函数\(H_1 \)以聚焦在X轴上。如果我们使用的话,则发生了相同的数据?(H_2(x):= \ lfloor x_2 \ rfloor?\)结果是图3。
虽然两者群体都是惊人的,但如果我们同时使用它们都可以更好地工作。也就是说,我们可以通过重新定义我们的聚类
\ [a \ sim b \ iff \ begin {is} h_1(a)= h_1(b),\ text {} \\ h_2(a)= h_2(b)。 \\ \结束{案例} {\ class {smallscrneg} {}} \ qquad(1)\]
这使得避免与分数相距远远有所了解的更好的工作,虽然我们在下面看到,我们仍然可以改进。
到目前为止,我们已经确定了确定性散列函数。让我们通过选择随机旋转矩阵\(u \)以及随机偏移\(b \在[0,1)中的随机偏移\(b \)来改变它。给定这样一个随机\(u \)和\ (b,\)我们可以通过以下方式定义新的哈希函数
在哪里我使用的符号\((\ textit {vec})_1 \)来指示矢量值Vec的第一个坐标。 (即,符号\((UX)_1 \)表示向量\(UX \)的第一坐标。)此功能是我提到的随机投影。
它似乎只使用这里的第一个坐标而不是任何其他坐标,但是我们采用随机旋转的事实是指我们具有相同的可能性,具有相同的概率分布,正如我们当时拔出任何其他单个坐标值。
使用随机散列函数的一个关键优势是我们想要制作关于性能的任何概率陈述(例如,“99%这个算法将为我们提供正确的答案”)同样适用于所有数据,而不是应用于某些数据数据集但不是别人。作为对比,考虑Quicksort通常快速的方式,但是讽刺地使用\(O(n ^ 2)\)时间来排序预先排序的列表;这是一种性能取决于数据的情况,我们想避免这种情况。如果我们使用的是确定性哈希函数,那么有人可以为我们的哈希函数选择最坏情况的数据,我们将陷入困难的性能(例如,选择最大限度地分开的点仍然被我们的\仍集中在一起(h_1)上面的函数)。通过使用随机选择的散列函数,我们可以确保我们的散列函数的任何平均行为同样适用于所有数据。相同的透视对于以通用散列形式的哈希表而言。
让我们重新审视我们上面使用的示例点,但现在应用一些随机散列函数。在图4中,如果两个散列值(来自\(h_1(x)\)和\(h_2(x)\))碰撞,则群集点是群集的。我们将使用同样的想法,但这次选择四个旋转\(u_1,\ ldots,u_4 \)以及四个偏移\(b_1,\ ldots,b_4 \)来定义\(h_1(),\ ldots, H_4()\)通过
图5显示了结果的聚类。这一次,有100分,自从使用更多散列函数有效地使群集区域变小。我们需要更高的点密度来查看现在聚集在一起的点。
我们实际上想要使用我们所有四个哈希函数并不明显。问题是我们的集群变得非常小。有几种方法可以解决这个问题。一个是简单地增加散列函数的规模;例如,设置:
其中\(s \)是一个比例因子。在此设置中,较大的\(s \)值将导致更大的群集。
但是,我们可以看一些更细微的一些细微差别,这是为了允许我们所要求的哈希碰撞的一些适应性。换句话说,假设我们有\(k \)总哈希函数(刚刚上面,我们有\(k = 4 \))。在我们说两点在同一群集中之前,我们可以匹配所有\(k \ \ \)哈希值,而不是坚持全部\(k \)哈希值,我们可以查看其中的一些数字\(j \ le k \)匹配的情况。要在数学上说明这一点,我们将重写等式(1)
这里发生了有趣的事情,这是\(a \ sim b \)关系不再是群集,而是变得更像是在图中的邻接(即共享边缘)。不同之处在于,在群集中,如果\(a \ sim b \)和\(b \ sim c,\)那么我们也必须具有\(a \ sim c \);这被称为过境关闭。图表不需要拥有此属性。同样,我们的相似关系\(a \ sim b \)是过境关闭的。
它可能有助于您的直觉在图5中查看同一100点的\(a \ sim b \)的新定义。这次,在图6中,有20个随机哈希,我们看到了图表由等式(3)生成,使用截止值(\(j \)的值)为6,7,8和9.图6中的左上图具有在两个点(a \)之间绘制的边缘。 (b \)每当有至少6个哈希函数\(h_i()\)时,\(h_i(a)= h_i(b),\)出可能的20个使用的散列函数。
事实上,我们可以可视化所有可能的截止值6或更高 - 这些是等式(3)中的\(j \)的值 - 使用具有加权边缘的单个图像,如图7所示。记住我们避风港'T明确地计算了任何成对距离到达此数据。
有另一种有趣的方式来构建我们散列提供的信息的直觉。让我们来看看圆的区域,其中所有点与给定查询点具有相同数量的哈希冲突。我们可以通过显示一个示例查询点\(q \),并根据区域点具有\(q \)的哈希冲突的数量阴影每个区域;这如图8所示。每个阴影区域中的每个点都具有所用散列函数的所有散列值。图8的第一部分显示了双哈希系统的缩放版本(使用\(h_1()\)和\(h_2()\),类似于我们之前看到的图4);第二部分使用5个随机哈希。最黑暗的区域包含点\(p \),其中所有哈希值碰撞,所以\(h_i(p)= h_i(q)\)所有\(i \)。在一个轻微阴影的区域中,方程式将仅适用于散列函数的较小子集\(h_i()。\)
图8的第二部分(5个哈希)显示了更好的行为,我会尝试解释原因。想象一下,我们正在为某些理论上完美的LSH设置绘制相同的图像,以便以某种程度地设法将点\(Q \)匹配,其中一些点\(|| pq || \ le r \)匹配(r \);所有其他点都不匹配。对于该完美的LSH设置,像图8这样的图像将显示一个带有\(Q \)的固定大小的圆,与\(q \)一起移动。考虑到这一点是完美的LSH结果,请注意,图8中的第二部分比第一部分更接近这种理想。请记住,阴影区域内的查找不再通过数据线性搜索,而是\(k)哈希表查找的交点 - 即附近点的查找明显更快。
它可能进一步帮助您的直觉来了解加权的边缘如何如何与图7中的邻居连接到其邻居,作为单个查询点Memives的变化。这是图9背后的想法,其中加权边缘在移动查询点和100个随机点之间绘制。请注意,边缘加权做出直观的感觉:它们倾向于强烈地连接到非常靠近的邻居,弱到更远的邻居,并根本不超过一定距离。
到目前为止,我们已经看到,我们可以使用哈希查找来查找附近的查询点的邻居,并且使用\(k \)不同的随机散列函数使我们比我们使用单个哈希函数更准确的查找。图7和图9的有趣属性是我们使用不同数量的哈希冲突 - 通过变量\(j \) - 发现点之间的不同程度。在许多应用程序中,例如查找附近的重复或关闭地理空间点,我们只想要二进制输出,因此我们必须为\(j \)选择特定的值。让我们讨论如何选择良好的值\(j。\)
为了回答这个问题,让我们暂时考虑一个完美的功能会为我们做些什么。我们将调用此功能搜索(Q)。在理想的世界中,此函数返回查询点的固定距离内的所有点。我们可以在查询点\(q \)周围以\(n - \)尺寸球体可视化。呼叫搜索(q)应该返回在此领域内生活的所有索引点\(p \)。
让我们从那种理想化的算法转到我们的快速但近似的地方敏感哈希世界。通过这种方法,没有确切的截止距离,尽管我们保持了附近邻居在返回的列表中的财产,并且很可能被排除在返回的列表中。由于我们的哈希函数是随机的,我们可以考虑邻居关系\(p \ sim q \)作为一个随机变量,一旦我们的所有参数都是固定的(作为提醒,我们的主要)参数是\(j \)和\(k))。
现在考虑在这个随机变量的上下文中的表现很好。理想情况下,有一些距离\(d \)这样
\ [|| p-q || < D- \ varepsilon {\四{\ {类smallscr} {\ hskip -0.8em}}} \ RIGHTARROW {\四{\ {类smallscr} {\ hskip -0.8em}}} p(P \ SIM Q)&GT ; 1 - \ delta; \] \ [|| p-q || > d + varepsilon {\ quad {\ class {smallscr} {smallscr} {\ hskip -0.8em}}}}}} \ lightarrow {\ quad {\ class {smallscr} {\ hskip -0.8em}} p(p \ sim q)< \三角洲。\]
换句话说,这个距离\(d \)就像我们近似搜索功能的截止一样。返回比距离\(d \)更接近查询点\(q \),而较进一步的点不是。
我写了一个Python脚本来计算特定参数\(k = 10 \)的一些概率,\(d = 2 \)(\(d \)是点的维度的维度),以及各种值(j \)。特别是,我将输入点限制为某些距离,并测量它们至少具有不同\(j \)值的哈希冲突的概率。如果您熟悉有条件概率,则该值可以写为:
我写的那里(p \ sim_j q \)表示点\(p \)和\(q)至少具有\(j \)哈希冲突。
使用此Python脚本,我可以在图10中可视化\(p \ sim_j q \)的碰撞行为,如图10所示的\(j \)更详细地详细介绍盒子图上的每个刻度表明,但直觉是,较短的盒子图更好,因为在这个可视化中,较短的盒子曲线表明较小的不确定性范围。
该图最有趣的元素是\(j \)的最佳值似乎是\(j = 6. \)您可能猜到了您最好的LSH方法是坚持以前必须碰撞所有的随机哈希您认为两个要点是邻居,但这种测量表明直觉是假的。
那么我们在图10中究竟究竟究竟是什么?传统盒子绘图可视化一组标量数据点的第25和第75百分位数作为框的边界。中位数(50百分位数)也在框内显示,但我们不包括图10中的类似标记。在任一端的“晶须”可以指示最小值和最大值,或类似的东西,例如极值删除异常值后。
在我们的情况下,我们为\(j \)的每个值有一个盒子图,每个绘图都被归一化,以便底部晶须都以值为1对齐。(我将解释为什么在片刻中为什么这是有用的。)底部晶须表示\(p \)和\(q \)之间的距离,因此\(p \ sim_j q \)是真正的99%的时间。盒子的底部是\(p \ sim_j q \)的相对距离是真正的75%的时间。继续在这种模式中,盒顶对应于我们在25%的时间内获得冲突的距离,顶部晶须是我们在1%的时间内发生冲突的距离。由于盒子图全部归一化(意味着每个\(j \)值全部被最小的距离分开),因此很容易看出每个盒子绘图位置与其最小距离的比率。换句话说,很容易看到哪个距离范围最小。
因为我喜欢数学和精度,我将提供最后一个定义来形式化图10的想法。给定值\(s \ in(0,1)\),定义距离\(d_s \)作为满足给定等式的价值:
其中\(p \ sim_j q \)意味着\(\#\ {i:h_i(p)= h_i(q)\} \ ge \)直观地,如果\(\ varepsilon \)接近零,然后距离\(d_ \ varepsilon \)很大,因为\(p \ sim_j q \)的概率很小。 \(d_ {1/2} \)的值是完美的距离,以便\(p \ sim_j q \)发生一半的时间,\(d_ {1- \ varepsilon}}是一个小距离\(p \ sim_j q \)几乎一直发生。使用此定义,从底部到顶部的每个盒子图中显示的四个值,是:
\ [d _ {。99} / d _ {。99},{\; \; {\ class {smallscr} {\ hskip -0.3em}} d _ {。75} / d _ {。99},{\; ; {\ class {smallscr} {\ hskip -0.3em}} d _ {。25} / d _ {。99},{\; \; {\ class {smallscr} {\ hskip -0.3em}} d_ { .01} / d _ {。99}。\]
到目前为止,我们一直坚持二维数据,因为这更容易在文章中可视化。但是,如果您认为每二维点计算10个哈希值,以便找到邻居,可能觉得您可以通过您的点进行线性搜索的简单解决方案。让我们查看使用LSH的案例比查找附近积分的其他方法更有效。
LSH有两种方式可以加快速度:通过帮助您处理大量的积分,或通过帮助您处理诸如图像或视频数据的高维空间中的点。
如果您想找到靠近查询点的所有点\(q,\),您可以执行完整的线性搜索。单个哈希LSH可以在不断的时间内给您导致导致结果。这更快了。
如果您保留\(j = k,\),那么您的LSH结果是\(j = k,\)的\(k。\)值稍微复杂。然后,您的LSH结果是\(k)不同列表的简单交点列出作为给定随机散列函数\(h_i()。\)找到此交叉点返回的哈希碰撞点集
......