大规模向量检索优化:Binary Quantization 让 RAG 系统内存占用降低 32 倍

简介: 本文介绍基于二值化量化的高效RAG系统,通过将float32嵌入压缩为1bit,实现32倍内存缩减。结合Milvus与Hamming距离检索,3600万向量查询仅需30ms。采用过采样与重排序策略,准确率可达95%以上,适合高维大规模场景。

当文档库规模扩张时向量数据库肯定会跟着膨胀。百万级甚至千万级的 embedding 存储,float32 格式下的内存开销相当可观。

好在有个经过生产环境验证的方案,在保证检索性能的前提下大幅削减内存占用,它就是Binary Quantization(二值化量化)

本文会逐步展示如何搭建一个能在 30ms 内查询 3600 万+向量的 RAG 系统,用的就是二值化 embedding。

二值化量化解决什么问题

常规 embedding 用 float32 存储:单个 embedding(1024 维)占 4 KB 左右,3600 万个 embedding 就是 144 GB

二值化量化把每个维度压缩成 1 bit:同样的 embedding 只需 128 bytes,3600 万个 embedding 降到 4.5 GB

内存直接减少约 32 倍,而且位运算做相似度搜索更快。

精度损失与应对策略

二值化量化确实会带来精度损失,这点不能回避。

从 float32 直接压缩到 1 bit信息丢失不可避免,根据测试数据显示纯二值检索的准确度会下降到 92.5% 左右。不过这个问题有成熟的解决方案。

Oversampling(过采样)

检索时多拿一些候选结果。比如本来只需要 top-5,可以先检索 top-20 或 top-50,用数量换精度抵消量化造成的分辨率损失。

Rescoring(重排序)

先用二值向量快速筛选候选集,然后用原始的 float32 向量重新计算相似度并排序。

具体做法是:把全精度向量存在磁盘、二值向量和索引放内存,检索时先用内存里的二值索引快速找到候选,再从磁盘加载原始向量做精确评分。

这两个技术组合使用,能把准确度拉回到 95%-96%,对大多数 RAG 应用来说够用了。

使用限制

维度小于 1024 的 embedding 不建议用二值化。维度太小时,1 bit 能保留的信息不足,准确度会掉得比较厉害。所以这个技术更适合高维向量(≥1024 维)和大规模数据集。

相比之下,float8 这类低位浮点格式在 4 倍压缩下性能损失不到 0.3%,但内存节省远不如二值化激进。32 倍的压缩率带来的精度代价,需要根据具体场景权衡。

数据加载

先用 LlamaIndex 的 directory reader 读取文档。

支持的格式挺全:PDF、Word、Markdown、PowerPoint、图片、音频、视频都行。

LLM 配置

 from llama_index.llms.groq import Groq  
 from llama_index.core.base.llms.types import (  
 ChatMessage, MessageRole )  

 llm = Groq(  
 model="MiniMaxAI/MiniMax-M2.1",  
 api_key=groq_api_key,  
 temperature=0.5,  
 max_tokens=1000  
 )  
 Moonshot Al  
 prompt_template = (  
 "Context information is below.\n"  
 "-----\n"  
 "CONTEXT: {context}\n"  
 "Given the context information above think step by step  
 "to answer the user's query in a crisp and concise manner.  
 "In case you don't know the answer say 'I don't know!'.\n"  
 "QUERY: {query}\n"  
 "ANSWER:  
 )  
 = query "Provide concise breakdown of the document"  
 prompt = prompt_template.format(context=full_context, query=query)  
 user_msg = ChatMessage(role=MessageRole.USER, content=prompt)  
 # Stream response from LLM  
 streaming_response = llm.stream_complete(user_msg.content)

LLM 配置完成,下一步开始对文件进行索引

二值 Embedding 生成

我们先生成标准 float32 embedding,然后用简单阈值转成二值向量。

每个维度的转换规则:

  • 值 > 0 → 1
  • 否则 → 0

Query Embedding 和二值化

 # Generate float32 query embedding  
 query_embedding = embed_model.get_query_embedding(query)  

 # Apply binary quantization to query  
 binary_query = binary_quantize(query_embedding)  
 # Perform similarity search using Milvus  
 search_results = client.search(  
 )  
 collection_name="fastest-rag",  
 data=[binary_query],  
 Similarity search  
 anns_field="binary_vector",  
 search_params={"metric_type": "HAMMING"},  
 output_fields=["context"],  
 limit=5 # Retrieve top 5 similar chunks  
 # Store retrieved context  
 full_context = []  
 for res in search_results:  
 context = res ["payload"]["context"]  
 full_context.append(context)

为什么用 Hamming distance? 它是二值向量的天然相似度度量,计算速度极快。

Milvus Schema 和索引设置

 from pymilvus import MilvusClient, DataType  

 # Initialize client and schema  
 client = MilvusClient("milvus_binary_quantized.db")  
 schema = client.create_schema (auto_id=True, enable_dynamic_fields=True)  
 # Add fields to schema  
 schema.add_field(field_name="context", datatype=DataType. VARCHAR)  
 schema.add_field(field_name="binary_vector", datatype=DataType.BINARY_VECTOR)  
 # Create index parameters for binary vectors  
 index_params = client.prepare_index_params()  
 index_params.add_index(  
 Specify index params  
 field_name="binary_vector",  
 index_name="binary_vector_index",  
 index_type="BIN_FLAT",  
 # Exact search for binary vectors  
 metric_type="HAMMING" # Hamming distance for binary vectors  
 )  
 # Create collection with schema and index  
 client.create_collection(  
 collection_name="fastest-rag",  
 schema=schema,  
 )  
 index_params=index_params  
 Create collection  
 # Insert data to index  
 client.insert(  
 collection_name="fastest-rag",  
 Insert data  
 data=[  
 {"context": context, "binary_vector": binary_embedding}  
 for context, binary_embedding in zip(batch_context, binary_embeddings)  
 ]  
 )

