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近邻个。

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

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

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

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

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

相关文章

golang kafka client 消费者代码

启动kafka [rootlocalhost kafka_2.12-2.5.1] # [kube:] cat start_zk.sh ./bin/zookeeper-server-start.sh config/zookeeper.properties > /dev/null & [rootlocalhost kafka_2.12-2.5.1] # [kube:] cat start_kafka.sh ./bin/kafka-server-start.sh config/server…

go执行go mod tidy时报错连接失败(go换依赖源)

报错: go: cloud.google.com/gov0.41.0: Get "https://proxy.golang.org/cloud.google.com/go/v/v0.41.0.mod": dial tcp 142.251.43.17:443: connectex: A connection attempt failed because the connected party did not properly respond after a pe…

ITK零碎笔记

1、ITK提取等高线&#xff08;区域轮廓&#xff09; itk::ContourExtractor2DImageFilter typedef itk::ContourExtractor2DImageFilter<FSliceType> ContourExtractorType; ContourExtractorType::Pointer contourFilter ContourExtractorType::New(); contourFilter-…

leetcode 2617. 网格图中最少访问的格子数【单调栈优化dp+二分】

原题链接&#xff1a;2617. 网格图中最少访问的格子数 题目描述&#xff1a; 给你一个下标从 0 开始的 m x n 整数矩阵 grid 。你一开始的位置在 左上角 格子 (0, 0) 。 当你在格子 (i, j) 的时候&#xff0c;你可以移动到以下格子之一&#xff1a; 满足 j < k < gri…

蓝桥杯算法练习系统—金属采集(树形dp)

问题描述 人类在火星上发现了一种新的金属&#xff01;这些金属分布在一些奇怪的地方&#xff0c;不妨叫它节点好了。一些节点之间有道路相连&#xff0c;所有的节点和道路形成了一棵树。一共有 n 个节点&#xff0c;这些节点被编号为 1~n 。人类将 k 个机器人送上了火星&…

谷歌地球三维模型

收费工具&#xff0c;白嫖党勿扰 收费金额2000元 0 概述 我也不知道为什么&#xff0c;之前发的谷歌地球三维模型相关的博客&#xff0c;被CSDN屏蔽&#xff0c;我问了客服&#xff0c;客服回答&#xff0c;他也不知道什么原因… 1 折中方案 同学们可以看这篇博客&#xff0…

【网络】:IP协议

IP协议 一.IP报头二.网段划分三.IP地址数量限制四.私有IP地址和公有IP地址五.路由 IP协议就是让数据有能力进行跨网络传输。 一.IP报头 4位版本号(version): 指定IP协议的版本, 对于IPv4来说, 就是4.4位头部长度(header length): IP头部的长度是多少个32bit, 也就是 length * …

【CMake】所见所闻所学

Note: 本贴仅记录遇到的CMake的问题&#xff0c;以问题为驱动。 - cmake_minimum_required - project - add_executable - target_include_directories - ExternalProject_Add ExternalProject_Add 是 CMake 中用于管理和构建外部项目的模块。通过 ExternalProject_Add&…

海外客户获取难?海外云手机助力电商引流!

海外电商面临的市场竞争激烈&#xff0c;如何在海外市场获客成为了摆在许多卖家面前的难题。而在这个问题的解决方案中&#xff0c;海外云手机崭露头角&#xff0c;成为助力电商引流的新利器。 在当前市场中&#xff0c;云手机主要用于游戏挂机&#xff0c;但其潜力在海外电商领…

WebGIS航线编辑器(无人机航线规划)

无人机航点、航线规划&#xff0c;实现全自动航点飞行作业及飞行航拍。禁飞区、作业区功能保障飞行安全。 GIS引擎加载 const viewer new Cesium.Viewer("cesiumContainer", { imageryProvider: new Cesium.IonImageryProvider({ assetId: 3872 }), }); const im…

License授权的基本思路

