DLAI-多向量图像检索笔记-全-
DLAI 多向量图像检索笔记(全)
001:课程介绍 🖼️


在本课程中,我们将学习多向量图像检索的工作原理,以及如何在实际AI应用中最佳地使用它。
长期以来,让大型语言模型仅访问文本数据更为容易。但这使得大量以图像和其他多模态形式存储的有用信息无法被利用。本课程将重点介绍一系列名为“多向量”的图像检索技术。
多向量与单向量检索对比
上一节我们提到了传统方法的局限性,本节中我们来看看多向量技术的核心思想。
与标准向量检索(用单个大向量表示每个文档或图像)不同,多向量技术使用许多较小的向量来表示图像中较小片段(例如图像块)的含义。存储这些详细的向量信息后,便可以在文本查询中的词元与构成图像或文档的图像块之间进行细粒度匹配。
这种方法可以提供高质量的图像搜索,即使在包含文本、图像、幻灯片等复杂文档上也能表现良好。然而,存储大量向量数据会带来内存使用和搜索效率方面的挑战。本课程中,我们也将探讨解决这些问题的新技术。
课程内容与结构



为了帮助您理解这些多向量图像检索技术,本课程将由经验丰富的AI工程师、向量数据库公司Cohere的开发者倡导者Kasper Juul进行讲授。
以下是本课程的核心学习路径:
- 多向量检索基础:首先通过其在文本检索中的应用来学习多向量检索的基本原理。
- 图像检索应用:接着,您将了解名为ColPali的模型如何将这种方法应用于图像检索,以及一些优化其性能的技术。
- 混合方法探索:最后,我们将探索一种名为Movea的方法,它旨在结合多向量和单向量图像检索的最佳特性。
您将学习每种方法背后的基础概念,然后在一个实际运行的多模态RAG系统中实现它们。本课程将很好地融合概念性主题与实践性主题。
行业背景与展望
目前,整个行业对图像检索,特别是多向量技术,抱有极大的热情。如今人们使用的大多数LLM实际上是VLM(视觉语言模型),它们处理图像的能力与处理文本一样强。挑战在于构建能够准确、高效地检索图像数据以利用这种能力的系统。本课程中的技术,如ColPali或Movea,正在最终实现这一目标。这很可能代表该领域的未来,并将在未来几年推动许多激动人心的应用。
本课程的开发得到了DeepLearning.AI、GPT和Roboflow的贡献。在第一课中,您将通过动手实践CoT来熟悉多向量检索,这是一种广泛用于文本检索的多向量方法。


本节课中,我们一起学习了多向量图像检索的课程概述、其核心思想(使用多个小向量进行细粒度匹配)、课程的具体学习路径(从基础到ColPali再到Movea),以及该技术在行业中的重要性和前景。接下来,让我们跟随Kasper,正式开始第一课的学习。
002:多向量文本检索与ColBERT
概述
在本节课中,我们将要学习多向量检索的基本概念,特别是针对文本的ColBERT模型。我们将了解其工作原理、优势、局限性以及如何在实践中使用它。
多向量检索基础概念



