20240316-1-向量化搜索

向量化搜索

在高维空间内快速搜索最近邻(Approximate Nearest Neighbor)。召回中,Embedding向量的搜索。

FAISS、kd-tree、局部敏感哈希、【Amnoy、HNSW】

FAISS

faiss是Facebook的AI团队开源的一套用于做聚类或者相似性搜索的软件库,底层是用C++实现。Faiss因为超级优越的性能,被广泛应用于推荐相关的业务当中。

faiss工具包一般使用在推荐系统中的向量召回部分。在做向量召回的时候要么是u2u,u2i或者i2i,这里的u和i指的是user和item。我们知道在实际的场景中user和item的数量都是海量的,最容易想到的基于向量相似度的召回就是使用两层循环遍历user列表或者item列表计算两个向量的相似度,但是这样做在面对海量数据是不切实际的,faiss就是用来加速计算某个查询向量最相似的topk个索引向量。

faiss查询的原理:

faiss使用了PCA和PQ(Product quantization乘积量化)两种技术进行向量压缩和编码,当然还使用了其他的技术进行优化,但是PCA和PQ是其中最核心部分。

主要流程

  • 构建索引index
  • 根据不同索引的特性,对索引进行训练(train
  • add 添加xb数据到索引
  • 针对xq进行搜索search操作

Example

1、数据集

d = 64                           # dimension
nb = 100000                      # 完整数据集
nq = 10000                       # 查询数据
np.random.seed(1234)             
xb = np.random.random((nb, d)).astype('float32')
xb[:, 0] += np.arange(nb) / 1000.
xq = np.random.random((nq, d)).astype('float32')
xq[:, 0] += np.arange(nq) / 1000.

2、构建索引

Faiss围绕Index对象构建。它封装了数据库向量集,并可选地对其进行预处理以提高搜索效率。索引的类型很多,我们将使用最简单的索引,它们仅对它们执行暴力L2距离搜索:IndexFlatL2

d在我们的例子中,所有索引都需要知道何时建立索引,即它们所操作的向量的维数

index = faiss.IndexFlatL2(d)   # build the index

3、对索引进行训练

然后,大多数索引还需要训练阶段,以分析向量的分布。对于IndexFlatL2,我们可以跳过此操作。

4、添加数据到索引

构建和训练索引后,可以对索引执行两项操作:addsearch

将元素添加到索引,我们称之为addxb。我们还可以显示索引的两个状态变量:is_trained,指示是否需要训练的布尔值,以及ntotal索引向量的数量。

一些索引还可以存储与每个向量相对应的整数ID(但不能存储IndexFlatL2)。如果未提供ID,则add只需将向量序号用作ID,即。第一个向量为0,第二个为1,依此类推。

index.add(xb)                  # add vectors to the index

5、对查询数据进行搜索操作

可以对索引执行的基本搜索操作是k-最近邻搜索,即。对于每个查询向量,k在数据库中找到其最近的邻居。

该操作的结果可以方便地存储在一个大小为nq-by-的整数矩阵中k,其中第i行包含查询向量i的邻居ID(按距离递增排序)。除此矩阵外,该search操作还返回一个具有相应平方距离的nqk浮点矩阵。

k = 4                          # we want to see 4 nearest neighbors
D, I = index.search(xb[:5], k) # sanity check, 首先搜索一些数据库向量,以确保最近的邻居确实是向量本身
print(I)
print(D)
D, I = index.search(xq, k)     # actual search
print(I[:5])                   # neighbors of the 5 first queries
print(I[-5:])                  # neighbors of the 5 last queries

索引方式

Faiss中的稠密向量各种索引都是基于 Index实现的,主要的索引方法包括: IndexFlatL2IndexFlatIPIndexHNSWFlatIndexIVFFlatIndexLSHIndexScalarQuantizerIndexPQIndexIVFScalarQuantizerIndexIVFPQIndexIVFPQR等,每个方法的具体介绍。

IndexFlatL2

  • 基于L2距离的暴力全量搜索,速度较慢,不需要训练过程。

IndexIVFFlat

  • 先聚类再搜索,可以加快检索速度;
  • 先将xb中的数据进行聚类(聚类的数目是超参),nlist: 聚类的数目
  • nprobe: 在多少个聚类中进行搜索,默认为1, nprobe越大,结果越精确,但是速度越慢
def IndexIVFFlat(nlist):
    quantizer = faiss.IndexFlatL2(d)
    index = faiss.IndexIVFFlat(quantizer, d, nlist)
    print(index.is_trained)
    index.train(xb)
    print(index.is_trained)
    index.add(xb)
    return index

IndexIFVPQ

  • 基于乘积量化(product quantizers)对存储向量进行压缩,节省存储空间
  • m:乘积量化中,将原来的向量维度平均分成多少份,d必须为m的整数倍
  • bits: 每个子向量用多少个bits表示
def IndexIVFPQ(nlist, m, bits):
    quantizer = faiss.IndexFlatL2(d)
    index = faiss.IndexIVFPQ(quantizer, d, nlist, m, bits)
    index.train(xb)
    index.add(xb)
    return index

kd树

kd树是一种对k维空间中的实例点进行存储以便对其进行快速检索的树形数据结构。kd树是二叉树,表示对k维空间的一个划分(partition)。构造kd树相当于不断地用垂直于坐标轴的超平面将k维空间切分,构成一系列的k维超矩形区域。kd树的每个结点对应于一个k维超矩形区域。

kd树的结构

kd树是一个二叉树结构,它的每一个节点记载了【特征坐标,切分轴,指向左枝的指针,指向右枝的指针】。

其中,特征坐标是线性空间 R n \mathbf{R}^n Rn的一个点 ( x 1 , . . . , x n ) (x_1,...,x_n) (x1,...,xn)

切分轴由一个整数 r r r表示,这里 1 ≤ r ≤ n 1\leq r\leq n 1rn,是我们在 n n n 维空间中沿第 n n n维进行一次分割。

节点的左枝和右枝分别都是 kd 树,并且满足:如果 y 是左枝的一个特征坐标,那么 y r ≤ x r y_r \leq x_r yrxr并且如果 z 是右枝的一个特征坐标,那么$x_r \leq z_r $。

kd树的构造

通过数据集来构造kd树存储空间,在推荐系统中即用物品Embedding池进行构建。

  • 输入:k维空间数据集 T = { x 1 , x 2 , ⋯   , x N } T=\left\{x_{1}, x_{2}, \cdots, x_{N}\right\} T={x1,x2,,xN},其中 x i = ( x i ( 1 ) , x i ( 2 ) , ⋯   , x i ( k ) ) T , i = 1 , 2 , . . . , N x_{i}=\left(x_{i}^{(1)}, x_{i}^{(2)}, \cdots, x_{i}^{(k)}\right)^{\mathrm{T}},i=1,2,...,N xi=(xi(1),xi(2),,xi(k))T,i=1,2,...,N

  • 输出:kd树;

  • (1)开始:构造根结点,根结点对应于包含 T T T k k k维空间的超矩形区域。

    选择 x ( 1 ) x^{(1)} x(1)为坐标轴,以 T T T中所有实例的 x ( 1 ) x^{(1)} x(1)坐标的中位数为切分点【若超平面上没有切分点,可以适当移动位置,使得超平面上有点】,将根结点对应的超矩形区域切分为两个子区域。切分由通过切分点并与坐标轴 x ( 1 ) x^{(1)} x(1)垂直的超平面实现。

    由根结点生成深度为1的左、右子结点:左子结点对应坐标 x ( 1 ) x^{(1)} x(1)小于切分点的子区域,右子结点对应于坐标 x ( 1 ) x^{(1)} x(1)大于切分点的子区域。
    落在切分超平面上的实例点保存在根结点

  • (2)重复:对深度为 j j j的结点,选择 x ( l ) x^{(l)} x(l)为切分的坐标轴, l = j (   m o d   k ) + 1 l=j(\bmod k)+1 l=j(modk)+1,以该结点的区域中所有实例的 x ( l ) x^{(l)} x(l)坐标的中位数为切分点,将该结点对应的超矩形区域切分为两个子区域。切分由通过切分点并与坐标轴 x ( l ) x^{(l)} x(l)垂直的超平面实现。
    由该结点生成深度为 j + 1 j+1 j+1的左、右子结点:左子结点对应坐标 x ( l ) x^{(l)} x(l)小于切分点的子区域,右子结点对应坐标 x ( l ) x^{(l)} x(l)大于切分点的子区域
    将落在切分超平面上的实例点保存在该结点。

  • (3)直到两个子区域没有实例存在时停止。从而形成kd树的区域划分。

最后每一部分都只剩一个点,将他们记在最底部的节点中。因为不再有未被记录的点,所以不再进行切分。

img

img

搜索kd树

在推荐系统中,即通过用户的Embedding向量来查找与其近邻的 K K K个物品Embedding向量。

  • 输入:已构造的kd树;目标点 x x x

  • 输出: x x x k k k近邻;

  • 设有一个$ k$个空位的列表,用于保存已搜寻到的最近点。

  • (1)在kd树中找出包含目标点 x x x的叶结点:从根结点出发,递归地向下访问树。若目标点 x x x当前维的坐标小于切分点的坐标,则移动到左子结点,否则移动到右子结点,直到子结点为叶结点为止;

  • (2)如果**“当前k近邻点集”元素数量小于 k k k或者叶节点距离小于“当前k近邻点集”中最远点距离**,那么将叶节点插入“当前k近邻点集”;

  • (3)递归地向上回退,在每个结点进行以下操作:

    • 如果“当前k近邻点集”元素数量小于k或者当前节点距离小于“当前k近邻点集”中最远点距离,那么将该节点插入“当前k近邻点集”。
    • 检查该子结点的父结点的另一子结点对应的区域是否与以目标点为球心、以目标点与于“当前k近邻点集”中最远点间的距离为半径的超球体相交。如果相交,可能在另一个子结点对应的区域内存在距目标点更近的点,移动到另一个子结点,接着,递归地进行最近邻搜索;如果不相交,向上回退;
  • 当回退到根结点时,搜索结束,最后的“当前k近邻点集”即为 x x x的k近邻点。

kd树的平均计算复杂度是 l o g ( N ) log(N) log(N)

参考资料:kd 树算法之详细篇

局部敏感哈希

局部敏感哈希的基本思想:

让相邻对的点落入同一个“桶”,这样在进行最近邻搜索时,仅需要在一个桶内,或相邻的几个桶内的元素中进行搜索即可。如果保持每个桶中的元素个数在一个常数附近,就可以把最近邻搜索的时间复杂度降低到常数级别。

首先需要明确一个概念,

在欧式空间中,将高维空间的点映射到低维空间,原本相近的点在低维空间中肯定依然相近,但原本远离的点则有一定概率变成相近的点。

所以利用低维空间可以保留高维空间相近距离关系的性质,就可以构造局部敏感哈希的桶。

对于Embedding向量,可以用内积操作构建局部敏感哈希桶。假设 v \mathbf{v} v是高维空间中的 k k k维Embedding向量, x \mathbf{x} x是随机生成的 k k k维向量。内积操作可以将 v \mathbf{v} v映射到1维空间,成为一个数值:
h ( v ) = v ⋅ x h(\mathbf{v})=\mathbf{v}\cdot \mathbf{x} h(v)=vx
因此,可以使用哈希函数 h ( v ) h(v) h(v)进行分桶:
h x , b ( v ) = ⌊ x x ⋅ v + b w ⌋ x h^{x,b}(\mathbf{v})=\lfloor x \frac{\mathbf{x}\cdot \mathbf{v}+ b}{w}\rfloor x hx,b(v)=xwxv+bx
其中 ⌊ ⌋ \lfloor \rfloor 是向下取整, w w w是分桶宽度, b b b是0到 w w w间的一个均匀分布随机变量,避免分桶边界固化。

如果仅采用一个哈希函数进行分桶,则必然存在相近点误判的情况。有效的解决方法是采用 m m m个哈希函数同时进行分桶。同时掉进 m m m个哈希函数的同一个桶的两点,是相似点的概率大大增加。找到相邻点集合后,取 K K K近邻个。

采用多个哈希函数进行分桶,存在一个待解决的问题,到底通过“与”还是“或”:

  • 与:候选集规模减小,计算量降低,但可能会漏掉一些近邻点;
  • 或:候选集中近邻点的召回率提高,但候选集的规模变大,计算开销变大;

以上是将欧式空间中内积操作的局部敏感哈希使用方法;还有余弦相似度、曼哈顿距离、切比雪夫距离、汉明距离等。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

图中的边关系和节点关系之间的转换

图中的边关系和节点关系之间的转换 边关系转为图 在relation数组中记录的是从一个节点到一个节点,前面的就叫做from,后面的就叫做to,因此每次添加进节点关系的数组的时候,from就是数组索引,to就是需要加入的值。也就是…

揭秘最热门AI写作软件,看看有哪些值得推荐的AI写作神器

在快节奏的现代生活中,我们常常面临各种压力,例如工作、学习等。因此,一款能够提高写作效率的工具变得尤为重要。那么,有没有什么AI写作软件是比较好用的呢?下面小编给大家推荐几款热门的写作软件。 一.爱制作AI写作 …

打造稳定高效的会员系统:技术架构解析与优化策略

随着互联网时代的发展和用户需求的变化,会员系统成为了各行各业企业实现用户粘性和增长的重要手段。一个稳定高效的会员系统架构能够帮助企业更好地管理会员数据、提供个性化服务和增加用户价值。本文将深入探讨会员系统的技术架构,分析其重要性和挑战&a…

Transformer的前世今生 day02(神经网络语言模型、词向量)

神经网络语言模型 使用神经网络的方法,去完成语言模型的两个问题,下图为两层感知机的神经网络语言模型: 假设词典V内有五个词:“判断”、“这个”、“词”、“的”、“词性”,且要输出P(w_next | “判断”、“这个”、…

Linux东方通下载及使用

把压缩包拖进去 解压文件 mkdir /usr/local/java

新品发布 | Ftrans FIE文件安全导入导出系统

关于飞驰云联 飞驰云联是中国领先的数据安全传输解决方案提供商,长期专注于安全可控、性能卓越的数据传输技术和解决方案,公司产品和方案覆盖了跨网跨区域的数据安全交换、供应链数据安全传输、数据传输过程的防泄漏、FTP的增强和国产化替代、文件传输自…

加速您的 AI 开发:NVIDIA AI Workbench 正式发布

加速您的 AI 开发:NVIDIA AI Workbench 正式发布 NVIDIA AI Workbench 是一款面向 AI 和 ML 开发人员的工具包,现已普遍提供免费下载。 它具有自动化功能,可以消除新手开发人员的障碍并提高专家的工作效率。 无论技能水平如何,开…

使用倒模耳机壳UV树脂胶液制作舞台监听耳返入耳式耳机壳有哪些优点?

使用倒模耳机壳UV树脂胶液制作舞台监听耳返入耳式耳机壳有很多优点,具体如下: 高音质表现:通过倒模工艺制作的耳机壳能够更好地贴合耳朵,减少声音散射和反射,提高声音的清晰度和质感。这对于舞台监听来说非常重要&…

【漏洞复现】福建科立迅通信指挥调度平台down_file.php sql注入漏洞

漏洞描述 福建科立迅通信调度平台 20240318 以及之前版本存在一个严重漏洞,影响了文件 api/client/down_file.php 的一个未知功能。攻击者可以通过操纵参数 uuid 发起 SQL 注入攻击。攻击者可以远程发起攻击。 免责声明 技术文章仅供参考,任何个人和组织使用网络应当遵守…

OpenGL学习笔记【3】—— GLAD配置

一、为什么用GLAD 由于OpenGL驱动版本众多,它大多数函数的位置都无法在编译时确定下来,需要在运行时查询。所以任务就落在了开发者身上,开发者需要在运行时获取函数地址并将其保存在一个函数指针中供以后使用。取得地址的方法因平台而异&…

Redis 大 Key 对持久化有什么影响?

资料来源 : 小林coding 小林官方网站 : 小林coding (xiaolincoding.com) Redis 的持久化方式有两种:AOF 日志和 RDB 快照。 所以接下来,针对这两种持久化方式具体分析分析 大 Key 对 AOF 日志的影响 先说说 AOF 日志三种写回磁盘的策略 Redis 提供了 3…

如何让 string 型的字符串变成 int 型的整数

之前我们讲过了如何裁剪字符串和如何反转字符串&#xff0c;具体情况可以看看我前几期发的博客&#xff0c;今天我们就来讲讲怎么将 string 型的字符串变成 int 型的整数。 我们可以使用在 <bits/stdc.h> 中的 atoi 函数来处理这种形式转变&#xff0c;如下&#xff1a;…

如何使用Android平板公网访问本地Linux code-server

文章目录 1.ubuntu本地安装code-server2. 安装cpolar内网穿透3. 创建隧道映射本地端口4. 安卓平板测试访问5.固定域名公网地址6.结语 1.ubuntu本地安装code-server 准备一台虚拟机,Ubuntu或者centos都可以&#xff0c;这里以VMwhere ubuntu系统为例 下载code server服务,浏览器…

设计模式之单例模式解析

单例模式 1&#xff09;动机 对于软件系统的某些类&#xff0c;无须创建多个实例&#xff0c;如 Windows 系统的任务管理器&#xff0c;重复对象会浪费系统资源。 2&#xff09;概述 1.定义 确保某个类只有一个实例&#xff0c;而且自行实例化&#xff0c;并向整个系统提供…

vue中循环数据,添加展开、收起操作

1.在data中定义变量 expandedIndex&#xff0c;默认展开第一条 expandedIndex:0,2.标题栏展开、收起显示判断&#xff0c;并填加点击事件 toggleVisibility <h5 class"titleLine">{{item.checkPart}} <span click"toggleVisibility(index)">…

【GPT概念04】仅解码器(only decode)模型的解码策略

一、说明 在我之前的博客中&#xff0c;我们研究了关于生成式预训练转换器的整个概述&#xff0c;以及一篇关于生成式预训练转换器&#xff08;GPT&#xff09;的博客——预训练、微调和不同的用例应用。现在让我们看看所有仅解码器模型的解码策略是什么。 二、解码策略 在之前…

【LVGL-按钮按钮矩阵部件】

LVGL-按钮&按钮矩阵部件 ■ LVGL-按钮部件■ 按钮部件&#xff1a; 点击三个按钮一个回调函数修改label值。 ■ LVGL-按钮矩阵部件■ 示例一&#xff1a;按钮换行&#xff0c;和宽度设置。■ 示例二&#xff1a;设置按钮宽度为2倍■ 示例三&#xff1a;获取点击的按钮下标&…

【以图搜图】GPUNPU适配万物识别模型和Milvus向量数据库

目录 以图搜图介绍项目地址Milvuscv_resnest101_general_recognition 代码使用流程结果展示模型部署环境Milvus部署及使用docker安装docker-compose安装Milvus可视化工具Attu进入网页端 Data数据示例点个赞再走呗&#xff01;比心&#x1f49e;️ 以图搜图 • &#x1f916; Mo…

【java】10.面向对象

一、类和对象 1.1 类和对象的理解 客观存在的事物皆为对象 &#xff0c;所以我们也常常说万物皆对象。 * 类 * 类的理解 * 类是对现实生活中一类具有共同属性和行为的事物的抽象 * 类是对象的数据类型&#xff0c;类是具有相同属性和行为的一组对象的集合 * 简单理解&am…

C#、.NET版本、Visual Studio版本对应关系及Visual Studio老版本离线包下载地址

0、写这篇文章的目的 由于电脑的环境不同&#xff0c;对于一个老电脑找到一个适配的vscode环境十分不易。总结一下C#、.NET、Visual Studio版本的对应关系&#xff0c;及各个版本Visual Studio的下载地址供大家参考 1、C#、.NET版本、Visual Studio版本对应关系如下 2、Visua…