miniCOIL:为BM25添加语义

miniCOIL:为BM25添加语义 如果你花时间构建搜索系统让我告诉你一些可能会让你有点刺痛的事情BM25这个算法为OpenSearch、Elasticsearch和大多数企业搜索系统中的词法搜索提供动力……它不理解它处理的任何一个词。一个都不理解。它只是计算词。它权衡它们的稀有度。它根据这些计数对文档进行排名。但它完全不知道任何词的实际含义。对于大多数查询这工作得很好。你搜索python tutorialBM25找到包含这些精确词的文档。太好了。但语言变得模棱两可的那一刻。search在两个不同文档中意味着不同的东西的那一刻vector在一个段落中指流行病学在另一个段落中指线性代数的那一刻……BM25失败了。静默地。自信地。毫无警告。miniCOIL修复了这个问题。而且它以一种令人惊讶的优雅方式做到了这一点。1、BM25它实际上做什么 来源研究BM25 最佳匹配25是一个来自1990年代的排名函数它对它的作用来说老化得非常好。公式看起来像这样score(D,Q)∑qi∈QIDF(qi)×TF(qi,D)score(D,Q)qi​∈Q∑​IDF(qi​)×TF(qi​,D)两个组件驱动一切IDF逆文档频率这个词在所有文档中有多罕见像the这样的词到处都出现所以它的IDF很低。像galacto这样的词很少出现所以它的IDF很高。稀有词更有信息量所以它们在最终得分中获得更多权重。TF词频这个词在这个特定文档中出现多少次更多出现表明更高的相关性达到一定程度。BM25应用饱和函数所以你不能通过无休止地重复词来游戏它。这就是整个模型。每个词两个数字乘在一起在所有查询词上求和。它很快它可解释对于关键字密集的查询和一致的语料库它工作得很好。OpenSearch在毫秒内对数百万文档运行它。BM25对于它被设计做的事情来说是一个真正好的算法。问题是它被设计做什么… 首先BM25将词的每次出现视为相同。关于冥想的文档中的**“search一词获得与关于Elasticsearch的文档中的search一词完全相同的IDF权重。线性代数教科书中的vector在计算上与关于蚊媒疾病的流行病学论文中的vector”**无法区分。BM25看不到上下文。它只看到词。2、同义词问题BM25总是失败的地方这不是边缘情况。这是语言的正常状态。大多数常见词根据上下文有多个有意义的不同的意义。考虑当你查询时会发生什么“vector disease control mosquito”BM25会对任何包含vector的文档进行评分。这包括关于蚊媒疾病媒介的公共卫生论文正确关于向量数据库的机器学习文章错误关于向量加法的物理教科书章节错误关于特征向量的线性代数课程错误所有这些都包含vector。BM25无法根据vector在每个文档中的实际含义来区分它们。它只是计数。而且计数相等。或者尝试“searching for inner peace and self discovery”BM25匹配每个包含search或searching的文档。关于搜索引擎优化的文档与关于精神自我发现的文档获得相同的关键字匹配分数。模型无法看到search作为技术数据库操作和search作为意义的人类隐喻之间的区别。词上下文ABM25告诉它们分开上下文Bsearch搜索引擎、索引、检索搜索灵魂、正念searching数据库查询寻找内心平静vector数据库、嵌入、AI控制、蚊子vector微积分、梯度、方程数据库、嵌入vector控制、蚊子向量微积分、梯度方程bank利率、货币政策河岸、侵蚀、洪泛区plant工厂、制造、设备花、叶子、光合作用语言是深度模棱两可的BM25忽略了每一个模棱两可。不是因为它设计糟糕而是因为它在设计时不存在更好的工具。那些工具现在存在。3、miniCOIL想法miniCOIL是由Qdrant开发的稀疏检索模型它用一个关键的补充扩展了BM25的公式上下文意义。核心洞察很简单。当你索引一个词时你可以访问所有周围的词。你知道vector是否出现在“database,” embeddings, AI旁边还是 disease, control, mosquito旁边还是 calculus, gradient, equations旁边。那个周围的上下文告诉你在这个文档中、就在这里、在这个特定出现中使用了哪个词义。miniCOIL通过为每个词出现计算一个4维上下文指纹来捕获这一点。四个数字。这听起来不可能小… 但它足够了。大多数有多个含义的词只有少数真正不同的意义。四个维度可以区分它们。在查询时相同的过程在查询上运行。当查询vector disease control mosquito遇到关于蚊媒疾病媒介的文档时vector这个词在查询和文档中都有相似的指纹。当它遇到关于向量数据库的文档时指纹不同。该词的匹配分数下降。4、公式一个额外的项改变一切现在有三个因素IDF这个词在整个集合中有多罕见与BM25相同重要性这个词在这个文档中有多重要类似于TF但是学习的意义查询和文档是否在相同的意义中使用这个词意义项是查询中词的4维上下文指纹与文档中相同词的4维上下文指纹的点积。相同词在相同上下文中意味着高意义分数。相同词在完全不同的上下文中意味着低意义分数。上下文感知的全部区分能力被捕获在那个点积中。那就是革命。不是一个全新的检索范式。只是你使用了几十年的公式中的一个额外项。5、为什么4维足够你可能会想为什么上下文指纹只用4维对于捕获词义来说这听起来不可能小。关键是miniCOIL不试图编码所有语义信息。它不是通用嵌入模型。它只试图区分共享相同表面形式的词的不同意义。对于大多数模棱两可的词有意义的不同意义数量很小。Vector可能有5或6个真正不同的上下文。Search有一小撮。Bank有两个主导的。4维空间可以在小数量的簇之间区分这就是这里所需的全部。这个设计选择有一个美丽的后果miniCOIL保持稀疏。它产生稀疏向量就像BM25一样每个词表术语一个值。你不需要运行完整的密集嵌入模型。词表大小匹配标准BM25词表。索引大小相当。你以大致BM25的成本获得上下文消歧。这就是miniCOIL的工程优雅。miniCOIL在OpenWebText上训练4000万个多样化的一般网络文本句子。这是一个深思熟虑的选择在像MS MARCO信息检索查询这样的领域特定语料库上训练会使模型偏向那个领域。OpenWebText给miniCOIL跨一般语言的广泛词义覆盖这就是为什么它能很好地泛化到领域外查询。6、IDF修饰符为什么这需要Qdrant这是基础设施变得有趣的地方也是大多数向量数据库不足的地方。miniCOIL产生稀疏向量其中存储的值是每个术语的重要性×意义因子。IDF因子被有意不烘焙到那些存储的值中。为什么因为IDF随着你的集合发展而变化。添加新文档删除旧文档… 每个术语在语料库中的频率会转移。如果你在索引时将IDF烘焙到存储的向量中每当添加或删除任何文档时你都需要重新索引每个文档。那不是检索系统。那是假装成一个的批处理流水线。Qdrant用一个参数解决了这个问题modifierModifier.IDF。from qdrant_client import QdrantClient from qdrant_client import models client QdrantClient(:memory:) client.create_collection( collection_nameminicoil, sparse_vectors_config{ minicoil: models.SparseVectorParams( modifiermodels.Modifier.IDF ) }, )有了那一行Qdrant动态跟踪文档频率并在查询时使用实时集合统计应用IDF。文档可以自由添加或删除IDF值自动保持正确。没有重新索引作业。没有陈旧的近似。没有维护流水线。OpenSearch和Elasticsearch没有等效功能。两个系统都没有自定义稀疏向量的IDF修饰符。你必须从完整语料库自己计算IDF在索引时将其乘入稀疏向量值并在语料库更改时重新索引所有内容。这在技术上是可行的。这也是那种当有人上传新文档批次而没有人重新运行IDF计算时在生产中静默损坏的东西。Qdrant正确地、原生地、始终保持最新地做到了这一点。这不是次要的实现细节。这是使miniCOIL作为生产检索器而不是研究原型可行的原因。7、FastEmbed内置miniCOIL支持miniCOILQdrant/minicoil-v1原生集成到FastEmbed来自Qdrant团队的轻量级嵌入库。FastEmbed为广泛的嵌入模型处理基于ONNX的推理包括miniCOIL无需PyTorch设置、CUDA配置或分词器管理。对于miniCOIL特别地Qdrant客户端的Document类在底层直接与FastEmbed集成# 索引时 — FastEmbed在内部处理miniCOIL推理 models.Document( textdocument_text, modelQdrant/minicoil-v1, options{avg_len: avg_document_length}, # BM25长度归一化 ) # 查询时 models.Document( textquery_text, modelQdrant/minicoil-v1 )你不需要自己加载模型。你不需要管理分词器。你只需传递一个Document对象Qdrant调用FastEmbed运行推理并处理其余部分。avg_len参数为BM25风格的文档长度归一化提供数据在摄入前从你的语料库计算一次。对于想要将miniCOIL添加到现有流水线的开发者来说进入门槛几乎为零。安装qdrant-client[fastembed]你就拥有了所需的一切。8、Qdrant为这种搜索而构建Qdrant是用Rust从头构建的考虑到AI规模的搜索。稀疏向量、密集向量和混合搜索都是一等公民不是事后附加到通用数据库上的。modifierModifier.IDF功能是Qdrant如何考虑检索正确性的一个很好的例子。他们不只是添加稀疏向量存储。他们考虑了完整的评分流水线并为其构建了在动态、实时集合中正确工作所需的基础设施。最近的版本使这对现实世界部署更加强大。Qdrant 1.16引入了ACORN这是对过滤向量搜索的重大改进。当你将语义检索与元数据过滤器结合时只在特定知识库内搜索或在特定日期范围内的文档内搜索传统的HNSW实现会静默降级。过滤后的邻域变得稀疏图遍历开始错过相关结果而不报告任何错误。ACORN通过在直接邻居被过滤掉时检查HNSW图中邻居的邻居来修复这个问题。基准数字清楚地表明了这一点在有多个低选择性过滤器的查询上ACORN的准确率为97.2%而没有它的准确率为53.3%。对于任何用户在知识库范围子集内搜索的生产部署这就是使系统值得信赖的原因。Qdrant 1.16还带来了条件更新它允许版本感知的点修改以防止并发冲突。当多个写入者同时更新实时miniCOIL集合时这使索引保持一致而无需外部锁定。Qdrant 1.17发布了用于混合查询的加权RRF。如果你将miniCOIL的稀疏检索信号与密集语义模型结合对生产RAG来说是一个强大的组合你现在可以为每个信号分配不同的重要性权重。关键字密集查询用更多稀疏权重概念查询用更多密集权重。在查询时可调无需重新训练。Qdrant 1.17还带来了相关性反馈查询它实现了代理细化循环。系统可以发出初始miniCOIL查询根据任务标准对结果进行评分反馈哪些结果是相关的并获得精炼的结果集。无需重新训练无需重新嵌入无需模型调用。使用向量结构的纯检索时细化。Qdrant文档涵盖稀疏向量、混合搜索、IDF修饰符以及与miniCOIL部署相关的一切。9、真实数字BEIR基准这是真正见分晓的地方。BEIR是评估跨不同数据集检索质量的标准基准。NDCG10测量排名质量越高越好。数据集BM25miniCOIL赢家MS MARCO0.2370.244miniCOILNQ0.3040.319miniCOILQuora0.7840.802miniCOILFiQA-20180.2520.257miniCOILHotpotQA0.6340.633BM25平局miniCOIL在所有五个数据集上获胜或平局零领域特定训练。它在一般网络文本上训练而不是在MS MARCO或任何这些特定领域上但它仍然始终优于BM25。Quora的增益特别值得注意0.018 NDCG10。Quora充满了改写的问题相同的意图以许多不同的方式表达词义消歧对于将同义查询匹配到相关答案很重要。miniCOIL的上下文指纹帮助识别两个不同的措辞何时在问同一件事即使精确的词不同。10、完整代码10.1 安装依赖项pip install qdrant-client[fastembed] rank-bm25 streamlit10.2 语料库DOCUMENTS [ The Art of Search and Self-Discovery, Searching the Soul: A Journey Through Mindfulness, In Search of Meaning: A Psychological Perspective, The Human Search for Connection in a Digital World, Search Engines: A Technical and Social Overview, Search Optimization Strategies for E-commerce, The Rise of Vector Databases in AI Systems, Efficient Vector Search Algorithms for Large Datasets, Vector Control Strategies in Public Health, Vectors in Physics: From Arrows to Equations, ]十个文档。search和vector这些词在它们中出现在非常不同的上下文中。完美的语料库用于展示BM25出错的地方和miniCOIL正确的地方。10.3 构建BM25索引from rank_bm25 import BM25Okapi def build_bm25(documents): tokenized [doc.lower().split() for doc in documents] return BM25Okapi(tokenized) def bm25_search(bm25, documents, query: str, n: int 5): scores bm25.get_scores(query.lower().split()) ranked sorted(enumerate(scores), keylambda x: -x[1]) return [(documents[i], round(s, 4)) for i, s in ranked[:n]]简单。快速。上下文盲目。10.4 构建miniCOIL索引from qdrant_client import QdrantClient from qdrant_client import models def build_minicoil(documents): client QdrantClient(:memory:) client.create_collection( collection_nameminicoil, sparse_vectors_config{ minicoil: models.SparseVectorParams( modifiermodels.Modifier.IDF # 来自实时集合统计的动态IDF ) }, ) # 为BM25风格长度归一化计算avg_len avg_len sum(len(d.split()) for d in documents) / len(documents) client.upsert( collection_nameminicoil, points[ models.PointStruct( idi, payload{text: documents[i]}, vector{ minicoil: models.Document( textdocuments[i], modelQdrant/minicoil-v1, options{avg_len: avg_len}, ) }, ) for i in range(len(documents)) ], ) return client def minicoil_search(client, query: str, n: int 5): hits client.query_points( collection_nameminicoil, querymodels.Document(textquery, modelQdrant/minicoil-v1), usingminicoil, limitn, with_payloadTrue, ).points return [(h.payload[text], round(h.score, 4)) for h in hits]10.5 运行比较bm25 build_bm25(DOCUMENTS) client build_minicoil(DOCUMENTS) query vector disease control mosquito print(fQuery: {query}\n) print(BM25 results:) for text, score in bm25_search(bm25, DOCUMENTS, query, n5): print(f [{score}] {text}) print(\nminiCOIL results:) for text, score in minicoil_search(client, query, n5): print(f [{score}] {text})预期输出BM25 results: [0.6123] Vector Control Strategies in Public Health ← 正确 [0.5891] The Rise of Vector Databases in AI Systems ← 错误 [0.5712] Efficient Vector Search Algorithms... ← 错误 [0.5543] Vectors in Physics: From Arrows to Equations ← 错误 [0.3201] Search Engines: A Technical and Social Overview miniCOIL results: [0.8932] Vector Control Strategies in Public Health ← 正确明显排名第一 [0.2341] Vectors in Physics: From Arrows to Equations [0.1892] The Rise of Vector Databases in AI Systems ...BM25返回所有包含vector的文档分数相似无法区分流行病学和计算机科学。miniCOIL将公共卫生文档清楚地放在顶部因为disease, “control,” 和 mosquito旁边的vector的上下文指纹正确匹配。10.6 第二个查询内省搜索query searching for inner peace and self discovery print(BM25 results:) for text, score in bm25_search(bm25, DOCUMENTS, query, n5): print(f [{score}] {text}) print(\nminiCOIL results:) for text, score in minicoil_search(client, query, n5): print(f [{score}] {text})BM25在所有10个文档中对search同等对待。miniCOIL看到search旁边的inner, “peace,” “self,” “discovery”并构建了一个哲学指纹与语料库顶部的内省文档对齐。11、miniCOIL在你的技术栈中的位置miniCOIL不是密集语义搜索的替代品。它是更好的BM25这意味着它是混合检索系统中正确的词法组件。理想的生产设置用户输入 - 查询输入查询分支A密集查询 - 密集模型例如BGE、E5 - 密集向量 - 密集搜索分支B稀疏查询 - miniCOIL - 稀疏向量 - 稀疏搜索融合密集结果 稀疏结果 - Qdrant加权RRF融合输出最终排名结果这给你两全其美密集检索用于概念查询miniCOIL用于带上下文感知的关键字精度Qdrant的加权RRF来自1.17根据你的用例调整每个信号贡献多少。如果你当前的系统在OpenSearch上使用BM25 密集搜索通往miniCOIL的路径是添加Qdrant作为检索层。在那里运行miniCOIL和你的密集模型。你现有的应用程序逻辑保持原样只是下面有更好的检索质量。12、结束语BM25对于它的作用来说是一个伟大的算法一个快速、可解释的关键字匹配器。它为搜索行业服务了几十年并且在很长一段时间内将继续这样做。但它是为一个vector只意味着一件事、search只意味着一件事的世界构建的。那个世界在自然语言中不存在。miniCOIL添加了BM25一直缺失的一个成分对词在上下文中的含义的理解。不是完整的语义理解只是足以区分意义。而且它这样做的同时保持稀疏保持快速保持与搜索系统多年来构建的BM25评分框架兼容。公式中的一个额外项。每个词一个4维指纹。突然你的搜索系统停止混淆疾病媒介和数学向量。那不是魔法。那只是一个设计得非常好的模型。原文链接miniCOIL为BM25添加语义 - 汇智网