上一节我们介绍了检索增强生成(RAG)的重要性。本节中我们来看看实现RAG的核心技术之一——向量检索。
向量检索回顾
检索之所以重要,是因为大型语言模型(LLM)无法在其训练数据中涵盖所有可能相关的信息。检索增强生成(RAG)通过为LLM配备一个相关信息的数据库来解决这个问题。当系统收到一个提示时,它会快速搜索数据库并检索最相关的文档。原始的提示随后会被修改以包含这些检索到的文档,使LLM能够基于这些信息生成更准确的回答。RAG可以显著提高LLM回答的质量,是目前应用最广泛的生成式AI系统之一。
例如,一个客户服务聊天机器人如果能访问你公司的产品和政策信息,会更有帮助。这可以通过将LLM与数据源配对来实现,从而检索相关信息。
双编码器(Bi-Encoder)
最常见的向量检索类型称为双编码器。知识库中的每个文档都由一个语义模型分配一个向量表示。当收到查询时,它也被嵌入成一个向量。系统随后搜索数据库,找到向量与查询向量最接近的文档。
双编码器的主要优点是速度非常快。原因之一是文档向量可以预先计算。即使你的数据库有十亿个文档,也只需要在搜索时为查询生成一个向量。搜索过程本身也极快,这得益于近似最近邻(ANN)算法的使用,它们可以轻松扩展到数十亿文档。
双编码器的主要问题在于,它本质上将每个文档和提示的含义压缩成一个单一的向量。这可能会丢失文档和提示的细微差别,并可能错过它们之间的微妙联系。
交叉编码器(Cross-Encoder)
交叉编码器采用不同的方法。查询和文档的完整文本同时输入到交叉编码器神经网络中。这使得交叉编码器能够检测查询和文档之间独特的细微关系或交互。交叉编码器随后直接输出一个相关性分数,可用于对文档进行排序。
交叉编码器往往能产生非常高质量的结果,但其扩展性很差。因为它需要完整的文档和查询文本来生成分数,无法进行预计算,并且为每个文档评分都需要通过神经网络进行一次计算量大的前向传播。因此,交叉编码器只适用于搜索少量文档(通常是几十个或更少)的场景。
交叉编码器的最佳用途是对其他技术检索到的结果进行重新排序。例如,如果你有100万个文档需要搜索,你可以先用双编码器快速找到50个最佳匹配文档,然后用交叉编码器对这些结果进行重新排序,找出5个绝对最佳匹配。
理想情况下,我们需要一种既能像双编码器一样支持预计算,又能像交叉编码器一样实现查询和文档之间深度令牌级交互的技术。多向量技术正是为此而引入的。
ColBERT:一种多向量文本检索技术
上一节我们对比了不同检索模型的优劣。本节中我们来看看最常用的文本多向量技术——ColBERT,看看它是如何工作的。
ColBERT工作原理
让我们通过一个简单的例子来看看ColBERT是如何工作的。假设这是你的文档文本。
首先,文本被分割成令牌(Token)。接着,这些令牌中的每一个都被分配了自己的嵌入向量。这些向量不仅反映了该特定令牌的含义,也反映了它在整个文档上下文中的含义。
如果这是一个双编码器,下一步将通过一个称为“池化”的过程将所有向量缩减为一个向量。最常见的池化方法是简单地平均所有这些向量。然而,在ColBERT中,你保留所有这些向量。
表示查询的过程完全相同。查询文本被转换为令牌,每个令牌被分配一个密集的语义向量。
现在,让我们对这个查询-文档对进行评分。这种方法称为“最大相似度”或简称为“MaxSim”。其核心思想是,每个查询令牌都找到最相似的文档令牌。
以令牌“puppy”为例。“puppy”的向量使用典型的向量距离度量(如点积或余弦距离)与每个文档令牌的向量进行比较。在这里,你可能会期望它与令牌“dog”匹配最紧密。然后对查询中的每个令牌重复此过程。这个查询-文档对的最大相似度分数就是所有查询令牌的最大相似度之和。
最大相似度的非对称性
MaxSim与其他距离度量的不同之处在于它是非对称的:A和B之间的距离与B和A之间的距离不同。为了理解这一点,想象一下你只需翻转上一个例子中用作文档和查询的文本。最初,MaxSim是3个点积之和,但现在它是5个点积之和。
因此,你可以利用通常为向量搜索提供动力的高度优化的近似最近邻(ANN)算法。问题出现在你尝试构建算法用于搜索的HNSW索引图时。该图依赖于知道每个文档与其几个最近邻之间的距离。由于MaxSim是非对称的,A可能是B的最近邻,但B不一定是A的最近邻。这种不一致性使得根本无法构建图,因此在使用多向量搜索时,HNSW索引通常被禁用。
ColBERT最终实现了其目标:它允许像双编码器一样预计算向量,同时查询和文档令牌之间的交互又模仿了交叉编码器。
ColBERT的实践应用与权衡
由于HNSW无法有效用于多向量检索,你很少单独使用它们来搜索较大的数据集。如果这样做,你将需要对所有文档执行暴力穷举搜索,这在规模上使用太慢。
常见使用模式:作为重排序器
使用延迟交互模型更常见的方式是作为更快技术(如双编码器)结果的重排序器。通常,使用更快的检索器进行“过采样”,意味着你检索的文档数量多于最终想要返回的数量。然后使用多向量模型作为重排序步骤,这样MaxSim只需在有限的候选集上计算。
主要缺点:内存消耗
ColBERT最大的缺点是所需的内存量。每个令牌一个向量,每个文档很容易存储数百或数千个向量。这些数字会迅速累加,尤其是在你拥有百万级规模设置的情况下(这在当今并不少见)。想象一下,你的文档有100个令牌,每个令牌获得一个128维的嵌入,每个数字由一个4字节的浮点数表示,仅向量就需要大约50KB。而双编码器每个文档只存储一个向量。即使你的向量维度非常高,每个文档通常也不会超过10KB,这使得规模更容易管理。
定位:中间地带
延迟交互模型是单向量嵌入模型和交叉编码器之间的中间地带。它们比任何其他嵌入模型需要更多内存且运行成本更高,但它们仍然允许预计算文档的表示,并且不需要为每个查询将它们通过神经网络。
最近,我们开始看到将类似方法应用于文本以外的其他模态(如图像)的巨大价值,而且它们似乎也比其前身效果更好。
动手实践:使用ColBERT进行检索
在讨论不同模态之前,让我们探索一下延迟交互模型在实践中是如何使用的。
加载模型与理解维度
我们将从加载斯坦福大学开发的延迟交互模型ColBERT V2开始。ColBERT将文本编码为每个令牌的128维向量,实现了细粒度的语义匹配。让我们将模型加载到内存中并查看其嵌入维度,它是128。
理解令牌化
在创建嵌入之前,让我们了解ColBERT如何处理文本。令牌化是将文本分解成模型可以处理的较小片段的过程。ColBERT使用WordPiece令牌化,并添加特殊令牌,如开头的[CLS](用于文档编码)和结尾的[SEP]。常规单词要么表示为单独的令牌,有时一个单词可能会被拆分成多个子词令牌。
让我们定义一个文档。我们有一个简单的辅助方法来处理令牌化过程。如果你对技术细节感兴趣,可以随时查看辅助文件中的函数定义。
让我们看看我们的示例文档是如何被令牌化的。大多数单词被表示为单个令牌。然而,“decibels”被拆分成三个单独的令牌,这些是子词级别的令牌。让我们简单地检查一下我们的文档被分成了多少个令牌。我们的文档由55个令牌组成。
创建文档嵌入
现在你理解了令牌化,让我们为你的文档创建嵌入。与产生单个向量的传统密集模型不同,ColBERT为每个令牌生成一个嵌入向量。正如你所见,我们的文档有55个令牌,所以ColBERT也应该创建55个独立的128维向量,每个对应令牌化序列中的一个令牌。注意形状,这证实了我们有55个令牌嵌入,每个由128维向量表示。
在实践中,当处理较长的文档或数据块时,你可以预期每个嵌入有数百个令牌,但原理保持不变:每个令牌一个向量。
创建查询嵌入
现在,让我们定义并嵌入你的查询,它比文档短得多。ColBERT对查询的编码方式与文档不同。它将查询填充到固定的32个令牌长度,以确保不同查询之间的一致性比较,但它也假设查询不能更长。虽然你的查询只有几个单词,但ColBERT将生成32个令牌嵌入,并使用额外的填充令牌来达到这个长度。如果你让它更长,它可能会被截断。正如预期的那样,我们得到了一个32x128维的嵌入。我们的查询相对较短。
让我们使用与文档相同的辅助方法看看查询的令牌化是什么样子。它有一个额外的参数来控制我们是为文档还是查询创建令牌化。如你所见,大多数令牌只是填充令牌,它们会自动添加到查询中。尽管如此,原始查询的每个单词都被转换成了单个令牌。
可视化延迟交互
现在,让我们看看延迟交互的实际运作。我们将通过计算每个查询令牌嵌入和每个文档令牌嵌入之间的点积来计算相似度矩阵。这将创建一个32x55的矩阵,其中每个单元格代表特定查询令牌与特定文档令牌之间的相似度。
在下一步中,你将只为每个查询令牌选择最大相似度,并将它们全部相加。我们的文档对于该查询的MaxSim分数约为17。
让我们将相似度矩阵可视化为热图。我们有另一个辅助函数可以做到这一点。每个单元格显示特定查询令牌与特定文档令牌的相似程度。蓝色表示较低相似度,红色边框用于突出显示最高分。你可以看到哪些查询术语(如“advantages”或“cars”)与哪些文档术语(如“benefits”和“fleets”)匹配最强。这种可视化有助于你理解为什么ColBERT认为某些令牌对是相关的。为了清晰起见,所有掩码令牌都已从查询中移除,但它们也贡献了最终分数。
对比:ColBERT vs. 传统密集嵌入模型
为了体会延迟交互的好处,让我们将ColBERT与传统的密集嵌入模型进行比较。你将从FastEmbed加载一个流行的嵌入模型,并将其与ColBERT一起使用,看看搜索结果在实践中如何不同。
创建支持两种嵌入的集合
现在,你将创建一个可以存储密集和多向量嵌入的Qdrant集合。你将集合名称和向量名称保留为变量,以便以后轻松引用它们。
以下是集合的真实设置。注意ColBERT的特殊配置:你启用了带有max比较器的多向量配置,这告诉Qdrant使用我们之前讨论过的MaxSim评分。你还通过将m参数设置为0来禁用HNSW图创建,因为这种数据结构无法有效用于多向量搜索。
插入示例文档
让我们创建一些关于Qdrant和SQL数据库的示例文档来测试搜索质量。现在,你将把这些文档插入到之前创建的集合中。你将使用ColBERT模型创建密集嵌入和多向量表示。
执行搜索
现在,让我们使用两种方法进行搜索。我们将创建一个辅助函数,使用ColBERT嵌入查询集合。我们传递一个查询,向量是使用你之前定义的模型生成的。请注意,你现在调用的是query_embed而不是passage_embed,因为某些模型处理查询的方式可能与文档不同。你还可以指定想要检索的结果数量。
另一个辅助函数将执行相同的过程,但使用常规的密集嵌入模型。
分析查询与结果
两种方法都已定义。让我们创建一个查询。你可以随意尝试不同的查询以查看不同的输出。
你可能想知道为什么我们特别选择了这个特定的查询。专有名称,如公司名称,经常被分成多个令牌,甚至由单个字母组成。这样的令牌无法有效表示该特定公司的业务,更像是一种后备方案。尽管我们都知道Qdrant是一个向量搜索引擎,但对于我们的ColBERT模型来说,它只不过是一个字母序列。为了确认我们的查询被分成了那么多令牌,让我们使用之前使用的相同辅助函数。
确实,“Qdrant”被分成了三个单独的令牌,这证明ColBERT在其训练数据中没有接触到该公司的名称。在密集嵌入模型中,这类令牌将贡献给最终的池化向量,但它们很可能只是一种噪声。通过令牌级交互,你可以预期此类多令牌序列在文档和查询之间得到更好的匹配。
运行搜索并比较结果
是时候使用两种方法运行搜索来检索结果了。在这里,你可以看到生成ColBERT密集向量花了多长时间,以及返回结果花了多长时间。我们有另一个辅助函数,可以简单地可视化比较两种方法的搜索结果。
ColBERT令牌级匹配的一个关键优势是它能够捕获特定术语,如专有名称。在这种情况下,Qdrant比池化的密集向量更好,因为单个令牌在整个匹配过程中保留了它们的语义身份。
注意ColBERT和密集嵌入结果之间存在差异,但很难说谁是赢家。有重叠,但通常仅凭肉眼观察是不够的。在真实案例中,我们构建参考数据集以确保返回的结果是高质量的。
从这个简单的例子中我注意到的是,所有ColBERT结果都提到了Qdrant,而密集结果则不然,这有点出乎意料,因为我们的查询提到了Qdrant。
可视化贡献度
让我们检查每个查询令牌对ColBERT检索到的最佳结果的分数贡献了多少。我们可以使用之前用过的相同热图。为此,我们需要文档和查询的令牌化以及它们的ColBERT表示。相似度矩阵是最后一个缺失的部分。既然所有组件都已就绪,让我们最终显示热图。
“perform”和“performance”是最接近的邻居,这并不奇怪。然而,“Q”、“drant”和“t”也对最终的MaxSim分数有相当显著的贡献。因此,即使ColBERT不理解Qdrant是什么意思,令牌级交互也有助于实现关键词匹配。
总结
本节课中我们一起学习了多向量文本检索技术,特别是ColBERT模型。我们了解到:
- ColBERT通过为每个令牌生成独立的向量,实现了比双编码器更细粒度的语义匹配。
- 它使用“最大相似度”评分机制,结合了双编码器的预计算优势和交叉编码器的深度交互能力。
- 其主要优势在于能更好地处理专有名词和复杂短语,但代价是更高的内存消耗和计算成本。
- 在实践中,ColBERT常作为快速检索器(如双编码器)之后的重排序器使用,以平衡速度与精度。
- 通过代码示例,我们实践了如何使用ColBERT进行文档嵌入、查询处理、相似度计算和结果可视化。

