跳到主要内容

密集嵌入与稀疏嵌入

在 RAG(Retrieval-Augmented-Generation)的混合检索中,密集嵌入(Dense Embedding)和稀疏嵌入(Sparse Embeddings) 是两种互补的向量表示方法,分别用于捕捉语义信息和关键词匹配。

混合检索概念

混合搜索结合了不同搜索范式的优势,以提高检索的准确性和鲁棒性。它充分利用了密集向量搜索和稀疏向量搜索的能力,确保对各种查询进行全面而准确的检索。

在这种情况下,使用语义向量相似性和精确关键词匹配两种方法检索候选内容。来自这些方法的结果会被合并、重新排序,并传递给 LLM 以生成最终答案。这种方法兼顾了精确性和语义理解,对各种查询场景都非常有效。

混合检索架构

两种形式的向量都要存储到向量数据库中:

  • 密集嵌入(Dense Embeddings):用于捕捉语义信息,找到语义最相近的向量
  • 稀疏嵌入(Sparse Embeddings):用于关键词匹配,ElasticSearch 中采用的就是稀疏嵌入

密集嵌入模型

密集嵌入模型将文本转换为稠密的向量表示,适合语义相似度匹配。

选择对比

模型提供商维度是否收费特点
bge-large-zh-v1.5智源研究院(BAAI)1024免费开源中文效果优秀
bge-small-zh-v1.5智源研究院(BAAI)512免费开源轻量级,速度快
text-embedding-3-smallOpenAI1536API收费全球通用
text-embedding-3-largeOpenAI3072API收费效果最好

BGE-Large 安装和使用

pip install --upgrade --quiet sentence_transformers
from langchain_huggingface import HuggingFaceEmbeddings

model_name = "BAAI/bge-large-zh-v1.5"
model_kwargs = {'device': 'cpu'}
encode_kwargs = {'normalize_embeddings': True} # set True to compute cosine similarity

bge_embedding = HuggingFaceEmbeddings(
model_name=model_name,
model_kwargs=model_kwargs,
encode_kwargs=encode_kwargs
)

embeddings = bge_embedding.embed_documents(
[
"Hi there!",
"Oh, hello!",
"What's your name?",
"My friends call me World",
"Hello World!"
]
)
print(len(embeddings), len(embeddings[0]))

embedded_query = bge_embedding.embed_query("What was the name mentioned in the conversation?")
print(embedded_query)

OpenAI Embeddings 使用

from langchain_openai import OpenAIEmbeddings

openai_embedding = OpenAIEmbeddings(
model="text-embedding-3-small",
openai_api_key="your-api-key"
)

# 生成向量
vector = openai_embedding.embed_query("你的查询文本")

normalize_embeddings 参数说明

HuggingFaceBgeEmbeddings 中,normalize_embeddings 参数用于决定是否对生成的嵌入向量进行归一化处理:

说明
True生成的嵌入向量会被归一化为单位向量,L2 范数为 1
False生成的嵌入向量保持原始数值,不进行归一化处理

归一化的优点

  • 提高相似度计算的稳定性:归一化后的向量可以避免因向量长度不同而导致的相似度偏差
  • 一致性:归一化可以确保不同批次或不同模型生成的嵌入向量在同一尺度上,便于比较和整合

BM25 稀疏嵌入详解

BM25(Best Matching 25)是一种 基于统计的稀疏检索算法,是信息检索(IR)领域最经典的排序函数之一。它是对传统 TF-IDF(词频-逆文档频率) 的改进,能够更合理地衡量文档与查询的相关性,广泛用于搜索引擎(如 ElasticSearch、Lucene)和问答系统。

BM25 的核心思想

BM25 的核心是计算查询(Query)文档(Document) 之间的相关性得分,主要考虑以下因素:

  1. 词频(Term Frequency, TF):查询词在文档中出现的频率(越高越相关)
  2. 逆文档频率(Inverse Document Frequency, IDF):查询词在整个语料库中的稀有程度(越稀有越重要)
  3. 文档长度归一化(Document Length Normalization):避免长文档因词频高而占据优势

BM25 公式

BM25 的公式比 TF-IDF 更精细,引入了可调参数(k₁b),使其对不同数据集更鲁棒:

参数说明

  • k₁:词频饱和度参数,控制词频增长对得分的影响速度,通常取值 1.2-2.0
  • b:文档长度归一化参数,控制文档长度对得分的影响程度,通常取值 0.75

Milvus 中使用 BM25

在 Milvus 中,可以通过 Function 来自动将文本字段转换为稀疏向量(BM25)。

from pymilvus import MilvusClient, DataType, Function, FunctionType

client = MilvusClient(uri='http://152.136.163.231:19530')

# 先删除旧的 collection
client.drop_collection(collection_name='t_demo2')

# 定义 Collections 模式 (三个字段)
schema = client.create_schema()
schema.add_field(field_name="id", datatype=DataType.INT64, is_primary=True, auto_id=True)
schema.add_field(field_name="text", datatype=DataType.VARCHAR, max_length=2000, enable_analyzer=True)
schema.add_field(field_name="sparse", datatype=DataType.SPARSE_FLOAT_VECTOR) # 存储稀疏嵌入后的值

# 进行稀疏嵌入的函数:从一个字段中读取原始数据,通过bm25算法,转换为向量,再把稀疏向量存到输出字段
bm25_function = Function(
name="text_bm25_emb", # Function name
input_field_names=["text"], # Name of the VARCHAR field containing raw text data
output_field_names=["sparse"], # Name of the SPARSE_FLOAT_VECTOR field reserved to store generated embeddings
function_type=FunctionType.BM25, # Set to `BM25`
)

schema.add_function(bm25_function)

# 配置索引
index_params = client.prepare_index_params()

index_params.add_index(
field_name="sparse",
index_name="sparse_inverted_index",
index_type="SPARSE_INVERTED_INDEX", # Inverted index type for sparse vectors
metric_type="BM25",
params={
"inverted_index_algo": "DAAT_MAXSCORE", # Algorithm for building and querying the index
"bm25_k1": 1.6, # 范围:[1.2 ~ 2.0]
"bm25_b": 0.75
},
)

# 创建一张表
client.create_collection(
collection_name='t_demo2',
schema=schema,
index_params=index_params
)

# 插入测试数据(sparse 字段由 BM25 函数自动计算,不需要手动提供)
client.insert('t_demo2', [
{'text': 'information retrieval is a field of study.'},
{'text': 'information retrieval focuses on finding relevant information in large datasets.'},
{'text': 'data mining and information retrieval overlap in research.'},
])

# 开始进行匹配搜索(全文搜索)
search_params = {
'params': {'drop_ratio_search': 0.2}, # 搜索时要忽略的低重要性词语的比例
}

resp = client.search(
collection_name='t_demo2',
data=['whats the focus of information retrieval?'],
anns_field='sparse', # 匹配的稀疏向量字段
limit=3,
search_params=search_params,
output_fields=["text"]
)

print(resp)

代码说明

  • Function 是 Milvus 的内置函数,用于自动将文本转换为向量
  • input_field_names 指定要处理的文本字段
  • output_field_names 指定生成的稀疏向量存储字段
  • enable_analyzer=True 启用文本分析器,用于分词

搜索结果示例

# [[{'id': 464302355161319771, 'distance': 1.3473916053771973, 'entity': {'text': 'information retrieval is a field of study.'}}, ...]]