Skip to main content

LOTR (Merger Retriever)(LOTR(合并检索器))

Lord of the Retrievers,也被称为MergerRetriever,接受一个检索器列表作为输入,并将它们的get_relevant_documents()方法的结果合并成一个列表。合并后的结果将是一个与查询相关且由不同检索器进行排名的文档列表。

MergerRetriever类可以通过多种方式提高文档检索的准确性。首先,它可以合并多个检索器的结果,有助于减少结果中的偏差风险。其次,它可以对不同检索器的结果进行排名,有助于确保最相关的文档首先返回。

import os
import chromadb
from langchain.retrievers.merger_retriever import MergerRetriever
from langchain.vectorstores import Chroma
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.embeddings import OpenAIEmbeddings
from langchain.document_transformers import (
EmbeddingsRedundantFilter,
EmbeddingsClusteringFilter,
)
from langchain.retrievers.document_compressors import DocumentCompressorPipeline
from langchain.retrievers import ContextualCompressionRetriever

# 获取3个不同的嵌入。
all_mini = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")
multi_qa_mini = HuggingFaceEmbeddings(model_name="multi-qa-MiniLM-L6-dot-v1")
filter_embeddings = OpenAIEmbeddings()

ABS_PATH = os.path.dirname(os.path.abspath(__file__))
DB_DIR = os.path.join(ABS_PATH, "db")

# 实例化2个不同的cromadb索引,每个索引都有不同的嵌入。
client_settings = chromadb.config.Settings(
is_persistent=True,
persist_directory=DB_DIR,
anonymized_telemetry=False,
)
db_all = Chroma(
collection_name="project_store_all",
persist_directory=DB_DIR,
client_settings=client_settings,
embedding_function=all_mini,
)
db_multi_qa = Chroma(
collection_name="project_store_multi",
persist_directory=DB_DIR,
client_settings=client_settings,
embedding_function=multi_qa_mini,
)

# 使用2个不同的嵌入和不同的搜索类型定义2个不同的检索器。
retriever_all = db_all.as_retriever(
search_type="similarity", search_kwargs={"k": 5, "include_metadata": True}
)
retriever_multi_qa = db_multi_qa.as_retriever(
search_type="mmr", search_kwargs={"k": 5, "include_metadata": True}
)

# The Lord of the Retrievers将保存两个检索器的输出,并可以像其他类型的链一样使用。
lotr = MergerRetriever(retrievers=[retriever_all, retriever_multi_qa])

从合并的检索器中删除冗余结果。 (Remove redundant results from the merged retrievers.)

# 我们可以使用另一个嵌入来从两个检索器中删除冗余结果。
# 在不同的步骤中使用多个嵌入可以帮助减少偏差。
filter = EmbeddingsRedundantFilter(embeddings=filter_embeddings)
pipeline = DocumentCompressorPipeline(transformers=[filter])
compression_retriever = ContextualCompressionRetriever(
base_compressor=pipeline, base_retriever=lotr
)

从合并的检索器中选择代表性的文档样本。 (Pick a representative sample of documents from the merged retrievers.)

# 此过滤器将将文档向量划分为意义的“中心”或聚类。
# 然后它将选择与该中心最接近的文档作为最终结果。
# 默认情况下,结果文档将按照聚类进行排序/分组。
filter_ordered_cluster = EmbeddingsClusteringFilter(
embeddings=filter_embeddings,
num_clusters=10,
num_closest=1,
)

# 如果您希望最终文档按照原始检索器的分数进行排序
# 您需要添加“sorted”参数。
filter_ordered_by_retriever = EmbeddingsClusteringFilter(
embeddings=filter_embeddings,
num_clusters=10,
num_closest=1,
sorted=True,
)

pipeline = DocumentCompressorPipeline(transformers=[filter_ordered_by_retriever])
compression_retriever = ContextualCompressionRetriever(
base_compressor=pipeline, base_retriever=lotr
)

重新排序结果以避免性能下降。 (Re-order results to avoid performance degradation.)

无论模型的架构如何,当包含10个以上的检索文档时,性能会显著下降。 简而言之:当模型必须在长上下文中访问相关信息时,它倾向于忽略提供的文档。 参见:https://arxiv.org/abs//2307.03172

# 您可以使用额外的文档转换器在删除冗余后重新排序文档。
from langchain.document_transformers import LongContextReorder

filter = EmbeddingsRedundantFilter(embeddings=filter_embeddings)
reordering = LongContextReorder()
pipeline = DocumentCompressorPipeline(transformers=[filter, reordering])
compression_retriever_reordered = ContextualCompressionRetriever(
base_compressor=pipeline, base_retriever=lotr
)