这只是对文本多向量技术的一个非常快速的介绍。让我们在下一课中看看这些技术对于图像是什么样子的。
003:ColPali多向量图像检索方法
在本节课中,我们将学习如何将多向量方法应用于图像检索。我们将介绍ColPali模型,它能够直接处理图像文档,无需复杂的布局检测和OCR步骤,从而实现高效的多模态搜索。
概述

上一节我们介绍了多向量方法在文本检索中的应用。本节中,我们将看看如何将类似的方法应用于图像检索。图像检索非常重要,因为大量有用信息并非以文本形式存储,而是存在于图像文件中,例如扫描文档、PDF或幻灯片。新一代的视觉语言模型能够同时接受文本和图像作为输入,因此,只要检索到正确的文档,就能构建强大的多模态RAG系统。挑战在于如何完成检索步骤。

图像检索的挑战
文档是最常见的需要检索的图像类型,但它们给检索带来了许多挑战。幻灯片或文档页面通常是文本、图像、图表和表格的复杂混合体。
以下是传统方法的处理流程:
- 首先使用专用模型检测文档的布局。
- 然后分别解析每种元素类型。

这种方法需要构建复杂且容易出错的自定义系统。更糟糕的是,针对一种文档的检索流程通常无法适用于另一种。
ColPali方法简介
ColPali是一种多向量图像检索方法,它解决了上述许多问题。ColPali代表“Contextualized Linear Direction over PaLI-JAMA”。PaLI-JAMA是谷歌创建的一个视觉语言模型。然而,ColPali这个名字也常用来指代基于相同原理构建的整个模型家族。
ColPali是构建在视觉语言模型之上的多向量嵌入模型,可以同时接受图像和文本作为输入。这类似于ColBERT,它既是一个特定模型,也常用来指代整个多向量文本检索技术家族。
ColPali具有许多优点:
- 它跳过了复杂且容易出错的格式检测步骤。
- 它灵活适用于所有文档类型。
- 虽然运行成本可能较高,但从长远来看,由于系统依赖单一模型而非传统方法所需的多个模型,最终可能更便宜。
视觉语言模型如何工作
让我们看看视觉语言模型如何被改造用于检索,然后将其应用于实践。
典型的纯语言模型工作流程如下:
- 输入文本传递给分词器。
- 分词器将其拆分为称为“标记”的较小单元,并创建标记ID序列,然后传递给模型。
- 实际上,模型将这些ID转换为训练期间学到的相应标记嵌入。这些向量是静态的,并且特定于每个标记。
- 语言模型将序列通过其自身的层,并生成考虑整个序列的当前标记的上下文化嵌入。
- 该向量通常用于生成逻辑值,然后是词汇表中每个标记作为序列延续的概率。
视觉语言模型在用于文本生成时与此并无不同。然而,添加另一种模态需要对该架构进行一些扩展。
视觉是一种完全不同的模态,因此无法直接转换为文本。因此,它有自己的预处理流程,显然从整张图像开始。总体目标是以某种方式处理图像,使其像文本一样作为嵌入序列输入到语言模型中。
以下是图像处理流程:
- 图像通过一个额外的Transformer进行处理,该Transformer首先将其转换为特定大小的图像块。
- 单张图像变成一个块序列,而不是单个输入。这类似于文本被处理为标记序列,而不是整个文档。
- 图像输入通过一个视觉Transformer神经网络,它再次产生一个上下文化的向量嵌入序列,每个输入块对应一个,通常维度很高。
- 输出必须被投影到一个更小的维度空间,然后传递给语言模型。最后一步的原因是使图像向量具有与文本向量相同数量的维度。
总而言之,这个额外的过程使得不同的模态能够被传递到同一个网络。无论输入模态如何,语言模型都会产生一个嵌入,该嵌入将在生成过程的后期用于生成每个标记的逻辑值和概率。
因此,与纯语言模型相比,典型的视觉语言模型为图像添加了一些额外的处理,同时保持文本处理不变。VLM仍然在给定输入的情况下预测下一个标记。所以,我们还没有完成嵌入生成。我们不想要生成系统,而是想要以向量嵌入形式表示检索输入的东西。
生成向量嵌入
为了产生向量嵌入,需要稍微扩展VLM的逻辑,并在语言模型从其最后一层生成隐藏状态后立即添加另一个投影层。这个投影层减少了嵌入的维度以便高效存储。对于Gemma模型,它从2048维减少到128维,但不同模型之间可能有所不同。没有使用池化变体。
因此,最终,整个输入序列,无论模态如何,都通过该网络进行转换,你会得到一个代表你输入的相同长度的序列。
一般来说,仅仅开始使用VLM进行向量检索是不够的。需要对最终的投影层和语言模型参数进行一些额外的训练。许多论文建议对语言模型的注意力权重使用低秩适配器。
低秩适配向冻结的基础模型添加低秩矩阵。“低秩”一词指的是维度复杂性,其中低秩意味着矩阵可以表示为两个较小矩阵的乘积。
例如,与其训练一个完整的2048x2048权重矩阵(需要超过400万个参数),LoRA使用两个大小为2048x32和32x2048的矩阵(仅需要约13.1万个参数),减少了97%。训练过程调整整个模型以进行有效检索,但由于这些优化,它比完全参数调优更容易、更快。
实践:使用ColPali
实际上,你不需要知道所有这些细节就可以开始使用ColPali。你将首先根据可用硬件加载适当的模型。虽然ColPali功能更强大,但它需要大量内存才能运行,甚至需要GPU。因此,你也可以使用Cosmo,这是一个拥有2.56亿参数的较小变体,你可以在DeepLearning.AI平台上使用。你也可以在具有完整ColPali模型的不同环境中运行它,以查看差异。
无论你最终使用哪个模型,都需要一个处理器来处理原始数据并将其转换为模型可以处理的表示形式。
让我们加载著名论文《Attention Is All You Need》中的一页,看看ColPali如何处理视觉文档。这个PDF页面包含图表、方程和大量文本。你还需要使用Pillow加载图像并检查其尺寸。这个高分辨率截图保留了原始PDF的所有视觉细节。
ColPali通过将图像划分为块网格来处理图像,类似于视觉Transformer的工作方式。对于ColPali v1.3,这始终是一个固定的32x32网格。对于Cosmo,网格尺寸会根据图像的宽高比进行调整。
让我们通过在各块之间添加一些间距来可视化这个特定图像是如何被划分的。你有一个辅助方法来完成这个。
现在,你将通过ColPali处理器处理图像。该处理器将图像转换为包含输入ID、注意力掩码和像素值的张量批次,模型可以理解这些。处理后的批次包含因模型而异的输入ID:对于ColPali是1024,对于Cosmo是1039。让我们解码这个,看看ColPali在内部如何表示图像。
注意,该序列包含1024个特殊的图像标记,代表图像的不同块,后面是指令标记。ColPali将图像作为块网格进行处理,为每个块创建嵌入。对于ColPali,它始终是一个固定的32x32网格,而对于Cosmo,网格尺寸根据图像大小而变化。
让我们为图像生成嵌入。你最终会为每个标记得到一个128维向量。对于搜索,我们可能更倾向于与图像标记对应的嵌入,而不是指令标记。使用辅助函数提取图像掩码,我们隔离这1024个图像块嵌入。这个数字对于ColPali是固定的,对于Cosmo可能不同,但巧合的是,创建的块数完全相同。
ColPali的可解释性
ColPali的一个强大特性是其可解释性。我们可以精确地可视化图像的哪些部分与特定查询标记最相关。与产生单一相似性分数的传统密集嵌入不同,ColPali的标记级嵌入允许我们看到查询中的每个单词如何与图像的不同区域相关联。这使得模型的推理过程透明,并帮助我们理解为什么检索到某些文档。