这套配置能让 Milvus 高效处理数千万级别的向量。

检索流程

检索时的数据流:

  1. 用户 query 转 embedding
  2. embedding 转二值向量
  3. 用 Hamming distance 做二值检索
  4. 返回 top-k 相关文本块

文档 Embedding 的二值化处理

 import numpy as np  
 from llama_index.embeddings.huggingface import HuggingFaceEmbedding  

 embed_model = HuggingFaceEmbedding(  
 model_name="BAAI/bge-large-en-v1.5",  
 trust_remote_code=True,  
 cache_folder='./hf_cache'  
 )  
 for context in batch_iterate(documents, batch_size=512):  
 # Generate float32 vector embeddings  
 batch_embeds = embed_model.get_text_embedding_batch(context)  
 # Convert float32 vectors to binary vectors  
 embeds_array = np.array(batch_embeds)  
 binary_embeds = np.where(embeds_array > 0, 1, 0).astype(np.uint8)  
 # Convert to bytes array  
 packed_embeds = np.packbits(binary_embeds, axis=1)  
 byte_embeds = [vec.tobytes() for vec in packed_embeds]  
 binary_embeddings.extend(byte_embeds)

这个转换过程快、简单、效果好。

LLM 生成环节

检索到 top-k 文本块后,用结构化 prompt 喂给 LLM。

 # Combine retrieved contexts
 full_context = "\n\n".join(full_context)

 # Format prompt with context and query
 prompt = prompt_template.format(context=full_context, query=query)

 # Create chat message
 user_msg = ChatMessage(role=MessageRole.USER, content=prompt)

 # Stream response from LLM
 streaming_response = llm.stream_complete(user_msg.content)

 # Display streaming response
 for chunk in streaming_response:
     print(chunk.delta, end="", flush=True)

这里把检索到的多个文本块拼接起来,填充到 prompt template 里。LLM 会基于这些上下文生成回答。如果检索内容里没有答案,LLM 会直接回复 "I don't know!"。

总结

二值化量化在大规模 RAG 系统中的价值已经得到验证。32 倍的内存压缩率配合 Hamming distance 的计算效率,使得在资源受限环境下部署千万级向量检索成为可能。

精度损失是这个方案的代价,但 oversampling + rescoring 的组合能将准确度维持在 95% 以上,这对多数应用场景足够。

Perplexity、Azure、HubSpot 的生产实践说明这套方案已经过大规模验证。不过具体部署时还是要根据数据特征做测试,尤其是 rescoring 的候选集大小(oversampling factor)需要根据实际召回率调整。

作者:Algo Insights

目录
相关文章
|
12天前
|
数据采集 人工智能 安全
|
7天前
|
机器学习/深度学习 人工智能 前端开发
构建AI智能体:七十、小树成林,聚沙成塔:随机森林与大模型的协同进化
随机森林是一种基于决策树的集成学习算法,通过构建多棵决策树并结合它们的预测结果来提高准确性和稳定性。其核心思想包括两个随机性:Bootstrap采样(每棵树使用不同的训练子集)和特征随机选择(每棵树分裂时只考虑部分特征)。这种方法能有效处理大规模高维数据,避免过拟合,并评估特征重要性。随机森林的超参数如树的数量、最大深度等可通过网格搜索优化。该算法兼具强大预测能力和工程化优势,是机器学习中的常用基础模型。
344 164
|
6天前
|
机器学习/深度学习 自然语言处理 机器人
阿里云百炼大模型赋能|打造企业级电话智能体与智能呼叫中心完整方案
畅信达基于阿里云百炼大模型推出MVB2000V5智能呼叫中心方案,融合LLM与MRCP+WebSocket技术,实现语音识别率超95%、低延迟交互。通过电话智能体与座席助手协同,自动化处理80%咨询,降本增效显著,适配金融、电商、医疗等多行业场景。
345 155
|
7天前
|
编解码 人工智能 自然语言处理
⚽阿里云百炼通义万相 2.6 视频生成玩法手册
通义万相Wan 2.6是全球首个支持角色扮演的AI视频生成模型,可基于参考视频形象与音色生成多角色合拍、多镜头叙事的15秒长视频,实现声画同步、智能分镜,适用于影视创作、营销展示等场景。
582 4
|
15天前
|
SQL 自然语言处理 调度
Agent Skills 的一次工程实践
**本文采用 Agent Skills 实现整体智能体**,开发框架采用 AgentScope,模型使用 **qwen3-max**。Agent Skills 是 Anthropic 新推出的一种有别于mcp server的一种开发方式,用于为 AI **引入可共享的专业技能**。经验封装到**可发现、可复用的能力单元**中,每个技能以文件夹形式存在,包含特定任务的指导性说明(SKILL.md 文件)、脚本代码和资源等 。大模型可以根据需要动态加载这些技能,从而扩展自身的功能。目前不少国内外的一些框架也开始支持此种的开发方式,详细介绍如下。
1019 7

热门文章

最新文章