【LangChain】Chapter10 - Retrieval

说在前面

上一节,我们介绍了语义搜索的基础知识,并做了一些实践案例,可以看到在有些情况下效果不错,但同时也能看到存在一些边缘情况。本节将介绍 检索(Retrieval)以及讲解一些解决这些边缘案例的高级方法。(视频时长11:48)

Main Content

检索(Retrieval)是检索增强生成(Retrieval Augmented Generation,RAG)的核心,指根据用户的问题去向量数据库中搜索与问题相关的文档内容。当我们访问和查询向量数据库时可能会运用到以下技术:

  • 基本语义相似度(Basic semantic similarity)
  • 最大边际相关性(Maximum marginal relevance,MMR)
  • 过滤元数据
  • LLM辅助检索

本节将介绍几种检索方法,以及解决前述’Failure modes‘的技巧。

在这里插入图片描述

前置工作

1.导入环境变量和 OPENAI API

import os
import openai
import sys
sys.path.append('../..')

from dotenv import load_dotenv, find_dotenv
_ = load_dotenv(find_dotenv()) # read local .env file

openai.api_key  = os.environ['OPENAI_API_KEY']

Vectorstore retrieval

Similarity Search

1.导入 Chroma 和 OpenAIEmbeddings,并做好初始化,文件内容使用的是上一节存储在 'docs/chroma/' 中的讲义数据。

from langchain.vectorstores import Chroma
from langchain.embeddings.openai import OpenAIEmbeddings
persist_directory = 'docs/chroma/'
embedding = OpenAIEmbeddings()
vectordb = Chroma(
    persist_directory=persist_directory,
    embedding_function=embedding
)

2.向量存储中一共有 209 个内容。

print(vectordb._collection.count())

在这里插入图片描述

3.下面设定 texts,并将其进行向量存储到 smalldb 中。用来做该部分的演示。

texts = [
    """The Amanita phalloides has a large and imposing epigeous (aboveground) fruiting body (basidiocarp).""",
    """A mushroom with a large fruiting body is the Amanita phalloides. Some varieties are all-white.""",
    """A. phalloides, a.k.a Death Cap, is one of the most poisonous of all known mushrooms.""",
]
smalldb = Chroma.from_texts(texts, embedding=embedding)

4.设定问题。

question = "Tell me about all-white mushrooms with large fruiting bodies"

5.在 samllbd 中做语义相似查询,k 设定为 2 返回两条相似度最高的 chunk。

smalldb.similarity_search(question, k=2)

在这里插入图片描述

我们可以看到,相似性搜索 similarity_search 返回两个文档,是texts的第一句和第二句。它们都与用户问题有关,且含义非常相近,其实只返回其中一句足以满足要求。

Addressing Diversity: Maximum marginal relevance

smallbd 中做最大边际相关搜索 MMR,设定返回的 chunk 数为 k=2

smalldb.max_marginal_relevance_search(question,k=2, fetch_k=3)

在这里插入图片描述

最大边际相关(Maximum Marginal Relevance)是实现多样性检索的常用算法。

MMR的介绍

MMR会平衡结果的相关性(relevance)和多样性(diversity),以避免返回内容冗余的结果。
LangChain 的 max_marginal_relevance_search 函数使用MMR算法初步检索 fetch_k 个文档,选择前k个返回。
在这里插入图片描述

下面是一个MMR与SS(语义相似度搜索)的效果对比。

question = "what did they say about matlab?"
docs_ss = vectordb.similarity_search(question,k=3)
docs_ss[0].page_content[:100]

在这里插入图片描述

docs_ss[1].page_content[:100]

在这里插入图片描述

docs_mmr = vectordb.max_marginal_relevance_search(question,k=3)
docs_mmr[0].page_content[:100]

在这里插入图片描述

docs_mmr[1].page_content[:100]

在这里插入图片描述

Addressing Specificity: working with metadata

上一节的 Failure modes中要求只在Lecture03中搜索答案,但是结果中额外包含 Lecture01 和Lecture02 的内容。我们可以通过过滤元数据(metadata)的方式实现精准搜索,从而解决这个问题。

1.设定问题,要求查找 Lecture03 中的内容。

question = "what did they say about regression in the third lecture?"

2.初始化语义相似度搜索,添加过滤器 filter 设定对 metadata 进行过滤,要求只能来源只能是 Lecture03。

docs = vectordb.similarity_search(
    question,
    k=3,
    filter={"source":"docs/cs229_lectures/MachineLearning-Lecture03.pdf"}
)

3.打印查找到的文档可以看到过滤后,就都来自 Lecture03。

for d in docs:
    print(d.metadata)

在这里插入图片描述

Addressing Specificity: working with metadata using self-query retriever

上面的 filter 中的过滤条件是手动设置的,有没有方法可以准确识别问题中的语义从而实现元数据自动过滤呢?LangChain提供 SelfQueryRetriever 解决这个问题,它使用LLM提取query 和 filter。