让我们通过创建一个查询并可视化它如何匹配文档的不同部分来探索这个功能。我们将创建一个关于Transformer架构的查询。《Attention Is All You Need》论文的这一页包含著名的Transformer图表,因此我们期望ColPali在处理我们的查询时突出显示这些特定区域。
你将通过模型处理查询以获得查询嵌入。每个查询标记也由一个128维向量表示,但查询只创建了21个标记。让我们通过移除填充和增强标记来稍微清理一下,然后重新分词,以便我们可以看到各个查询标记以进行进一步分析。
为了创建我们的相似性可视化,我们首先需要计算图像被划分成了多少块。你还将提取图像掩码以隔离仅图像标记嵌入,排除提取标记。现在,你将使用ColPali可解释性工具生成相似性图。这将计算每个查询标记与每个图像块之间的相似性,为查询中的每个标记创建一个热图。
让我们可视化特定查询标记的相似性图。你将选择查询中的“layer”标记,以查看图像的哪些部分它最强烈地关注。这个可视化将在原始图像上叠加一个热图,其中较暖的颜色表示ColPali认为与该特定单词最相关的区域。


这个可解释性功能对于调试和理解你的检索系统非常有价值。你可以识别模型是否关注正确的内容,发现意外的匹配模式,并建立对系统决策的信任。在生产应用中,这种透明度还可以帮助向最终用户解释检索结果。
然而,由于我们在这里使用较小的Cosmo模型,你可以看到模型似乎关注了一些背景块,这有点出乎意料。让我们看看完整的ColPali模型会是什么样子。这是相同相似性图在更大的ColPali模型上的样子。Cosmo模型进行了一些像素混洗,所以模型关注了一些随机位置。但对于ColPali,你可以看到模型主要关注页面上的“layer”一词多次,这是预期的,因为这是你正在可视化的标记。
构建多模态搜索系统
让我们使用Qdrant构建一个实用的多模态搜索系统。我们将使用MaxSim比较器设置一个支持向量的集合,以处理ColPali的每个文档多个嵌入。你还需要禁用HNSW图创建,因为Qdrant无论如何也无法有效使用它。
现在,我们将为《Attention Is All You Need》论文的所有页面建立索引。load_precomputed标志设置为True,这将加载每个页面的预计算向量数据,而不是实时计算。然后,每个页面的向量被上传到Qdrant,并将图像文件路径作为元数据记录。
让我们创建一个辅助搜索函数,用于处理文本查询并为它们创建ColPali嵌入。同一个函数还将查询你刚刚创建的集合以获取最接近的匹配项。由于我们正在处理视觉数据,我们有另一个辅助方法在屏幕上显示搜索结果。
让我们搜索“model architecture”,看看模型认为论文的哪些页面是最佳匹配。我们询问模型架构,所以这个最著名的图表首先出现并不奇怪,但论文中也有一个名为“Model Architecture”的特定部分,因此它也被选为回答该特定问题的最佳页面之一。
接下来,让我们搜索“scaled dot product attention”。同样,返回的第一个结果中的图表正好展示了这一点,但文档中也有一个名为“Scaled Dot-Product Attention”的部分,也被ColPali检索捕获。
最后,让我们搜索“experiment results”。这里的目的是检索包含表格和性能指标的页面,我们期望论文中包含这些内容。ColPali似乎同时理解文本内容和视觉元素,如表格和图表。令人惊讶的是,整篇论文的第一页被返回为最佳匹配,尽管它似乎不包含任何总结实验的表格。然而,实验结果可能在摘要或文章的脚注中被提及,所以这并不奇怪。尽管如此,第二和第三个匹配项都包含一些总结该方法与其他方法效率的表格,这实际上是我们期望这类系统返回的内容。
总结
本节课中,我们一起学习了ColPali多向量图像检索方法。ColPali无需OCR步骤或复杂的文档处理,即可实现对视觉文档的强大多模态搜索。通过直接将文档作为图像处理,它能自然地捕获文本和视觉语义。

然而,多向量表示也存在一定的局限性。假设每个文档需要0.5MB,而你有一百万个文档,最终可能仅用于搜索的ColPali嵌入就需要500GB内存。这对于拥有数千个文档的小规模项目来说可能没问题,但在实践中,你经常需要处理数十亿个文档,这将需要大量的内存来运行。如果你的公司有大量数据需要搜索,检索系统中有一百万个文档是很有可能的。通常,这些向量会包含大量冗余信息,一些有趣的优化技术可以帮助你减少它们的RAM消耗。我们将在下一课中讨论这些方法。
004:优化多向量表示的检索效率 🚀

在本节课中,我们将学习如何优化多向量图像检索系统,以减少其内存占用,同时尽可能保持检索质量。我们将探讨量化、池化和层次化聚类等多种技术。
概述
Copilot 技术的一个主要缺点是,为了存储每个文档的所有向量,它需要比其他技术多得多的内存。


因此,需要使用优化技术来减少 Copilot 的内存占用。让我们看看这些方法是什么。
优化技术概览
以下是三种主要的优化方法:
- 标量或二进制量化:将每个向量中的浮点数转换为压缩格式,如四比特整数甚至一比特。
- 行或列池化:将图像块按行或列分组,并通过平均池化每组中的向量来创建单个向量。
- 层次化池化:这是一种更智能的行或列池化,将具有相似嵌入向量的图像块分组。

