探秘检索增强生成:上下文检索、混合搜索与密集检索全解析

📖阅读时长:15分钟

🕙发布时间:2025-02-09

近日热文:全网最全的神经网络数学原理(代码和公式)直观解释
欢迎关注知乎和公众号的专栏内容
LLM架构专栏
知乎LLM专栏
知乎【柏企
公众号【柏企科技说】【柏企阅文

在自然语言处理(NLP)领域,检索增强生成(RAG)是一种前沿且强大的技术。今天,咱们就深入探讨一下RAG,以及与之紧密相关的上下文检索、混合搜索和基于嵌入的检索(密集检索)。

一、了解检索增强生成(RAG)

检索增强生成(RAG)是NLP中用于生成准确、详实回复的先进方法。传统模型仅依赖自身内部知识,而RAG则不同,它在生成过程中,会从外部文档或数据库检索相关信息,以此提升模型能力。这意味着,当处理需要最新信息或专业知识的主题时,模型能“查找”相关数据,给出更精准的回复。

上下文检索和混合搜索属于RAG的进阶技术,它们能更巧妙地优化系统检索相关信息的方式。

二、什么是上下文检索和混合搜索?

上下文检索

上下文检索可不止是简单的关键字匹配。它不再局限于查找包含查询中确切单词的文档,而是依据查询的含义来检索信息。这一过程涉及对上下文和语义的理解,能够让系统获取更具相关性和意义的文档。

混合搜索

混合搜索融合了两种信息检索方法:

  1. 词法搜索(BM25):这是一种传统方法,依据精确的关键字匹配来检索文档。比如说,当你搜索“猫在垫子上”,它会找到包含这些确切字词的文档。
  2. 基于嵌入的搜索(密集检索):这是一种较新的技术,通过比较文档的语义含义来检索。它会将查询和文档都转化为高维向量(即嵌入),系统会检索那些含义(向量表示)与查询最为接近的文档。

将这两种方法结合,混合搜索能得出更优的结果。它兼具基于关键字的BM25的精准性,以及密集检索的语义理解能力,确保系统既能依据使用的单词,又能结合其含义,找到最相关的文档 。

三、为什么要将BM25与嵌入式相结合?

把BM25和上下文嵌入结合起来,可谓是优势互补:

  • BM25:在查找具有精确关键字匹配的文档方面表现出色,当特定术语很重要时,它的作用就凸显出来了。
  • 基于嵌入的检索:擅长理解查询背后更深层次的含义,哪怕没有使用确切的关键字,也能领会其意图。

这种组合能够保证RAG系统检索到的文档,不仅包含正确的单词,还能把握正确的含义,极大地提升了系统生成回复的质量 。

四、实现混合搜索:代码示例

第1步:使用BM25进行词汇搜索

在这个示例里,我们会借助rank_bm25库来实现词法搜索:

from rank_bm25 import BM25Okapi
from nltk.tokenize import word_tokenize

documents = [
    "The cat sat on the mat.",
    "The dog barked at the moon.",
    "The sun is shining bright."
]

tokenized_corpus = [word_tokenize(doc.lower()) for doc in documents]
bm25 = BM25Okapi(tokenized_corpus)

query = "cat on mat"
tokenized_query = word_tokenize(query.lower())

bm25_scores = bm25.get_scores(tokenized_query)
bm25_results = bm25.get_top_n(tokenized_query, documents, n=3)

print("BM25 Results: ", bm25_results)

这段代码利用BM25,根据文档与查询的匹配程度来检索文档。

第2步:具有密集检索的基于嵌入的搜索

这里,我们会运用transformers为文档和查询创建密集嵌入,再借助faiss查找最相似的文档:

from transformers import AutoTokenizer, AutoModel
import torch
import faiss

tokenizer = AutoTokenizer.from_pretrained("sentence-transformers/all-MiniLM-L6-v2")
model = AutoModel.from_pretrained("sentence-transformers/all-MiniLM-L6-v2")

def embed_texts(texts):
    inputs = tokenizer(texts, padding=True, truncation=True, return_tensors="pt")
    with torch.no_grad():
        embeddings = model(**inputs).last_hidden_state.mean(dim=1)
    return embeddings

