EnsembleRetriever中的倒数融合排序算法
langchain目前最常见的检索器retriever是BM25、密集检索器,然而两者优势各异。稀疏检索器擅长基于关键词查找相关文档,而密集检索器擅长基于语义相似性查找相关文档。
最常见的模式是将稀疏检索器(如BM25)与密集检索器(如嵌入相似度)结合起来,实现优势互补,也被称为“混合搜索”。
这里首先介绍多种检索结果混合常用的倒数融合排序算法RRF,然后进一步介绍langchain EnsembleRetriever中的RRF的实现,所用到的代码示例参考和修改自网络资料。
1 倒数融合排序
1.1 RRF概要
如上所述,目前有多种排序结果,比如BM25,密集向量检索,那到底该优先采用那种排序结果。
用户有可能不理解这些排序算法的确切分布(或排序分数),不确定那种检索后排序更合理。
倒数融合排序(RRF, Reciprocal Rank Fusion),它忽略了各个检索的确切分数,也不考虑这些检索结果的分数。
RRF仅考虑每个检索排序结果中的每个文档的位置,比如在这个检索排序结果中文档d处于第几名,基于这些信息重新计算RRF分数,如此RRF通过这种简化逻辑,讲所有检索结果有效混合起来。
1.2 RRF计算公式
这里更进一步,细化每个文档d的RRF分数的计算过程。
对于某个检索到的给定文档d,d必然在某个query检索到的有序结果中。
query可以是BM25,也可以是密集检索。
这里将query当作r,r(d)表示在本次query有序结果中d所处的排名位置,比如0、1、5的目光/
对于每次检索,对d的RRF score贡献表示为1/(k+r(d)),其中k为调节参数,通常为60。
对于某个检索到的给定文档d,RRF的计算公式如下所示,
D - 文档集
R - 一组排名作为 1..|D| 的排列
K - 通常默认设置为 60
1.3 RRF计算伪码
如果还是对RRF不理解,可以参考如下网络文档给出的解释RRF计算过程的伪码。
score = 0.0
for q in queries:if d in result(q):score += 1.0 / ( k + rank( result(q), d ) )
return score# where
# k is a ranking constant
# q is a query in the set of queries
# d is a document in the result set of q
# result(q) is the result set of q
# rank( result(q), d ) is d's rank within the result(q) starting from 1
2 EnsembleRetriever
2.1 EnsembleRetriever简述
langchain EnsembleRetriever采用RRF算法,混合langchain的BM25、faiss、SQL等多种retriever的检索结果。
EnsembleRetriever接受多个retriever组成的列表作为输入,并根据各自的get_relevant_documents()进行集成,根据Reciprocal Rank Fusion算法重新计算排序分数。
EnsembleRetriever虽然忽略各自检索器的排序打分,但客观上有效利用了各自retriever的算法优势,相比任何单一retriever算法,排序结果更均衡,实际上排序结果更好。
2.2 EnsembleRetriever RRF
langchain EnsembleRetriever中的RRF的实现示例如下所示,明确使用了RRF融合各个retriever的检索结果,整体检索结果更均衡和客观。
def weighted_reciprocal_rank(self,doc_lists: list[list[Document]],) -> list[Document]:"""Perform weighted Reciprocal Rank Fusion on multiple rank lists.You can find more details about RRF here:https://plg.uwaterloo.ca/~gvcormac/cormacksigir09-rrf.pdf.Args:doc_lists: A list of rank lists, where each rank list contains unique items.Returns:The final aggregated list of items sorted by their weighted RRFscores in descending order."""if len(doc_lists) != len(self.weights):msg = "Number of rank lists must be equal to the number of weights."raise ValueError(msg)# Associate each doc's content with its RRF score for later sorting by it# Duplicated contents across retrievers are collapsed & scored cumulativelyrrf_score: dict[str, float] = defaultdict(float)for doc_list, weight in zip(doc_lists, self.weights, strict=False):for rank, doc in enumerate(doc_list, start=1):rrf_score[(doc.page_contentif self.id_key is Noneelse doc.metadata[self.id_key])] += weight / (rank + self.c)# Docs are deduplicated by their contents then sorted by their scoresall_docs = chain.from_iterable(doc_lists)return sorted(unique_by_key(all_docs,lambda doc: (doc.page_contentif self.id_key is Noneelse doc.metadata[self.id_key]),),reverse=True,key=lambda doc: rrf_score[doc.page_content if self.id_key is None else doc.metadata[self.id_key]],)
https://github.com/langchain-ai/langchain/blob/master/libs/langchain/langchain_classic/retrievers/ensemble.py
2.3 EnsembleRetriever检索示例
EnsembleRetriever的多retriever混合检索应用示例和python代码实现,请移步如下链接。
https://blog.csdn.net/liliang199/article/details/153331761
reference
---
Elasticsearch:倒数排序融合 - Reciprocal rank fusion (RRF)
https://zhuanlan.zhihu.com/p/678234806
Reciprocal Rank Fusion outperforms Condorcet and individual Rank Learning Methods
https://cormack.uwaterloo.ca/cormacksigir09-rrf.pdf
langchain基于EnsembleRetriever实现多检索器集成
https://blog.csdn.net/liliang199/article/details/153331761
集成检索器 Ensemble Retriever
http://docs.autoinfra.cn/docs/modules/data_connection/retrievers/ensemble
es混合检索与langchain检索增强
https://zhuanlan.zhihu.com/p/665097446
Ensemble Retriever的代码实现
https://colab.research.google.com/github/LangChain-OpenTutorial/LangChain-OpenTutorial/blob/main/10-Retriever/03-EnsembleRetriever.ipynb