加载文档嵌入
上一节我们介绍了 Copilot 如何将 PDF 页面转换为截图并生成多向量嵌入。本节中,我们将使用一个辅助函数,它在内部处理所有这些步骤:PDF 转换、图像加载和嵌入生成。由于这些细节已在上一课中介绍,我们可以专注于本课的核心——优化技术。
为了课程流畅,我们将使用预计算的嵌入,但您也可以将 load_precomputed 设置为 False 来查看整个过程。
我们将加载 Copilot 处理器,以演示 Copilot 如何将文档图像结构化为图像块网格。这将帮助您理解稍后将探讨的空间池化技术。
另一个辅助函数处理所有复杂性:要么从 Parquet 文件加载预计算的嵌入,要么通过转换 PDF、加载 Copilot 模型和批量处理图像来重新生成它们。
load_or_compute_image_embeddings 函数返回一个包含图像路径和已转换为 NumPy 数组的嵌入的 DataFrame。
以下是部分条目的样子。显然,由于我们处理的是图像,我们也可以在屏幕上显示它们。这些幻灯片来自不同的深度学习 AI 课程,因此,如果您在完成本课程后曾学习过其中任何一门,您将可以直接向幻灯片提问。
每页生成大约 1000 个令牌嵌入,这是大量需要存储和搜索的向量。但让我们看看单个嵌入向量的形状。
每个图像的向量数量始终是 1031 个。现在,我们可以逐一实现不同的优化技术,并最终在同一数据集上比较它们。
标量与二进制量化
在计算存储向量所需的内存时,一个常见因素是每个单独维度的大小。标准嵌入使用每个维度 4 字节的 float32 值,但如果您能在不牺牲搜索质量的情况下减小这个大小,您将拥有一种节省大量 RAM 的简单方法。
标量和二进制量化技术旨在使用一字节整数,甚至每个维度仅用一比特来表示向量,它们被广泛用于来自编码器的常规密集嵌入。
标量量化将 4 字节的 float32 值压缩到仅 1 字节的 8 位值,实现了 4 倍压缩。
标量量化的原理是将连续的浮点数范围映射到离散的整数桶。对于每个维度,量化器学习整个数据集的最小值和最大值,然后将此范围线性映射到 0 到 255 的整数空间。
例如,如果维度 5 在所有向量中的范围是 -0.8 到 1.2,那么值 0.2 将映射到大约 128。这使得标量量化是数据集感知的。它需要分析所有向量(或至少一个代表性样本)以确定最佳范围。
幸运的是,像 Quadrant 这样的向量搜索引擎在内部处理此过程。我们只需在集合级别配置量化,发送原始的浮点嵌入,引擎就会自动处理压缩和范围校准。
二进制量化进一步压缩,将每个 float32 维度从 4 字节减少到仅 1 比特,实现了惊人的 32 倍压缩比。
过程很简单:正值变为 1,而负值或 0 变为 0。这种极端的压缩将相似性计算转换为高效的按位运算。
当嵌入以 0 为中心且分布相对对称时(这在归一化的神经网络输出中很常见),二进制量化效果特别好。然而,这种激进的压缩确实会带来权衡。
与标量量化一样,Quadrant 在内部处理二进制量化。我们在集合级别配置它,并发送原始浮点嵌入,让引擎执行二进制转换。
池化方法
池化方法可以利用 Copilot 的空间结构。回想上一课,Copilot 将文档图像处理为 32x32 的图像块网格。
行池化平均网格中每一行的嵌入,而列池化平均每一列的嵌入。这保留了空间关系,同时将向量数量大幅减少到仅 32 个。
让我们提取图像块的嵌入。处理器从 32x32 的网格中创建一个包含 1024 个图像块令牌的序列,外加一些用于模型架构的空间令牌。
对于池化方法,我们不需要这些额外的令牌,因此我们可以使用处理器的图像掩码来仅提取与图像相关的令牌位置。
通过使用此掩码过滤到仅图像块令牌,我们可以将 1024 个嵌入的扁平序列重塑为 32x32x128 的网格。
让我们创建一个辅助函数来完成这个操作。在这个 32x32 网格中的每个位置对应于原始图像中的一个图像块位置,每个图像块都有一个 128 维的嵌入。
让我们在第一个文档的嵌入上测试您的重塑函数。您刚刚应用了 embeddings_to_grid 函数来验证它是否正确地将掩码嵌入转换为预期的 32x32x128 维结构。
一旦我们的嵌入处于网格形式,您就可以将行和列池化实现为简单的平均操作。
行池化平均网格的 32 行中的每一行,产生 32 个代表向量,捕捉文档中的水平模式。类似地,列池化平均 32 列中的每一列,捕捉垂直模式。两种方法都将向量总数从 1024 个减少到仅 32 个。
仅看这些数字不容易判断什么,但为了确认形状正确,您将在创建的网格上运行行平均池化,它确实是 32x128 维。列平均池化也是如此。调用方法后,我们可以看到形状符合预期大小。尽管如此,我们不需要花太多时间看这些我们无法解释的数字。
层次化令牌池化
层次化令牌池化使用聚类来智能地分组相似的嵌入。该算法对令牌嵌入执行层次聚类,然后将每个聚类平均为一个代表性向量。
聚类由相似的图像块创建。因此,如果我们有很多相同颜色的背景块,它们应该被分组在一起。Copilot 引擎库提供了该技术的实现,让我展示一个简单的工作原理示例。
假设您在一张有 9 个图像块的图像上运行层次化令牌池化。每个图像块都有自己的嵌入,因此我们的多向量表示是一个包含 9 个向量的序列。如果池化因子设置为 2,您将创建 4 个聚类(因为 9 / 2 = 4.5,向下取整为 4)。我在这里假设,但很可能包含耳朵的两个图像块最终会在同一个聚类中。类似地,仅包含毛发的图像块彼此也非常相似,因此它们也很可能被分组在一起。一个有眼睛的图像块非常独特,因此很可能是一个单图像块聚类,依此类推。
图像块不必是连续的才能形成一个聚类。实际上,池化技术接收一个没有空间关系信息的嵌入序列,因此甚至无法做到这一点。您使用的 Copilot 引擎库提供了此技术的实现,因此您只需创建一个名为 hierarchical_token_pooling 的辅助函数,以便更简单地在我们的原始嵌入上使用它。
让我们在数据集中的一个示例上运行它,看看使用池化因子为 2 的此方法能节省多少内存。您已将 1031 个向量减少到 515 个,通过聚类保留了语义信息,同时将内存使用量减少了近一半。
实验与比较
现在到了关键的实验:在真实的向量数据库中比较我们所有的优化策略。我们将在单个 Quadrant 集合中创建多个命名向量,每个代表一种不同的优化方法。这允许您直接比较所有策略的检索质量和内存使用情况。
原始嵌入、二进制量化、不同因子下的层次化池化以及空间池化。我们将回答关键问题:在不牺牲检索质量的情况下,我们能节省多少内存?
首先,确保集合不存在。
您将在单个集合中设置多个命名向量。每个向量将使用具有最大比较的多向量配置,但池化的向量数量将根据策略而变化。对于量化方法,我们将在相应的命名向量上启用它们,以便与原始方法进行比较。
现在,让我们将所有嵌入插入到集合中。每个文档将有七种不同的向量表示,使我们能够并排比较检索性能。
辅助方法加载预计算的向量数据,并将使用的幻灯片组限制为整个列表的一个子集。为了避免将所有向量加载到内存中,辅助函数逐个生成示例。
我们在所有已实现的优化技术上运行相同的查询,以比较检索质量。辅助函数处理所有复杂性:它将根据 load_precomputed 标志加载预计算的查询嵌入或重新计算它们。查询、模型和处理器只是在辅助函数内部创建的。
现在,让我们使用每种向量配置进行搜索,并并排比较结果。我们将为每个查询和优化策略检索前三个文档。因此,让我们定义优化的顺序,以便我们可以轻松引用它们。
我们不会对查询应用量化和池化,因为它们在序列长度方面已经有限。对于查询,行和列池化没有意义,因为文本中没有空间关系。此外,Quadrant 在内部处理量化。因此,在所有情况下,调用的 Quadrant API 都直接使用查询嵌入。
因此,我们有一个辅助方法,它在所有向量上运行相同的查询,并将它们呈现为指标。让我们用实际的文档图像并排可视化结果。这种比较显示了带有颜色编码的精确度指标,以快速识别哪些优化保持了检索质量。
这里的精确度衡量了检索到的文档与基线(即原始嵌入)返回的文档匹配的百分比。这显示了每种优化在多大程度上保持了检索准确性。
我们的第一个查询 “coffeeoff Mac” 返回这组文档作为前三个匹配项。标量量化、二进制量化和层次化令牌池化都能够返回相同的结果集。有时顺序略有不同。尽管如此,精确度并不衡量文档的顺序,它只关注是否相关。然而,对于列池化和行池化,我们得到的结果集略有不同。我们可以清楚地看到,至少列池化在这方面做得不是很好。
由于您只有三个查询,您可以逐个运行它们。是时候进行第二个查询了。同样,标量量化能够返回相同的文档集,但所有其他方法都遇到了困难,尤其是列池化甚至无法选取一个被原始 Copilot 嵌入标记为相关的文档。
最后但同样重要的是,让我们检查最后一个查询 “one learning algorithm”。标量量化仍然提供与原始 Copilot 嵌入完全相同的结果集。类似地,层次化令牌池化能够返回与原始基线向量相同的结果集。不幸的是,行池化和列池化似乎都不太有效,至少对我们的数据集来说是这样。
然而,在实践中,我们不看特定情况下的检索质量,而是全局计算所有测试示例的指标。所有测试方法的平均精确度@5 如下所示。列池化和行池化表现不佳,因为它们很少能选择最相关的文档,正如您在笔记本示例中看到的那样。层次化令牌池化效果明显更好,而且池化因子似乎甚至没有太大影响,因此可能可以进一步增加它。
令人惊讶的是,简单的标量量化方法能够始终返回与基线相同的文档,使其成为一种有前途的方法,不需要任何预处理,因为它只能在集合级别配置。二进制量化虽然不是最好的方法,但考虑到其对内存使用和处理速度的巨大可能影响,在某些情况下可能会被考虑。
值得一提的是,您可以组合多种不同类型的内存优化。标量量化也可以在应用层次化令牌池化后为嵌入启用,如果您看到质量仍然可以接受的话。尽管如此,真正的基准测试不应只关注三个精心挑选的示例,而应更广泛。评估是关键,并且取决于您正在处理的数据集。尝试不同的技术,看看哪些组合在您的数据中效果最好。