前言 对于收费软件&#xff0c;一般是我们需要去购买一个许可&#xff0c;然后输入这个许可到软件里就能够使用软件。 于是有的小伙伴就开始好奇这个许可是怎么实现的&#xff0c;特别是在离线情况下它是怎么给软件授权&#xff0c;同时又能避免被破解的。 License 内容 一个…

碳素光线疗法——动,植物 光育实验

碳素光线疗法——动&#xff0c;植物 光育实验 碳素光线疗法&#xff1a; 中西医、民间疗法融为一体&#xff0c;提高机体自身治愈力&#xff0c;免疫力&#xff0c;改善体质和保持健康&#xff0c;有助于疾病的预防和治疗的疗法。不吃药、不打针、不手术也能得健康&#xff0c…

【笔记】以论文发表形式通俗理解 TCP/IP模型

【笔记】以论文发表形式通俗理解 TCP/IP模型 前言TCP/IP模型理论通俗理解 前言 在网络基础学习过程中&#xff0c;以前只对TCP/IP理解个字面&#xff0c;网上查一下能知道个字面意思&#xff0c;但是连起来到底是什么意思&#xff0c;还是一知半解的&#xff0c;停留在表面&am…

springboot mail 发送163邮件基础配置操作

# 发送邮件配置 spring.mail.protocolsmtps # 配置 smtp 服务器地址 spring.mail.hostsmtp.163.com #服务器的端口 spring.mail.port465 # 配置邮箱用户名 spring.mail.usernamexxx163.com # 配置申请到的授权码(刚让复制的授权码) spring.mail.passwordxxx # 配置邮件编码 spr…

算法-图的广度优先搜索,图的深度优先搜索

1.图的广度优先搜索 (1). 定义 图的广度优先搜索&#xff08;Breadth-First Search, BFS&#xff09;是一种用于遍历或搜索树或图的算法。这个算法从图的某一节点&#xff08;源节点&#xff09;开始&#xff0c;探索最靠近源节点的节点&#xff0c;然后是一层一层地向外扩展&a…

蓝桥杯B组C++省赛 全球变暖【bfs】

题目描述&#xff1a; 你有一张某海域NxN像素的照片&#xff0c;"."表示海洋、"#"表示陆地&#xff0c;如下所示&#xff1a; ....... .##.... .##.... ....##. ..####. ...###. ....... 其中"上下左右"四个方向上连在一起的一片陆地组成一座…

SurfaceFlinger的面试题目

题目&#xff1a;简述SurfaceFlinger在Android系统中的作用是什么&#xff1f; **答案&#xff1a;**SurfaceFlinger是Android图形系统的核心组件之一&#xff0c;它负责合成各个应用程序产生的图形缓冲区内容&#xff0c;并将最终的图像呈现到屏幕上。SurfaceFlinger管理多个S…

211本科搞前端好找工作吗?AI程序员对程序员的影响有多大?

211本科去搞前端好找工作吗? 最近看到网友有问到这个问题,211本科搞前端好找工作吗?那么我根据自己的工作经历来深度聊聊网上的这个问题。 本人计算机硕士,在前端领域干了7年。我毕业后先后在上市国企,上市云计算公司,现在在一家正在IPO的公司,做新能源无人驾驶赛道,…

EDIUS11新版中国首发!新增多个AI功能比Sora更强!

2024年2月27日&#xff0c;中国苏州 - 著名专业视频剪辑工具EDIUS正式推出中国地区的最新版本EDIUS11。这次发布距上一版已有3年时间&#xff0c;EDIUS11带来了一系列创新&#xff0c;开发商Grass Valley团队引入了多项人工智能功能&#xff0c;涵盖特效、调色、降噪等多个方面…

网络安全必修课:20个核心知识点大揭秘

1、什么是SQL注入攻击 概述 攻击者在 HTTP 请求中注入恶意的 SQL 代码&#xff0c;服务器使用参数构建数据库 SQL 命令时&#xff0c;恶意SQL 被一起构造&#xff0c;并在数据库中执行。 注入方法 用户登录&#xff0c;输入用户名 lianggzone&#xff0c;密码 ‘ or ‘1’’…