最近邻搜索 - 经典树型结构 M-Tree

news2024/12/12 13:39:27

前言

如果你对这篇文章感兴趣,可以点击「【访客必读 - 指引页】一文囊括主页内所有高质量博客」,查看完整博客分类与对应链接。

最近邻搜索的目标是从 N N N 个对象中,快速找到距离查询点最近的对象。根据需求的不同,该任务又分为「精准查找」与「近似查找」,并且查找的目标也分为「找到前 K K K 个最近的对象」与「找到距查询点距离小于 r r r 的对象」。处理此类任务的关键在于组织已有对象的数据结构,大致分为以下三类:

  1. 树型结构:例如 Vantage-Point Tree (VP-Tree) [1]、M-Tree [2]、Cover Tree [3]、Faster Cover Trees [4] 等;
  2. 图型结构:例如 Hierarchical Navigable Small World (HNSW) [5]、Locallyadaptive Vector Quantization (LVQ) [6] 等;
  3. 哈希方法:例如 Locality Sensitive Hashing (LSH) 中的 SimHash、MinHash 等。

在当前时代下,如果你的对象就是向量,使用的距离度量也是常见度量(e.g., 欧式距离、余弦相似度等),可以直接调用一些成熟的向量数据库或最近邻检索算法库解决问题。

本文主要对一个经典的树型结构 M-Tree 进行介绍(已提出 20 多年,在数据库领域有大量应用),其特点是:

  1. 支持任意满足对称性 (Symmetry)、非负性 (Non-negativity)、三角不等式 (Triangle Inequality) 的距离度量;
  2. 支持整体数据结构的动态维护,即根据新对象的插入对整体结构做局部调整。

M-Tree 结构

M-Tree 中分为内部节点和叶节点,其中每个内部节点包含一个路由对象 (Routing Objects) O r O_r Or,并且动态维护该路由节点所覆盖的半径 r ( O r ) r(O_r) r(Or),即 O r O_r Or 对应子树中的所有点,距 O r O_r Or 的距离小于等于 r ( O r ) r(O_r) r(Or)。维护每个内部节点的覆盖半径,可用于后续近邻查搜过程中的快速剪枝,避免遍历整棵树。

每个内部节点还需维护其与父节点 P ( O r ) P(O_r) P(Or) 之间的距离(同样用于后续查搜时的快速剪枝),具体维护的信息如下所示:
在这里插入图片描述
与内部节点不同的是,每个叶节点无需维护其覆盖半径,并且该节点直接指向具体的数据点,具体维护的信息如下所示:
在这里插入图片描述

需要注意的是,所有的数据点都会出现在叶节点中,内部节点中的路由对象为一些 “关键” 数据点的拷贝。


M-Tree 查搜

近邻查搜通常分为两种,一种是 Range Queries,即对于待查询数据 Q Q Q,查找所有满足 d ( O j , Q ) ≤ r ( Q ) d(O_j,Q)\leq r(Q) d(Oj,Q)r(Q) 的数据 O j O_j Oj;另一种是 Nearest Neighbor Queries,即查找距查询数据 Q Q Q 最近的前 k k k 个数据点。

首先介绍 Range Queries,其查搜时有两种剪枝方式。假设当前查询节点为 O r O_r Or,其与查询点 Q Q Q 之间的距离为 d ( O r , Q ) d(O_r,Q) d(Or,Q),则 O r O_r Or 子树内任意点 O ′ O' O 满足(距离三角不等式):
d ( O ′ , Q ) ≥ d ( O r , Q ) − d ( O ′ , O r ) ≥ d ( O r , Q ) − r ( O r ) . d(O',Q)\geq d(O_r,Q)-d(O',O_r)\geq d(O_r,Q)-r(O_r). d(O,Q)d(Or,Q)d(O,Or)d(Or,Q)r(Or).