总结

本节课中,我们一起学习了如何优化多向量图像检索系统的内存占用。我们探讨了三种主要技术:标量与二进制量化、行/列池化以及层次化令牌池化。通过实验比较,我们发现标量量化在保持检索质量的同时能有效压缩内存,而层次化池化则通过智能聚类在减少向量数量方面表现优异。实际应用中,应根据具体数据集评估和组合这些技术,以达到最佳平衡。
005:Muvera嵌入


在本节课中,我们将学习一种名为Muvera的技术,它旨在解决多向量表示在近似最近邻搜索中的核心挑战。我们将了解Muvera如何将可变长度的向量序列转换为固定维度的嵌入,从而解锁高效的HNSW搜索。
概述
上一节我们介绍了ColPali多向量嵌入及其内存占用问题。本节中,我们来看看Muvera方法,它通过将多向量转换为固定维度的单向量表示,解决了ColPali无法与HNSW索引高效结合的根本缺陷。
Muvera的核心思想
Muvera的核心思想是将可变长度的向量序列转换为固定维度的嵌入。这使得我们可以使用与常规稠密向量搜索相同的近似方法。
ColPali无法使用HNSW搜索,因为无法为“最大化”这类非对称相似性度量构建HNSW索引。因此,ColPali需要线性时间的暴力搜索技术。如果你想用ColPali搜索一百万个文档,就需要计算一百万次最大化操作。
Muvera采用不同的方法,它将向量序列转换为高维度的单向量嵌入。这使得可以使用HNSW索引,其搜索时间是对数级别的,可以轻松扩展到数百万甚至数十亿个文档。这使得它比原始的多向量表示更实用。
Muvera算法步骤
以下是实现Muvera技术所需的步骤。
第一步:基于SimHash的聚类
Muvera算法首先对多向量嵌入执行聚类算法。Muvera论文建议使用局部敏感哈希方法,即SimHash。
当我们的每个标记(token)被映射到一个向量空间(在我们的例子中是二维空间)时,SimHash会生成一些随机超平面,将该空间分割成两部分。每个向量将根据超平面的定义落在其中一侧。
然而,一个超平面是不够的。SimHash会生成K个超平面,将空间划分为2^K个桶(bucket)。我们可以使用0或1来为每个桶分配一个二进制字符串,以指示其位于对应超平面的哪一侧。桶ID的长度正好是K位。在我们的例子中,K=2,正好有四个不同的桶,因为两位可以表示四个值。
第二步:文档与查询处理
在为每个输入标记嵌入分配桶之后,我们将属于同一簇(即同一桶)的项分组在一起。SimHash旨在通过为每个桶找到一些代表性向量来减少序列的可变长度。
文档和查询的处理方式略有不同。
- 对于文档:SimHash会取属于特定簇的所有标记向量的平均值。由于某些桶可能是空的,它还会用汉明距离(Hamming Distance)最近的非空簇的值来填充它们。直观地说,汉明距离为1意味着两个簇ID仅相差一位。在这个过程结束时,每个簇将由一个单独的向量表示。这意味着无论你有多少个输入向量,最终总会得到2^K个代表性向量。
- 对于查询:与取平均值不同,我们对同一簇中的所有向量进行求和。这意味着,向量数量越多的簇,其生成的向量幅度越大。这样做的目的是保留查询项的自然分布,这可能有利于检索。与文档不同,查询不需要填充空簇。查询通常比文档短,因此某些簇更可能为空。填充它们可能会引入噪声并扭曲表示,因为每个项可能会对点积贡献多次。因此,如果某个特定簇中没有标记,它将被表示为零向量。
无论处理文档还是查询,现在每个由SimHash过程创建的簇都有一个单独的128维向量(128维是多向量表示中单个向量最典型的长度)。
通常,SimHash过程中的超平面数量K至少设置为5,这给我们32个簇(2^5=32)。每个簇向量128维,总共是4096维,维度相当高。
降维:随机投影
向量之间的距离对于语义搜索至关重要。当你在非常高维的空间中处理数据时,处理会变得困难,但你可以缩小数据维度而不会丢失太多信息。
随机投影是一种巧妙的技巧,它使用一个填充了随机值(通常只是+1或-1)的矩阵,将高维数据投影到低维空间。这个简单的方法可以保持点之间的距离几乎不变,这要归功于约翰逊-林登斯特劳斯引理。该引理保证,如果你选择足够的维度,就可以安全地降低数据维度。因此,只需一点随机数学运算,你就能在保持数据形状的同时,使其更易于处理。
所以,当你有一个应用了SimHash后的向量矩阵时,你将其乘以另一个形状为(原始维度 × 目标维度)的、由+1/-1组成的矩阵。结果,你将每个簇向量的维度降低到你想要的目标维度。就这么简单。
随机投影可以将平均相似度误差保持在非常低的水平,即使你显著减少了维度数量。图表展示了对于10000个随机生成的、每个维度为4096的向量,误差是如何变化的。这是一个有趣的方法,不仅对Muvera有用,也可以用于降低任何向量的维度。图表显示,在4000维、1000维时误差保持不变,直到接近100维时误差才显著增加。这意味着你可以显著降低数据的维度,同时只在相对距离方面引入最小的误差。
生成最终单向量表示
此时,你仍然拥有多向量表示,但此时它始终是相同长度的序列。每个簇向量的维度等于随机投影的目标维度。然而,HNSW搜索只需要一个向量,所以我们只需按照簇ID定义的顺序将它们全部连接起来。
最终,你会得到一个长的单向量表示,但你可以通过修改随机投影的参数来控制它的长度。
处理随机性:重复过程
此时,你可能已经意识到Muvera严重依赖于随机过程。如果你不够幸运,SimHash超平面可能会以某种方式分割空间,导致两个标记即使有很多共同点,也可能落在超平面的不同侧。例如,“狗是唯一会叫的动物”这句话中的标记,可能由于随机性而被分到不同的桶中。这就是为什么Muvera假设整个过程会重复多次。这既适用于SimHash,也适用于随机投影。
重复次数是你可以控制的另一个过程参数。通常,你将其设置为至少10次。一旦你重复了这个过程,就将所有嵌入连接起来,这就是你的最终向量。
实践应用
通常,无需从头实现这个过程,你可以依赖现有的实现。让我们看看它在实践中是如何工作的。
我们将为生产环境中的ColPali嵌入配置Muvera。你将使用k=6来创建64个簇(2^6=64),随机投影会将每个簇向量从128维压缩到仅16维。整个过程将重复20次。每次重复使用不同的随机超平面进行SimHash,以及不同的随机矩阵进行随机投影,从而创建多样化的表示,然后将它们连接在一起。
这给我们每个文档带来:64个簇 × 16维 × 20次重复 = 20480维。这比典型的稠密向量要大,但它实现了比完整多向量搜索更快的检索,同时保持了高精度。
请注意,你显式地设置了随机种子值。由于Muvera基于随机性,这样做是为了确保结果可重现。所有文档和查询都必须通过相同的SimHash超平面和随机投影矩阵进行处理,否则它们将无法相互兼容。
在真实数据集上应用Muvera
现在让我们将Muvera应用到真实文档中。你将使用与上一课相同的PDF截图数据集。这些是讲义笔记的页面,每个都用ColPali的多向量嵌入进行了编码。
相同的辅助函数将处理加载这些预计算的嵌入,或在需要时重新计算它们。这些嵌入每页包含超过1000个标记向量,是进行Muvera压缩的理想候选。
是时候将这些多向量嵌入压缩成固定维度的编码了。我们将通过我们的Muvera实例处理每个文档。process_document方法处理这个完整的流程:SimHash聚类、平均簇内向量、应用随机投影后连接所有簇向量。这将每个页面的1031个可变长度向量转换为单个20480维的固定维度嵌入。
现在,让我们准备搜索查询。我们仍然使用与上一课相同的三个查询,并使用Muvera的查询流程处理它们。辅助函数将类似地加载或创建查询嵌入。
现在我们可以通过相同的Muvera过程处理这些查询。但请注意,你调用的是process_query而不是process_document,因为在Muvera算法中,我们处理查询的方式略有不同。
建立检索对比实验
现在到了激动人心的部分:创建一个Qdrant集合,将两种表示并排存储。我们将设置两个命名向量:
- 一个用于原始的ColPali多向量,使用最大化比较,并禁用HNSW。
- 一个用于Muvera固定维度编码,作为启用了HNSW的常规单向量。
这种设置允许你直接比较两种方法的检索速度和准确性。
让我们将所有文档页面插入到我们的集合中,为每个页面提供两种表示。这使得Qdrant可以使用任一方法进行索引和搜索。
一旦你将向量上传到Qdrant,它们立即可用于搜索。然而,HNSW图可能仍未就绪,优化器可能在后台构建它。在生产环境中,你很少关心这一点,但在这里我们确实想测试Muvera嵌入的近似搜索。在循环中检查集合状态直到变为绿色,是确保一切就绪的简单方法。
对比检索结果
是时候揭晓真相了。让我们比较ColPali原始多向量和Muvera压缩表示之间的检索质量和速度。我们将用两种方法进行搜索,并检查它们检索到的文档。Muvera应该能提供显著更快的搜索速度,同时保持高精度。
我们将定义三个简单的辅助函数,使我们的研究实验清晰易懂。让我们从ColPali向量的辅助函数开始。每个辅助函数都将返回结果和计时。然后,一个比较函数将计算精度和加速指标。这种分离使得可以独立运行每次搜索并理解发生了什么。
下一个辅助函数将仅使用Muvera嵌入执行搜索。
让我们通过独立使用两种方法搜索来测试第一个查询。我们将从ColPali的原始多向量搜索(我们的基线)开始,然后通过Muvera的压缩向量运行相同的查询,最后比较结果以查看精度和加速指标。
如你所见,Muvera平均快了18倍,这使其成为提高多向量搜索速度的良好候选。
分析查询结果
以下是ColPali检索到的文档的样子。以下是Muvera能够检索到的内容。我们查看它是因为该查询报告的精度只有40%。
现在,让我们按照相同的模式测试第二个查询。Muvera仍然明显更快,但精度相对较低。如果你有兴趣查看单个结果,它们就显示在这里。
最后,让我们测试第三个查询以完成我们的比较。尽管Muvera再次比原始ColPali嵌入快18倍,但0%的精度使其在真正关心搜索质量时变得没有意义。以下是搜索结果的样子。你可以清楚地看到,Muvera并没有真正能为我们的查询找到最佳匹配。
总结性能与混合策略
让我们计算所有查询的平均性能,以了解Muvera压缩的整体影响。平均而言,Muvera比原始ColPali向量表示快17倍。然而,与原始ColPali嵌入相比,精度损失巨大。
在生产环境中,最佳方法是将两种方法结合在一个高效的查询中。使用Muvera进行快速的初始候选文档检索,然后使用原始的ColPali多向量对这些相同的文档进行重新排序以获得最高精度。
让我们创建另一个辅助函数。Qdrant的预取机制让你可以在一个API调用中实现整个流程。Muvera检索比预期更多的候选文档(正好是10倍),然后ColPali对它们进行排序以获得恰好N个结果。这为你提供了单向量搜索的速度和延迟交互模型的精度,因为在一个小子集上运行最大化操作足够快。
如果使用Muvera进行预取并只获取我们想呈现给用户的那么多文档,意义不大。如果你使用ColPali进行重新排序,那么它只能对给定的文档重新排序。因此,如果Muvera没有返回可能的最佳匹配,它只会对这个次优集合定义一个顺序。
测试两阶段检索
让我们使用ColPali多向量以及Muvera加ColPali排序的两阶段检索来运行同一组测试。我们将从第一个查询开始。你肯定能看到,我们获得的加速并不那么惊人。然而,精度稍好一些,因为目前对于“咖啡机”查询,精度达到了80%。
ColPali的结果没有改变。然而,我们可以预期两阶段检索会得到稍好的结果,事实上,它确实做得更好了一点。让我们测试第二个查询。你每次尝试都能得到更好的结果。你应该预期加速是相当一致的。但这次我们得到了理想的精度。这意味着我们为ColPali嵌入和两阶段Muvera加ColPali排序得到了完全相同的结果集。
还有一个我们仍然想测试的查询。不幸的是,这次Muvera两阶段检索未能找到任何有意义的结果。确实,它不断返回一些与所提供查询毫无共同之处的随机幻灯片。然而,我们可以通过增加过采样率来控制两阶段流程的检索质量。例如,通过将过采样率设置为20,这样我们可以得到100个结果,然后用ColPali重新排序。
最终总结
让我们总结所有查询的结果。如你所见,两阶段检索的平均耗时仍然显著低于整个ColPali嵌入搜索。然而,如果你确实需要最佳匹配,精度可能无法接受。
尽管如此,我们已经成功演示了Muvera在多模态文档检索中的实际应用。结果表明,Muvera实现了其承诺:与原始ColPali多向量相比,实现了显著更快的搜索速度,同时保持了高精度。压缩后的固定维度编码支持HNSW索引,从而显著提高了查询延迟。
两阶段检索模式是推荐的校正方法:利用Muvera的速度进行初始候选检索,然后利用ColPali的精度进行最终排序。这种混合策略提供了性能和质量的平衡,使得多向量模型在大规模应用中变得实用。在现实世界场景中,面对数千或数百万文档,Muvera的优势将变得更加明显。

