SQL高效最近邻查询

7
我是一名有用的助手,可以为您翻译文本。

我在处理以下情况时遇到了SQL查询效率低的问题:

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

groupId : int 
value : float

这张表非常庞大(数百万行)。每个“groupId”具有不同数量的“values”,大约在100到50,000之间。所有浮点值都大于或等于零,但否则没有限制。

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

这种相似性的定义让我很疑惑。我认为,针对上述定义计算相似性的朴素算法是O(n ^ 2)。现在我正在寻找重新定义“相似性”或有效实施上述内容的思路。我可以想象涉及k近邻的解决方案,类似于PostGis几何最近邻居或者可能是最长公共子序列算法(尽管我需要后者的“模糊”实现,因为“values”几乎永远不会完全相等)。

我们目前使用的是MySQL,如果有影响请告知。

谢谢!

Sören

Danbruc(下面的第一个答案)比我更好地描述了这个问题。也许他的分析会澄清这个问题?我们目前有大约500个组和180万个值。我们希望将其扩展到数十万个组。当前的设置只是一个小测试用例。 - BuschnicK
当你说“30个值的所有可能对之间的最小欧几里得距离”时,你是指{10, 100}更接近于{10, 9999}(因为它们相距0),还是更接近于{20, 90}(因为最小总距离为10 + 10 = 20)? - FryGuy
1
dist((10,100),(10,9999)) = sqrt((10-10)^2 + (9999-100)^2) = 大 dist((10,100),(20,90)) = sqrt((10-20)^2 + (100-90)^2) = 较小 - BuschnicK
对FryGuy答案的原始评论:这个SQL语句可能会多次使用某些坐标。这是允许的还是期望的?对于<1, 2, 3, 4>和<5, 100, 100, 100>,距离计算将四次使用第一个坐标5。 - Daniel Brückner
看一下我的答案,看看是否能够解决你的问题。 - Evan Carroll
显示剩余2条评论
4个回答

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的平方根。例如<3,4>和<5,6>产生了这个距离。
那么,我的理解是否正确?
一个非常简单的算法对于n个具有m个分量的向量需要计算(n-1)个距离。对于每个距离,该算法会计算每个向量的m! / (m - 30)! 投影的距离。因此,对于100维(您的下限),每个向量都有2.65 * 10 ^ 32个可能的投影。这需要计算大约7 * 10 ^ 64次投影之间的距离并找到最小值来找到两个向量之间的距离。然后重复这个过程n次。
我希望我误解了您或犯了一个错误。否则,这听起来像是非常具有挑战性和不可行的事情之间的某种东西。
我想到的一个解决方案是对向量组件进行排序并尝试匹配它们。如果可能的话,使用曼哈顿距离可以帮助简化解决方案。

是的,你完美地理解了问题并且比我更好地解释了它。对向量进行排序也是我一直在思考的问题,因此我提到了LCS(最长公共子序列)。我会研究一下曼哈顿距离是否对我们有用。 - BuschnicK

1
所有浮点数值都大于或等于零,但在其他方面是无界的。
如果您想在浮点数上执行KNN,请使用PostgreSQL的btree_gist模块并创建一个GIST索引。
此外,对于存在自然距离度量的数据类型,btree_gist定义了一个距离运算符<->,并提供了使用此运算符进行最近邻搜索的GiST索引支持。为int2、int4、int8、float4、float8、带时区的时间戳、不带时区的时间戳、不带时区的时间、日期、间隔、oid和货币提供距离运算符。 float8double precision

1

以下是一些不错的近似方法:

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

另一种方法是通过哈希每行的坐标,将哈希到相同位置的行视为相似,从而更新两个组的相似度。

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

信息是否经常更新,如果是,则更新间隔是多少。 需要多新和多准确的信息?


一维的质心?那不就是中位数或平均数吗?还是你指的是所有可能的30个值向量排列的质心?哈希基本上意味着量化所有值,对吧?也就是说,我们会将所有值捕捉到一个网格上? - BuschnicK
现有的信息永远不会被更新 - 只会添加新的组。每天大约100个。准确性很好,但并非关键。整个设置是一个预处理步骤。想法是从数据库中获取最可能的“匹配项”,然后使用更昂贵的独立工具来测试这些匹配项。 - BuschnicK
我没有看到第一个回答,它解决了问题。鉴于此,我不确定我的回答是否有用。 - fuzzy-waffle

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使用索引快速查找连接中最近的邻居。

可能会有错误,但希望这种思路能够帮助。


谢谢FryGuy。这基本上是我们尝试过的,但它根本不可扩展。我会尝试以上的变化并发布结果。 - BuschnicK
你的 groupid 和 value 字段都有索引吗? - FryGuy
是的。据我所知,mySQL的“explain”(查询执行计划)看起来已经很好了。 - BuschnicK
这个 SQL 语句可能会多次使用某些坐标。这是允许的还是期望的?对于 <1, 2, 3, 4> 和 <5, 100, 100, 100>,距离计算将会四次使用第一个坐标 5。 - Daniel Brückner
嗯,我误读了问题。这个查询不会回答你的问题,但是按照从group1到group2的所有距离选择的最小距离排序(而不是最小距离的总和)。我应该删除这个答案吗? - FryGuy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接