老铁们,今天咱们聊聊如何用多向量检索器(MultiVectorRetriever)来提升文档检索效果。某些情况下,存储一个文档的多个向量可以带来很多好处,比如我们可以对文档的多个片段进行嵌入,并将这些嵌入与父文档关联,允许在子片段上的检索命中返回整个更大的文档。
LangChain提供了一个名为baseMultiVectorRetriever的实现,它简化了这个过程。事实上,很多复杂性都在于如何为每个文档创建多个向量。今天我们会聊到几种常见的方法来生成这些向量,并如何使用MultiVectorRetriever。
技术背景介绍
创建多个向量的方法包括:
- 较小的片段:将文档分割为较小的块,然后对这些块进行嵌入(这是ParentDocumentRetriever的一部分功能)。
- 摘要:为每个文档创建一个摘要,并对摘要进行嵌入(可以替代或补充原始文档)。
- 假设性问题:生成适用于每个文档的假设性问题,并对这些问题进行嵌入。
此外,你还可以手动添加需要嵌入的内容,比如特定问题或查询,这样可以更精准地控制检索结果。
原理深度解析
我们先从一个例子开始,初始化一些文档,然后将它们索引到一个内存中的Chroma向量存储中,使用OpenAI嵌入。实际上,任何LangChain支持的向量存储或嵌入模型都可以使用。
%pip install --upgrade --quiet langchain-chroma langchain langchain-openai > /dev/null
from langchain.storage import InMemoryByteStore
from langchain_chroma import Chroma
from langchain_community.document_loaders import TextLoader
from langchain_openai import OpenAIEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter
loaders = [
TextLoader("paul_graham_essay.txt"),
TextLoader("state_of_the_union.txt"),
]
docs = []
for loader in loaders:
docs.extend(loader.load())
text_splitter = RecursiveCharacterTextSplitter(chunk_size=10000)
docs = text_splitter.split_documents(docs)
vectorstore = Chroma(
collection_name="full_documents", embedding_function=OpenAIEmbeddings()
)
实战代码演示
我们使用较小的片段进行嵌入。这个过程类似于ParentDocumentRetriever的内部机制。这里我们展示了一些幕后操作。
import uuid
from langchain.retrievers.multi_vector import MultiVectorRetriever
store = InMemoryByteStore()
id_key = "doc_id"
retriever = MultiVectorRetriever(
vectorstore=vectorstore,
byte_store=store,
id_key=id_key,
)
doc_ids = [str(uuid.uuid4()) for _ in docs]
child_text_splitter = RecursiveCharacterTextSplitter(chunk_size=400)
sub_docs = []
for i, doc in enumerate(docs):
_id = doc_ids[i]
_sub_docs = child_text_splitter.split_documents([doc])
for _doc in _sub_docs:
_doc.metadata[id_key] = _id
sub_docs.extend(_sub_docs)
retriever.vectorstore.add_documents(sub_docs)
retriever.docstore.mset(list(zip(doc_ids, docs)))
优化建议分享
对于在实际应用中提高检索效果,建议你在检索时使用Max Marginal Relevance(最大边际相关性)方法,增强检索多样性。
from langchain.retrievers.multi_vector import SearchType
retriever.search_type = SearchType.mmr
补充说明和总结
同时,我们也可以为文档生成摘要并进行嵌入,这可以更准确地获取文档的主题。你甚至可以使用LLM生成假设性问题来提升检索的语义关联性。
说白了,通过多向量检索器,你大可以像做魔术一般灵活控制你的文档检索,实现更高效的信息获取。
今天的技术分享就到这里,希望对大家有帮助。开发过程中遇到问题也可以在评论区交流~
—END—