在本节课中,我们一起学习了Muvera嵌入技术。我们了解了它如何通过SimHash聚类和随机投影将可变长度的多向量序列转换为固定维度的单向量,从而解锁高效的HNSW搜索。我们还探讨了其随机性特点以及通过重复过程来稳定结果的方法。最后,我们通过实验对比了纯Muvera搜索和“Muvera初筛 + ColPali精排”的两阶段混合策略,认识到在追求检索速度的同时,需要根据精度要求灵活调整策略参数。这为处理大规模多向量检索提供了一种有效的工程解决方案。
006:构建基于Colpali的多模态RAG系统 🛠️


在本节课中,我们将整合之前学到的所有知识,构建一个基于Colpali的、功能完整的多模态检索增强生成(RAG) 系统。我们将结合Colpali的多向量检索能力和OpenAI的视觉模型,创建一个能够直接理解图像文档并回答问题的系统。
系统概述与准备
上一节我们介绍了Colpali的工作原理、内存优化以及Movea如何加速多向量检索。本节中,我们将把这些组件组合起来。
首先,你需要加载OpenAI API密钥并创建客户端。接着,我们将重建一个包含Colpali嵌入及其各种优化版本的集合,确保本课程可以独立运行。
以下是创建集合的步骤:
- 加载环境变量中的OpenAI API密钥。
- 创建一个OpenAI客户端实例。
- 调用辅助函数,重建包含所有优化策略(如原始嵌入、二进制量化、标量量化、不同池化方法)以及Movea嵌入的集合。
实现检索器
现在,让我们来实现系统的核心检索功能。我们将创建一个统一的检索器,它内部处理查询的嵌入过程,并返回相关的图像路径和分数。
检索器函数 retrieve 接收一个文本查询,并通过指定 using 参数来调用Quadrant的查询方法,以使用特定的向量进行检索。由于我们讨论过的某些量化方法内置了重打分机制,为了避免量化向量可能带来的结果偏差,我们在此明确不使用该机制。
此外,我们还有一个实现两阶段检索的辅助函数。它将首先使用Movea嵌入进行快速的预筛选,然后使用你指定的向量(如原始Colpali向量或池化向量)对候选结果进行重排序,以在速度和精度之间取得最佳平衡。
让我们用一个示例查询来测试检索器:
query = "人类大脑是如何工作的?"
results = retrieve(query, using="original_colpali_vectors")
检索器将返回与查询最相关的PDF页面。
集成生成模型
检索到相关文档页面后,下一步是生成答案。我们将把检索到的图像页面连同用户问题一起发送给GPT-4o。
这个过程包含几个部分:
- 发送一个系统提示,指示模型仅基于提供的文档图像回答问题,并以Markdown格式输出。
- 传递用户的原始问题。
- 附上所有检索到的图像。
整个交互将被发送到OpenAI服务以获取大语言模型的输出。让我们用之前检索到的文档测试生成功能,看看GPT-4o如何回答问题。
测试完整工作流
现在,让我们用几个不同的问题来测试完整的RAG工作流。
首先,使用原始的Colpali检索器为所有查询检索文档,提取图像路径,然后利用GPT-4o的视觉能力生成答案。这种“检索”加“生成”的两步方法使流程清晰可控。
我们不仅会显示LLM生成的答案,还会显示发送给模型的提示和输入图像。你可以对数据集中的第二个和最后一个查询重复此过程。
探索优化策略
在实践中,我们通常会尝试使用所有学到的技术来优化这个过程。我们的集合中存储了多种优化后的向量,因此可以轻松测试不同方法。
让我们测试一些优化技术,例如使用二进制量化向量和分层令牌池化(池化因子设为2)。现在用相同的检索器但不同的优化技术进行测试,由于输入的文档集会略有不同,你应该会看到一些不同的输出。
尽管二进制量化版本使用的图像集可能不同,但它仍然对输入提示给出了相当不错的回答。在我们的实验中,分层令牌池化表现得甚至更好。
然而,仅优化内存通常不够。正如上一课所见,单独使用Movea可能比原始Colpali多向量的精度低,但它通过HNSW索引提供了显著更快的搜索速度。
最优的生产方案是结合两者:使用Movea快速检索一个更广泛的候选集,然后用任何优化方法对这些候选进行重排序以获得最终结果。这就是我们创建 retrieve_with_two_stage 函数的原因,它利用Quadrant的预取机制和灵活的重排序策略实现了这一模式。
让我们看看实际操作。测试两阶段检索,并将其与原始Colpali检索器进行比较。对于此比较,我们将使用原始完整的Colpali嵌入作为默认的重排序策略,在Movea快速预取之后进行。这有望在受益于Movea的HNSW搜索(在扩展到更大文档集时尤为重要)的同时,保持较高的准确性。
不同策略的比较
最后,让我们比较不同的重排序策略如何影响检索质量和性能。
当我们运行这个实验时,Movea检索到的文档将分别使用以下策略进行重排序:
- 原始Colpali向量:提供最高精度,但需要计算所有多向量。
- 二进制量化向量:通过使用压缩表示实现更快的重排序,并显著减少存储所有向量所需的内存,但精度略有降低。
- 分层令牌池化:减少了重排序期间需要比较的向量数量,提供了另一个速度与精度的平衡点。
在生产中,你可以根据需求选择:对成本敏感的应用选择二进制量化,或者坚持使用原始向量。使用Movea的两阶段检索模式对于生产系统尤其强大,它结合了HNSW索引的速度优势,同时重排序阶段又非常灵活,因为我们可以使用任何优化方法。
总结 🎯
本节课中,我们一起学习了如何构建一个直接处理扫描文档、PDF或图像的检索系统。我们重点解决了与多向量相关的两个关键问题:
- 高内存使用:通过应用量化或池化技术,甚至组合多种方法来缓解。
- 与近似最近邻技术不兼容:多向量表示默认需要全表扫描,无法扩展。Movea 是一种将可变长度的向量序列转换为固定维度表示的有前景的方法,从而实现了近似搜索。