doc_embeddings = embed_texts(documents).numpy()
query_embedding = embed_texts([query]).numpy()

index = faiss.IndexFlatL2(doc_embeddings.shape[1])
index.add(doc_embeddings)

_, dense_results_idx = index.search(query_embedding, k=3)
dense_results = [documents[idx] for idx in dense_results_idx[0]]

print("Dense Retrieval Results: ", dense_results)

在这段代码中,我们利用密集嵌入,依据文档与查询的语义相似性来检索文档。

结合BM25和密集检索:混合搜索

要进行混合搜索,我们需要整合BM25和密集检索的结果。对每种方法得出的分数进行归一化和加权处理,从而得到最佳的整体结果:

import numpy as np

bm25_scores = np.array(bm25_scores)
bm25_scores_normalized = bm25_scores / np.max(bm25_scores)

dense_scores = np.linalg.norm(query_embedding - doc_embeddings, axis=1)
dense_scores_normalized = 1 - (dense_scores / np.max(dense_scores))

combined_scores = 0.5 * bm25_scores_normalized + 0.5 * dense_scores_normalized
top_idx = combined_scores.argsort()[::-1]
hybrid_results = [documents[i] for i in top_idx[:3]]

print("Hybrid Search Results: ", hybrid_results)

五、基于嵌入的检索(密集检索)

基于嵌入的检索,也就是我们常说的密集检索,是信息检索领域的前沿方法。它通过将查询和文档映射到高维向量空间(即嵌入),来理解它们背后的含义。这种方法不再依赖精确的关键字匹配,而是衡量查询和文档之间的语义相似性。即便使用的单词没有直接重合,也能高效地检索出相关信息。

密集检索如何工作?

密集检索主要依靠将查询和文档转化为密集向量(即嵌入),这些嵌入由神经网络生成,常见的是像BERT、RoBERTa或句子转换器这样的预训练模型。具体过程如下:

  1. 文本到嵌入的转换:密集检索的首要步骤是把查询和文档都转化为嵌入。这些嵌入是高维向量,代表着文本的语义含义。每个向量对文本中的复杂模式、关系和上下文进行编码,让模型捕捉到的不仅仅是特定关键字的出现。比如,“The cat is sitting on the mat.”和“A feline rests on a rug.”这两句话,尽管没有完全相同的单词,但嵌入模型能学习到它们在语义上是相似的 。
  2. 相似性度量:当查询和文档都被表示为嵌入后,系统会测量查询嵌入和文档嵌入之间的相似性。常用的相似性度量指标有余弦相似性(衡量两个向量之间夹角的余弦值)和欧几里得距离(衡量两个向量之间的直线距离)。其原理是,含义相似的文档,在向量空间中的嵌入也会更接近 。
  3. 排名结果:计算完相似性后,系统会根据文档嵌入与查询嵌入的接近程度对文档进行排序。相似度得分最高的文档,会被认为与查询最为相关。

密集检索的优势

  1. 语义理解:密集检索突破了精确单词匹配的限制,能够捕捉文本深层次的含义和上下文,处理转述、同义词以及表达相同意思的不同方式。在现实场景中,用户可能会用各种不同的表述来描述同一个概念,这时密集检索的优势就体现出来了。
  2. 对同义词和释义的鲁棒性:传统的基于关键字的检索方法(如BM25),在查询和文档使用不同词汇表达同一概念时,可能会失效。而密集检索通过映射语义含义,即使单词不匹配,也能找到相关内容的文档。
  3. 上下文敏感性:密集嵌入对上下文很敏感。像BERT这样的预训练模型,能够理解单词在句子中的位置以及与其他单词的交互方式。所以,同一个单词在不同的上下文中,会有不同的嵌入。例如,在“Apple is a great company”和“I love eating apples”这两句话中,“apple”这个词的嵌入就会不同,因为它在不同句子里代表不同的含义。

