SQL高效的最近邻查询

时间:2009-04-06 09:25:41

标签: sql nearest-neighbor

我无法提出有效的SQL查询来处理以下情况:

假设我们有一个包含两列的表

groupId : int 
value : float

表格很大(数百万行)。每个“groupId”有不同数量的“值” - 比如介于100和50.000之间。所有浮点值都大于或等于零,但是无限制。

对于给定的groupId,查询应该返回通过减少相似性排序的所有其他组,其中“相似”被定义为两组中所有可能的30对值之间的最小欧几里德距离。

相似性的定义是杀死我的原因。我认为,对于如上定义的计算相似性,naiive算法是O(n ^ 2)。现在我正在寻找重新定义“相似性”或上述有效实现的想法。我可以想象一个涉及k-最近邻居的解决方案,比如PostGis几何最近邻居或者可能是最大的常见子序列算法(虽然我需要后者的“模糊”实现,因为“值”几乎不会完全相等)

我们目前正在使用mySQL,以防万一。

欢呼声,

Sören

4 个答案:

答案 0 :(得分:4)

你能否证实我的问题是对的?

您的表格表示groupId标识的向量。每个向量的维度都在100到50,000之间,但维度上没有定义顺序。这是表中的向量实际上是等价类的代表。

现在,您将两个等价类的相似性定义为任意两个代表等价类的投影与前30个维度的子空间的最小欧几里德距离。

投影到两个维度的示例:

A = <1, 2, 3, 4>
B = <5, 6, 7, 8, 9, 10>

A代表以下等价类的向量。

<1, 2, 3, 4>    <2, 1, 2, 3>    <3, 1, 2, 4>    <4, 1, 2, 3>
<1, 2, 4, 4>    <2, 1, 3, 2>    <3, 1, 4, 2>    <4, 1, 3, 2>
<1, 3, 2, 4>    <2, 3, 1, 4>    <3, 2, 1, 4>    <4, 2, 1, 3>
<1, 3, 4, 2>    <2, 3, 4, 1>    <3, 2, 4, 1>    <4, 2, 3, 1>
<1, 4, 2, 2>    <2, 4, 1, 3>    <3, 4, 1, 2>    <4, 3, 1, 2>
<1, 4, 3, 2>    <2, 4, 3, 1>    <3, 4, 2, 1>    <4, 3, 2, 1>

这个等价类的所有代表对前两个维度的预测产生。

<1, 2>    <1, 3>    <1, 4>
<2, 1>    <2, 3>    <2, 4>
<3, 1>    <3, 2>    <3, 4>
<4, 1>    <4, 2>    <4, 3>

B表示具有720个元素的等价类。对前两个维度的投影产生30个元素。

< 5, 6>    < 5, 7>    < 5, 8>    < 5, 9>    < 5, 10>
< 6, 5>    < 6, 7>    < 6, 8>    < 6, 9>    < 6, 10>
< 7, 5>    < 7, 6>    < 7, 8>    < 7, 9>    < 7, 10>
< 8, 5>    < 8, 6>    < 8, 7>    < 8, 9>    < 8, 10>
< 9, 5>    < 9, 6>    < 9, 7>    < 9, 8>    < 9, 10>
<10, 5>    <10, 6>    <10, 7>    <10, 8>    <10,  9>

因此A和B的距离是8的平方根,因为这是两个向量与投影的最小距离。例如&lt; 3,4&gt;和&lt; 5,6&gt;产生这个距离。

那么,我对这个问题的理解是对的吗?

对于具有m个分量的n个向量,真正天真的算法必须计算(n-1)个距离。对于每个距离,算法将计算m的距离! /(m - 30)!每个向量的投影。因此,对于100维度(您的下限),向量的可能投影为2.65 * 10 ^ 32。这需要计算投影之间的大约7 * 10 ^ 64距离并找到找到两个向量的距离的最小值。然后重复这个n次。

我希望我误解了你或犯了错误。否则,这听起来真的很有挑战性,也不可行。

我想到的是对矢量组件进行排序并尝试匹配它们。使用曼哈顿距离 - 如果可能 - 可能有助于简化解决方案。

答案 1 :(得分:1)

以下是一些不错的近似值:

您可以计算每组的质心,然后根据每组质心的距离进行比较。

你可以做的另一种方法是通过哈希将每个行的坐标和散列到同一位置的行视为相似,从而更新两组的相似性。

更多信息会有所帮助,例如:

信息是否不断更新,如果是,则以何种间隔更新。 它是如何更新的,它需要多准确?

答案 2 :(得分:0)

天真的版本将是这样的:(不通过查询分析器运行)

select groupid, min(distance) as mindist
from
   (select other.groupid as groupid,
           min(abs(other.value - us.value)) as distance
    from g us
    join g other on other.groupid != us.groupid
    where us.groupid = ?)
order by mindist
group by groupid

然后,利用指标:

select groupid, min(abs(value - usvalue)) as mindist
from
   (select other.groupid as groupid,
           max(other.value) as value,
           us.value as usvalue
    from g us
    join g other on other.groupid != us.groupid and other.value <= us.value
    where us.groupid = ?

    union

    select other.groupid as groupid,
           min(other.value) as value,
           us.value as usvalue
    from g us
    join g other on other.groupid != us.groupid and other.value >= us.value
    where us.groupid = ?)
order by mindist
group by groupid

这应该允许mysql使用索引快速找到连接上最近的邻居。

这可能存在错误,但希望这一思路会有所帮助。

答案 3 :(得分:0)

  

所有浮点值都大于或等于零,但无限制。

如果要在浮点数上执行KNN,请使用PostgreSQL的btree_gist模块并创建一个GIST索引。

  

对于具有自然距离度量标准的数据类型,btree_gist定义了一个距离运算符<->,并为使用该运算符的最近邻搜索提供了 GiST索引支持。为int2,int4,int8, float4 ,float8,带时区的时间戳,不带时区的时间戳,不带时区的时间,日期,间隔,oid和金钱提供了运算符。

float8double precision