通常,原始向量仍会用于重排序,以实现速度和高质量的结合。正如你所见,将所有这些工具整合在一起,可以构建一个灵活且高性能的多模态RAG系统。
007:总结 🎯
在本课程中,我们学习了多向量检索的基础概念。从文本检索开始,逐步扩展到图像检索,并探讨了相关的优化技术与实际应用。接下来,让我们回顾一下整个学习旅程。
课程概述 📖
在本节课中,我们将要学习多向量检索的核心内容总结。我们从基础的文本检索概念出发,探索了如何将这些原理应用于图像领域,并研究了提升效率的优化方法,最后构建了一个完整的应用示例。
核心内容回顾 🔄
上一节我们介绍了多向量检索的优化与应用,本节中我们来对整个课程进行总结。
以下是本课程涵盖的主要知识点:
- 文本检索基础:课程始于使用
ColBERT模型进行文本检索的基本概念。 - 图像检索扩展:接着,你了解了如何将相同的概念应用于使用
CoCa模型进行多向量图像检索。 - 优化技术:由于多向量技术会生成大量向量数据,你随后探索了一些优化技术来减少
CoCa模型的内存占用。 - 混合搜索:然后,你看到了
MVER如何允许CoCa模型与混合(硬件与软件)搜索结合使用。 - 应用构建:最后,你探索了一个围绕所有这些技术构建的完整工作应用程序示例。
总结与展望 🌟
本节课中,我们一起学习了多向量检索的底层概念,并获得了构建新应用的想法。

我希望你在完成本课程后,能对多向量检索背后的概念充满信心,并带着想要构建的新应用灵感离开。
我迫不及待想看到你的成果。😊


浙公网安备 33010602011771号