因此如果 O r O_r Or 满足 d ( O r , Q ) > r ( Q ) + r ( O r ) d(O_r,Q)>r(Q)+r(O_r) d(Or,Q)>r(Q)+r(Or),即 d ( O ′ , Q ) > r ( Q ) d(O',Q)>r(Q) d(O,Q)>r(Q),则无需再遍历 O r O_r Or 子树内的节点,此为第一种剪枝方式。

此外,假设 O r O_r Or 的父节点为 O p O_p Op,其距 Q Q Q 的距离为 d ( O p , Q ) d(O_p,Q) d(Op,Q),则 O r O_r Or 子树内任意点 O ′ O' O 满足(距离三角不等式):
d ( O ′ , Q ) ≥ d ( O r , Q ) − r ( O r ) ≥ ∣ d ( O r , Q p ) − d ( O p , Q ) ∣ − r ( O r ) . d(O',Q)\geq d(O_r,Q)-r(O_r)\geq |d(O_r,Q_p)-d(O_p,Q)|-r(O_r). d(O,Q)d(Or,Q)r(Or)d(Or,Qp)d(Op,Q)r(Or).

因此如果 O r O_r Or 满足 ∣ d ( O r , Q p ) − d ( O p , Q ) ∣ > r ( Q ) + r ( O r ) |d(O_r,Q_p)-d(O_p,Q)|>r(Q)+r(O_r) d(Or,Qp)d(Op,Q)>r(Q)+r(Or),则可以直接将整颗 O r O_r Or 子树剪枝,并且无需计算 d ( O r , Q ) d(O_r,Q) d(Or,Q),此为第二种剪枝。

以下为 M-Tree 执行 Range Queries 的具体算法:

在这里插入图片描述

接下来是 Nearest Neighbor Queries,其剪枝思路与 Range Queries 类似,只是将查询范围 r ( Q ) r(Q) r(Q) 换成了 d k d_k dk,即当前距 Q Q Q k k k 小的距离。另外 d m i n ( T ( O r ) ) = max ⁡ { d ( O r , Q ) − r ( O r ) , 0 } , d m a x ( T ( O r ) ) = d ( O r , Q ) + r ( O r ) d_{min}(T(O_r))=\max\{d(O_r,Q)-r(O_r),0\},d_{max}(T(O_r))=d(O_r,Q)+r(O_r) dmin(T(Or))=max{d(Or,Q)r(Or),0},dmax(T(Or))=d(Or,Q)+r(Or) 分别代表 O r O_r Or 子树中距 Q Q Q 可能的最小和最大距离,执行 Nearest Neighbor Queries 的具体算法如下所示:

在这里插入图片描述


M-Tree 构建

M-Tree 的构建为依次插入新的数据点,其中每个叶节点可以存储多个数据点,如果叶节点满了,则会进行节点分裂,进而动态地维护整颗树结构。

M-Tree 数据插入

在每一层插入新数据点时,首先选择不会使内部节点覆盖半径变大的节点,如果不存在这样的节点,则选择使覆盖半径变大幅度最小的节点,具体算法如下所示:

在这里插入图片描述

M-Tree 节点分裂

如果最后插入的叶节点满了,则需要进行分裂。具体分裂过程为:

  1. 从当前节点 N 的所有数据点 + 新数据点 O n O_n On 中选出两个代表节点 O p 1 O_{p_1} Op1 O p 2 O_{p_2} Op2,对应 Promote 操作;
  2. 将这些数据点集合 N \mathcal{N} N 分为距 O p 1 O_{p_1} Op1 更近的 N 1 \mathcal{N}_1 N1 和距 O p 2 O_{p_2} Op2 更近的 N 2 \mathcal{N}_2 N2,对于 Partition 操作;
  3. 将 N 中的路由节点替换为 O p 1 O_{p_1} Op1,并判断 N 的上层节点是否满了,如果没满则插入 O p 2 O_{p_2} Op2,否则继续向上进行分裂。

整体算法如下所示:

在这里插入图片描述

节点分裂之后,仍需维护新增节点的覆盖半径,具体如下:
r ( O p 1 ) = max ⁡ { d ( O r , O p 1 ) + r ( O r ) ∣ O r ∈ N 1 } . r\left(O_{p_1}\right)=\max \left\{d\left(O_r, O_{p_1}\right)+r\left(O_r\right) \mid O_r \in \mathcal{N}_1\right\}. r(Op1)=max{d(Or,Op1)+r(Or)OrN1}.

节点分裂策略

两个代表点的选择有多种策略,具体可以查看原文,此处介绍几种代表性策略:

  1. m_RAD: 计算 N \mathcal{N} N 中所有节点两两之间距离,选择最小化 r ( O p 1 ) + r ( O p 2 ) r(O_{p_1})+r(O_{p_2}) r(Op1)+r(Op2) 的代表点;
  2. mM_RAD: 计算 N \mathcal{N} N 中所有节点两两之间距离,选择最小化 max ⁡ { r ( O p 1 ) , r ( O p 2 ) } \max\{r(O_{p_1}),r(O_{p_2})\} max{r(Op1),r(Op2)} 的代表点;
  3. m_LB_DIST: 将 O p 1 O_{p_1} Op1 设置为原节点 O p O_p Op,选择距离 O p O_p Op 最远的点作为 O p 2 O_{p_2} Op2.

代码实现

  • 原作者代码实现 - C++
  • Github 其他开源版本 - Python
  • Github 其他开源版本 - C++ / Java / Python

参考资料

  1. VLDB 1994 - Content-based image indexing.
  2. VLDB 1997 - M-tree: An Efficient Access Method for Similarity Search in Metric Spaces.
  3. ICML 2006 - Cover Trees for Nearest Neighbor.
  4. ICML 2015 - Faster Cover Trees
  5. TPAMI 2020 - Hierarchical Navigable Small World
  6. VLDB 2023 - Similarity search in the blink of an eye with compressed indices

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2258263.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Jmeter进阶篇(30)深入探索 JMeter 监听器

前言 在性能测试领域里,Apache JMeter 是一款经典而强大的工具,而其中的监听器(Listeners)组件更是发挥着不可或缺的关键作用。 监听器就像敏锐的观察者,默默记录测试执行过程中的各种数据,作为系统性能分析的数据依据。 本文将带你全方位走进 JMeter 监听器的奇妙世界,…

uni-app 个人课程表页面

uni-app 个人课程表页面 插件参考地址 大部分代码都是参考了上述代码&#xff0c;只对代码做出了优化 1. 页面模板 在 schedule.vue 文件中&#xff0c;编写页面结构&#xff1a; <template><view><u-navbar title"个人中心"><view class&q…

ElementEye,网页分析器

介绍 我们经常使用Python写爬虫&#xff0c;爬到网页数据之后&#xff0c;就需要用beautifulSoup进行解析。因为写爬虫并不是我的主营工作&#xff0c;大多数只是用来分析一下想要的数据而已&#xff0c;所以经常会忘记beautifulSoup的用法。 同时&#xff0c;我们总是分析页面…

【Compose multiplatform教程】01 创建你的多平台项目 <官网搬运>

这是 “创建带有共享逻辑和用户界面的 Compose 多平台应用” 教程的第一部分。 第一步&#xff1a;创建你的多平台项目 第二步&#xff1a;探究可组合代码 第三步&#xff1a;修改项目 第四步&#xff1a;创建你自己的应用程序 在这里&#xff0c;你将学习如何使用 Kotlin 多平…

使用OpenTK展示3D点云图像(C#)

最近在研究3D显示&#xff0c;找到一款在winform上展示3D点云的控件&#xff0c;并且实现了点线面的展示&#xff0c;及光照渲染纹理贴图等功能&#xff0c;如下面几张图所展示。 一些基础知识可以在LearnOpenTK - OpenTK 这个网站上学习到。 我这边使用的是openTK3.3.3版本&a…

李宏毅机器学习-批次 (batch)和动量(momentum)

一.batch&#xff08;批次&#xff09; 在计算微分时&#xff0c;不是对所有的数据算出来的Loss值做微分&#xff0c;而是将所有的数据分成一个一个的batch。一个batch是一个B&#xff0c;在更新参数时&#xff0c;拿B的资料计算Loss&#xff0c;计算gradient&#xff0c;再更新…

洗鞋小程序(源码+文档+部署+讲解)

本文将深入解析“洗鞋小程序”的项目&#xff0c;探究其架构、功能以及技术栈&#xff0c;并分享获取完整源码的途径。 系统概述 为洗鞋提供服务&#xff0c;包含小程序和管理端。 本项目名称为洗鞋小程序&#xff0c;是一个基于小程序的在线洗鞋平台。该系统提供下单、订单管…

【数据结构】二叉树的性质和存储结构

性质 在二叉树的第i层上至多有2^{i-1}个结点,至少有1个结点 深度为k的二叉树至多有2^{k-1}个结点&#xff08;k≥1&#xff09;&#xff0c;至少有k个结点 对任何一棵二叉树T&#xff0c;如果其叶子数为n0&#xff0c;度为2的结点数为n2&#xff0c;则n0n21 具有n个结点的完…

交换排序(Swap Sort)详解

交换排序Swap Sort详解 冒泡排序冒泡算法代码实现冒泡分析 快速排序快排算法代码实现快排分析 交换类排序主要是通过两两比较待排元素的关键字&#xff0c;若发现与排序要求相逆&#xff0c;则交换之。在这类排序方法中最常见的是起泡排序&#xff08;冒泡排序&#xff09;和快…

MySQL追梦旅途之性能优化

1、索引优化 索引可以显著加速查询操作&#xff0c;但过多或不适当的索引也会带来负面影响&#xff08;如增加写入开销&#xff09;。因此&#xff0c;选择合适的索引至关重要。 创建索引&#xff1a; 为经常用于WHERE子句、JOIN条件和ORDER BY排序的列创建索引。 CREATE I…

小程序IOS安全区域优化:safe-area-inset-bottom

ios下边有一个小黑线&#xff0c;位于底部的元素会被黑线阻挡 safe-area-inset-bottom 一 用法及作用&#xff1a; IOS全面屏底部有小黑线&#xff0c;位于底部的元素会被黑线阻挡&#xff0c;可以使用以下样式&#xff1a; .model{padding-bottom: constant(safe-area-ins…

矩阵的乘(包括乘方)和除

矩阵的乘分为两种&#xff1a; 一种是高等代数中对矩阵的乘的定义&#xff1a;可以去这里看看包含矩阵的乘。总的来说&#xff0c;若矩阵 A s ∗ n A_{s*n} As∗n​列数和矩阵 B n ∗ t B_{n*t} Bn∗t​的行数相等&#xff0c;则 A A A和 B B B可相乘&#xff0c;得到一个矩阵 …

解决阿里云轻量级服务器 Ubuntu 24.04.1 LTS 没网也 ping 不通 8.8.8.8 以及 route -n 没有输出任何转发信息

事情发生在两天前&#xff0c;位于公网的阿里云轻量级服务器&#xff08;Ubuntu 24.04.1 LTS&#xff09;忽然没网。主要是上次上服务器进行配置已经是一个多月前&#xff0c;最近也没有做什么事情&#xff0c;就忽然没网了&#xff0c;让人纳闷。更主要的是&#xff0c;上次备…

Cesium中实现仿ArcGIS三维的动态图层加载方式

Cesium 加载 ArcGIS 动态图层的方式 如果你在 Cesium 中加载过 ArcGIS 的动态图层&#xff0c;你会发现&#xff0c;Cesium 对于动态图层仍然采用类似切片图层的逻辑进行加载。也就是每个固定的瓦片 export 一张图片。 这样会造成一些问题&#xff1a; 请求量大&#xff0c;…

Tablesaw封装Plot.ly实现数据可视化

上文介绍tablesaw的数据处理功能&#xff0c;本文向你展示其数据可视化功能&#xff0c;并通过几个常用图表示例进行说明。 Plot.ly包装 可视化是数据分析的重要组成部分&#xff0c;无论你只是“查看”新数据集还是验证机器学习算法的结果。Tablesaw是一个开源、高性能的Java…

智慧商城项目2(vue核心技术与实战)

页面访问拦截了解 router/index.js import Vue from vue import VueRouter from vue-router import Login from /views/login import Layout from /views/layout import Search from /views/search import SearchList from /views/search/list import Prodetail from /views/…

第一个C++程序--(蓝桥杯备考版)

第一个C程序 基础程序 #include <iostream>//头⽂件 using namespace std;//使⽤std的名字空间 int main()//main函数 {cout << "hello world!" << endl; //输出&#xff1a;在屏幕打印"hello world!" return 0;}main函数 main 函数是…

华为云域名网站,域名切换到Cloudflare CDN出现访问报错:DNS 重定向次过多

网站域名切换到Cloudflare出现访问报错&#xff1a;重定向次过多&#xff0c;应该如何处理&#xff1f; 最近我自己已经遇到很多次这个情况了&#xff0c;将网站域名DNS切换到Cloudflare之后&#xff0c;网站会打不开&#xff0c;出现重定向次数过多报错。 网站域名切换到Clo…

颜色的基本处理

数码相机能够获取彩色图像&#xff0c;但相机的色彩处理是一个非常复杂的过程&#xff0c;是非常重要的。 此过程生产制造商在细节方面都是不公布的&#xff0c;但是基本的概念是相同的。当相机捕捉一个真实场景时&#xff0c;是怎么还原成人眼所看到的图像呢&#xff1f; 1.R…

与 Cursor AI 对话编程:2小时开发报修维修微信小程序

本文记录了如何通过与 Cursor AI 对话&#xff0c;全程不写一行代码的情况下&#xff0c;完成一个完整的报修小程序。整个过程展示了 AI 如何帮助我们&#xff1a; 生成代码 、解决问题、优化实现、完善细节。 先看一下效果图&#xff1a; 一、项目配置 首先我是这样和 AI 对…