密集检索的挑战

  1. 计算复杂性:密集检索方法需要大量的计算资源。为大型语料库生成嵌入不仅耗时,还会占用大量内存,毕竟像BERT或句子转换器这类现代模型的计算量很大。而且,在高维嵌入(比如768维)中进行搜索,也会消耗大量资源。
  2. 缺少关键字精度:虽然密集检索在理解含义方面表现出色,但可能会忽略包含特定关键字的文档。在一些情况下,精确的关键字匹配非常关键,尤其是专业查询或者特定术语很重要的时候。
  3. 训练要求:许多密集检索模型需要针对特定任务或领域(如医学、法律、技术文档等)进行微调,才能提高准确性。这就需要有标记的训练数据,然而某些领域获取这些数据并不容易。

基于嵌入的检索:代码示例

为了让大家更清楚地了解密集检索的工作原理,下面用代码示例来演示一下,我们会使用transformers创建文档和查询的嵌入,用faiss进行检索:

from transformers import AutoTokenizer, AutoModel
import torch
import faiss

tokenizer = AutoTokenizer.from_pretrained("sentence-transformers/all-MiniLM-L6-v2")
model = AutoModel.from_pretrained("sentence-transformers/all-MiniLM-L6-v2")

def embed_texts(texts):
    inputs = tokenizer(texts, padding=True, truncation=True, return_tensors="pt")
    with torch.no_grad():
        embeddings = model(**inputs).last_hidden_state.mean(dim=1)
    return embeddings

documents = [
    "The cat sat on the mat.",
    "The dog barked at the moon.",
    "The sun is shining bright."
]

doc_embeddings = embed_texts(documents).numpy()
query = "cat on mat"
query_embedding = embed_texts([query]).numpy()

index = faiss.IndexFlatL2(doc_embeddings.shape[1])
index.add(doc_embeddings)

_, top_indices = index.search(query_embedding, k=3)
top_results = [documents[idx] for idx in top_indices[0]]

print("Top 3 documents based on dense retrieval:", top_results)

密集检索中的关键组件

  1. 预训练模型:密集检索在很大程度上依赖像BERT或句子转换器这样的预训练模型,这些模型在大量文本数据上进行训练,学习单词和句子的上下文表示。
  2. 嵌入:嵌入是密集检索的核心所在。它们是编码文本含义的高维向量(通常有几百维)。含义相似的两段文本,其嵌入在向量空间中会比较接近。
  3. 相似性搜索:生成嵌入之后,下一步就是进行相似性搜索。可以使用像FAISS(Facebook AI Similarity Search)这样的库,它针对在大型数据集上进行快速、可扩展的相似性搜索做了优化。

密集检索的应用

密集检索在许多需要理解文本深层次含义的任务中应用广泛,常见的有:

  1. 问答系统:密集检索能够检索出与用户问题语义相关的段落,即便问题的表述有所不同。这对谷歌搜索引擎、聊天机器人等系统来说至关重要。
  2. 法律文件搜索:在法律案件中,虽然精确的措辞可能有差异,但含义往往相似。密集检索可以依据搜索查询的含义,检索相关的案例或法律文件。
  3. 医学信息检索:医学术语和病症的描述方式多种多样,密集检索能够让系统根据含义检索相关信息,在医疗文档搜索和医疗问答中发挥着关键作用。

六、结论

将BM25和基于嵌入的检索(密集检索)相结合,形成了一种高效的混合搜索方法,为检索增强生成(RAG)系统注入强大动力。BM25凭借精确的关键字匹配确保检索的精准度,密集检索则通过捕捉单词背后的深层含义提升语义理解能力。二者相互补充,让系统能基于词汇和上下文的相似性,检索出最相关的信息。

虽然密集检索在计算方面要求较高,且可能需要针对特定领域进行微调,但它理解语义上下文的能力,极大地提高了回复的质量和准确性。这种混合方法让RAG系统能生成更有价值、更具意义的结果,成为现代信息检索领域的得力工具。
## 推荐阅读
1. DeepSeek-R1的顿悟时刻是如何出现的? 背后的数学原理
2. 微调 DeepSeek LLM:使用监督微调(SFT)与 Hugging Face 数据
3. 使用 DeepSeek-R1 等推理模型将 RAG 转换为 RAT
4. DeepSeek R1:了解GRPO和多阶段训练
5. 深度探索:DeepSeek-R1 如何从零开始训练
6. DeepSeek 发布 Janus Pro 7B 多模态模型,免费又强大!

本文由mdnice多平台发布


柏企科技圈
15 声望5 粉丝