1.导入 SelfQueryRetriever

from langchain.llms import OpenAI
from langchain.retrievers.self_query.base import SelfQueryRetriever
from langchain.chains.query_constructor.base import AttributeInfo

2.定义元数据过滤条件。metadata_field_info 参数用于描述元数据中的字段及其类型和含义。这个参数的设置对检索器如何解析查询并匹配元数据非常关键。根据 metadata_field_info 信息,LLM会自动从用户问题中提取 query 和 filter,然后向量数据库基于这两项去搜索相关内容。

metadata_field_info = [
    AttributeInfo(
        name="source",
        description="The lecture the chunk is from, should be one of `docs/cs229_lectures/MachineLearning-Lecture01.pdf`, `docs/cs229_lectures/MachineLearning-Lecture02.pdf`, or `docs/cs229_lectures/MachineLearning-Lecture03.pdf`",
        type="string",
    ),
    AttributeInfo(
        name="page",
        description="The page from the lecture",
        type="integer",
    ),
]

document_content_description = "Lecture notes"
llm = OpenAI(model='gpt-3.5-turbo-instruct', temperature=0)
retriever = SelfQueryRetriever.from_llm(
    llm,
    vectordb,
    document_content_description,
    metadata_field_info,
    verbose=True
)

3.LLM提取 query 和filter,query=‘regression’ 来自用户问题,filter是 LLM 根据 metadata_field_info和用户问题设定的过滤条件。

question = "what did they say about regression in the third lecture?"
docs = retriever.get_relevant_documents(question)

在这里插入图片描述

4.可以看到成功的按照我们的要求进行了查找。

for d in docs:
    print(d.metadata)

在这里插入图片描述

Additional tricks: compression

另一种提高检索文档质量的方法是压缩。向量数据库会返回与问题相关的 chunk 中的所有内容,其中包含大量不相关的信息。LangChain提供 ContextualCompressionRetriever 对返回的完整chunk进行压缩,提取与用户问题相关的内容。可以有效提升输出质量,减少计算资源的浪费。

在这里插入图片描述

1.导入 ContextualCompressionRetrieverLLMChainExtractor

from langchain.retrievers import ContextualCompressionRetriever
from langchain.retrievers.document_compressors import LLMChainExtractor
def pretty_print_docs(docs):
    print(f"\n{'-' * 100}\n".join([f"Document {i+1}:\n\n" + d.page_content for i, d in enumerate(docs)]))

3.设定压缩器。

# Wrap our vectorstore
llm = OpenAI(temperature=0, model="gpt-3.5-turbo-instruct")
compressor = LLMChainExtractor.from_llm(llm)

4.进行压缩并打印。

compression_retriever = ContextualCompressionRetriever(
    base_compressor=compressor,
    base_retriever=vectordb.as_retriever()
)
question = "what did they say about matlab?"
compressed_docs = compression_retriever.get_relevant_documents(question)
pretty_print_docs(compressed_docs)

在这里插入图片描述

我们定义了压缩器LLMChainExtractor,负责从向量数据库返回的chunk中提取信息。ContextualCompressionRetriever有两个参数,一个是压缩器LLMChainExtractor实例,另一个是vectordb检索器。

Combining various techniques

这里我们把上面的几种方法结合一下(Compression + metadata)

compression_retriever = ContextualCompressionRetriever(
    base_compressor=compressor,
    base_retriever=vectordb.as_retriever(search_type = "mmr")
)
question = "what did they say about matlab?"
compressed_docs = compression_retriever.get_relevant_documents(question)
pretty_print_docs(compressed_docs)

Other types of retrieval

vectordb并不是LangChain唯一的检索器,LangChain还提供了其他检索文档的方式,例如TF-IDF、SVM。

from langchain.retrievers import SVMRetriever
from langchain.retrievers import TFIDFRetriever
from langchain.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
# Load PDF
loader = PyPDFLoader("docs/cs229_lectures/MachineLearning-Lecture01.pdf")
pages = loader.load()
all_page_text=[p.page_content for p in pages]
joined_page_text=" ".join(all_page_text)

# Split9
text_splitter = RecursiveCharacterTextSplitter(chunk_size = 1500,chunk_overlap = 150)
splits = text_splitter.split_text(joined_page_text)
# Retrieve
svm_retriever = SVMRetriever.from_texts(splits,embedding)
tfidf_retriever = TFIDFRetriever.from_texts(splits)
question = "What are major topics for this class?"
docs_svm=svm_retriever.get_relevant_documents(question)
docs_svm[0]

在这里插入图片描述

question = "what did they say about matlab?"
docs_tfidf=tfidf_retriever.get_relevant_documents(question)
docs_tfidf[0]

在这里插入图片描述

总结

本节介绍了关于 Retrieval 的内容。这部分的内容也是现在的 RAG 方向比较新的和充满挑战的方向,学习这部分可以多去阅读最新的文献去了解技术的动向。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值