大模型应用系列:从Ranking到Reranking

原创 精选
人工智能
Reranking是大模型应用中RAG 系统的一个非常关键的步骤,其核点是提高最初检索到的文件的相关性和质量。在最初的检索过程之后,对这些文档进行重新排序和重新组织,目标是确定最相关信息的优先次序,确保在作出回应或决策时使用尽可能好的数据。​

每个搜索引擎背后都隐藏着一个至关重要却往往被忽视的组成部分——Reranking(重新排名)。那么,什么是Rerank呢?简而言之,这一过程旨在优化并调整搜索结果的顺序,使之更加精准地匹配用户的查询需求。值得注意的是,不同情况下采用的Rerank策略差异极大,因为针对每一个具体问题都需要量身定制解决方案。以去年问世的一款名为RankGPT的先进模型为例,它利用大型语言模型对搜索结果进行重新排序,不仅效果显著,而且无需事先针对新数据进行额外训练即可实现。

“治学先治史”,这句话同样适用于技术领域。深入了解Ranking技术的发展历程对于掌握当前最先进的Rerank方法来说很有意义。通过回顾这些系统随时间演变的过程,我们能够更深刻地理解现今所采用的各种解决方案是如何设计出来的,以及它们是如何有效解决实际问题的。

1. 早期的Ranking

早期的搜索引擎,相对简单且但功能有限。当时的技术,就像 BM25一样,主要集中在精确的术语匹配上。这意味着,如果搜索查询中的确切单词没有出现在文档中,即使它正是你想要的,那么该文档就不会被认为是相关的。

这些早期系统的核心是一个名为“TF-IDF”的公式,它基于三个关键因素来确定文档的相关性,其中TF是搜索术语在文档中出现的频率,DF是有多少其他文档包含相同的术语,以及文档的长度。

图片图片

为了标准化文档长度并比较文档与搜索的相关性,在余弦距离中使用了一种称为“向量空间模型”的技术。虽然这种方法为早期的搜索引擎奠定了基础,但是它有一个很大的缺点,那就是它不能理解单词背后的意思或检测同义词。所有东西都必须完全匹配。

例如,如果你搜索一个“悲惨的爱情故事”,一个带有短语“命运多舛的恋人”的文档(如罗密欧与朱丽叶)可能不会出现,即使它正是你想要的。这个问题被称为词汇不匹配问题,是这些早期搜索系统面临的最大挑战之一。随着语言和术语的发展,不同的术语可能指的是同一个概念,但精确匹配系统无法弥合这一差距。

为了克服这个词汇不匹配的问题,出现了一些技巧:

  • 查询扩展(Query Expansion)通过添加相关术语来扩展搜索范围,或者通过用户反馈,或者通过假设排名最高的文档是相关的。它还使用 WordNet 等工具来查找同义词。
  • 文档增强(Document Enrichment)改进了文档的表示方式,特别是在处理语音转录或短文本片段等噪音数据的时候。
  • 超越精确匹配: 早期的语义学方法,如LSA和统计翻译,试图捕捉精确词匹配以外的意义。

然而,真正的突破来自神经网络,它革命性地支持语义匹配,即理解单词背后的意思的能力,即使它们不是完全匹配的搜索。

这种从精确词汇匹配到语义匹配的转变标志着搜索技术的一个转折点。今天的搜索引擎结合了这两种方法来给我们更准确的结果,即使在措辞不完全匹配的情况下,也可以提供相关的内容。

2. LTR的兴起

在神经网络时代之前,像 BM25这样的搜索引擎和类似的方法使用无监督的方法根据词频等因素对文档进行排序。这些系统可以通过数据进行微调,但它们缺乏一种更精确的、基于学习的方法。LTR(Learning to Rank)依赖于从文本本身设计的特征,比如词频、文档长度和 BM25得分。例如,Ranking系统现在可以考虑特定术语在文档的某个距离内出现的频率,或者它们出现在标题等关键领域的频率。这些洞见使得搜索结果更加准确、相关。

图片图片

LTR 模型通常根据它们处理排序任务的方式进行分类:

  • Pointwise: 这种方法查看单个文档,将排名作为分类或回归任务处理。这就像是在问“这份文件到底有没有关联”
  • Pairwise: 这种方法侧重于比较查询/文档对,旨在优化首选项。例如,在文档 A 和文档 B 都相关时,确保文档 A 的级别优于文档 B。
  • Listwise: 这种方法不关注单个文档或对,而是评估整个搜索结果列表,并直接优化排名指标,如标准化折扣累积增益(nDCG) ,它衡量排名结果对用户的有用性。

在2010年前后,LTR达到了顶峰。基于树的模型,特别是梯度增强的决策树,成为优化搜索Ranking的首选解决方案。

LTR 技术为如今更先进的搜索系统铺平了道路。然而,深度学习的到来把事情带到了一个新的水平,提供了更复杂的方法来排序搜索结果,更准确和更深入地理解用户意图。搜索的发展还远未结束,但 LTR 代表着这个过程中的一个重要里程碑。

XGBoost 实现了通过一组目标函数和性能指标执行LTR。它的目标函数是rank:ndcg,基于 LambdaMART 算法,该算法是对 LambdaRank 框架的改造,以适应梯度提升树。下面的代码片段展示了如何实现LTR模型。

from sklearn.datasets import make_classification
import numpy as np

import xgboost as xgb

# Make a synthetic ranking dataset for demonstration
seed = 1994
X, y = make_classification(random_state=seed)
rng = np.random.default_rng(seed)
n_query_groups = 3
qid = rng.integers(0, n_query_groups, size=X.shape[0])

# Sort the inputs based on query index
sorted_idx = np.argsort(qid)
X = X[sorted_idx, :]
y = y[sorted_idx]
qid = qid[sorted_idx]

ranker = xgb.XGBRanker(tree_method="hist", lambdarank_num_pair_per_sample=8, objective="rank:ndcg", lambdarank_pair_method="topk")
ranker.fit(X, y, qid=qid)

3. 深度学习的到来: 搜索排名的新方式

随着深度学习的兴起,搜索排名向前迈进了一大步。有两个关键的突破,首先,连续向量表示允许模型超越简单地精确匹配,并理解词之间更深层次的关系。其次,深度学习减少了对手工标注特征的需求,而特征标注在早期的LTR系统中是一个重大挑战。

在回顾文本Ranking中的深度学习时,可以考虑两个不同的阶段:BERT 之前的模型和基于 Transformer 的模型。在2019年,BERT的引入改变了游戏规则,因为基于 BERT 模型提供了更好的性能。这种区别基本上代表着搜索排名深度学习的两个时代。

3.1 BERT出现之前的神经网络Ranking模型

在 BERT 之前,用于神经排序的模型主要有两种: 基于表示的模型和基于交互的模型。

基于表示的模型分别学习了查询和文档的密集向量表示,并使用余弦距离等指标进行比较。一个早期的例子是深度结构化语义模型(DSSM),它使用字符 n-gram 来创建向量表示。后来,诸如基于 CNN 的 DSSM 和双嵌入空间模型(DESM)通过增加上下文和预先训练的词语嵌入进行了改进。

基于交互的模型侧重于通过使用相似矩阵来捕获查询和文档中特定术语之间的关系。矩阵反映了嵌入查询术语与文件中查询术语的相似程度。这种方法通过使用连续向量来解决词汇不匹配的问题,而不是依赖于精确的词语匹配。

图片图片

这些模型通常遵循两个步骤:

  1. 特征提取: 该模型利用不同的技术分析相似度矩阵,聚合词语的相似度。例如,DRMM 和 KNRM 这样的模型创建了这些相似性的直方图,而更高级的模型,如 MatchPyramid,PACRR 和 convknRM,则包含了位置信息来捕捉单词序列之间的模式。
  2. 相关性评分: 在提取特征之后,该模型将它们结合起来,使用诸如池化和前馈神经网络等技术为查询文档对生成相关性评分。

3.2 Transformer时代的Reranking

随着像 BERT 这样的Transformer模型的兴起,搜索系统中的Ranking已经变得更加复杂,以便提供更加准确和相关的结果。现代信息检索系统通常遵循两个步骤来平衡速度和准确性,其工作原理如下:

  1. 初始检索是一种轻量级、高效的方法,比如 BM25或 DPR,可以快速检索一小组候选文档。这些方法不需要大量的计算,可以快速处理大量的数据。然而,它们的准确性是有限的,因为它们对文档和查询都没有深入的理解。
  2. Reranking: 一旦初始文档被选中,一个更强大的基于神经网络的模型被用来重新排序这些候选项。这些重新排序模型通常基于诸如 BERT 之类的 Transformers,因为它们更深入地分析文档和查询之间的关系,所以更加精确。然而,由于它们的计算成本很高,因此从一开始就在大型数据集上使用它们将花费太长的时间。因此,它们只应用于在第一阶段检索到的较小文档集。

图片图片

这种两阶段方法由于兼顾了效率和准确性而被广泛使用。通过首先检索较小的文档集,系统避免了神经网络的高计算成本,同时仍然受益于它们在Reranking中的高精度。

4. Reranking的实现方法

虽然有许多方法可以应用于Reranking,但基本上可以分为3类:相关性分类、 提炼查询和文档的表示和稠密式表达。

4.1 基于相关性分类的Reranking

一个最简单的方法来处理文本排序是把它作为一个文本分类问题。这个想法是将每个文本分为“相关”和“不相关”两类,然后根据每个文本属于“相关”类别的可能性对结果进行排序。实际上,我们正在训练一个模型来估计给定文本与用户查询相关的概率,然后根据这些概率对文本进行排序。

对于给定的查询 q 和候选文本 d,该模型计算一个代表相关性的得分 s_i,表示为:

图片图片

但是这些候选文本是从哪里来的呢?考虑到典型文本语料库的大小,将这个过程应用到语料库中的每个文档,这在计算上是不可行的。对每个用户查询的数百万个文档运行神经网络推理,这将非常缓慢而且成本高昂。

大多数搜索系统,包括那些使用 BERT 的系统,都遵循检索和Reranking的方法。这意味着系统不会对语料库中的所有文档运行 BERT,而是首先使用更快、更传统的方法(比如 BM25,它依赖于关键字匹配)检索一组更小的候选文本。这个初始阶段称为候选生成或第一阶段检索。

一旦检索到候选项,BERT 就会通过计算每个候选项的相关性得分对他们进行重新排序。这个重新排名的步骤为搜索增加了更深层次的内容和理解,与最初的基于关键字的ranking相比,提高了搜索结果的质量。

例如,使用monoBert模型对查询和每个候选文本进行结构化分析来实现关联分类。对于每个查询 q 和一个候选文档 d,输入序列的结构如下:

图片图片

其中,CLS是BERT 用来表示整个输入的特殊标记;q是用户的查询,已有分词标记;SEP用于分隔段的特殊标记;d _ i: 候选文本的token。这种结构化的输入被称为输入模板,是BERT处理文本的关键部分。BERT然后为该序列中的每个token生成上下文向量表示。

MonoBERT 聚焦于 CLS 令牌的表示,它捕获查询和候选文本之间的总体关系。该表示方法通过一个单层完全连通的神经网络生成候选文档的相关分数 s _ i。有趣的是,monoBERT 只使用 CLS 令牌的表示来计算相关性得分,而忽略了其他令牌的表示。这使模型既简单又有效。

图片图片

MonBERT 的ranking模型通过输入查询和待评分的候选文本(由适当的特殊标记包围) ,使 BERT 适用于相关性分类。MonoBERT 接受一个文本序列作为输入。这个序列包括特殊的标记和需要比较的内容。具体来说,它看起来像这样:

[CLS], query, [SEP], document, [SEP]

CLS 和 SEP 是 BERT 使用的特殊标记,用于识别输入的不同部分在哪里开始和结束。查询是用户输入的文本,而文档是被评估相关性的候选文本。查询标记完全按照用户输入的内容获取。当我们探索表达搜索的不同方式时,这个细节变得非常重要,比如搜索是一个简单的标题还是一个更长的描述。查询被标记为段 A,文档被标记为段 B,这有助于模型理解每个段的角色。

一旦这个输入序列准备好了,它就被传递给 BERT,它处理整个序列并为序列中的每个标记或单词生成一个“上下文表示”。BERT 的工作是根据单词出现的上下文来捕捉它们的意思。在 monoBERT 中,[ CLS ]充当整个序列的模型摘要。最后一步是将这个[ CLS ]令牌输入一个简单的神经网络,以预测文档与查询的相关程度。输出是一个分数,告诉我们该文档匹配的可能性。

这个将查询和文档传递给 BERT 进行比较的过程,称为交叉编码方法。虽然 monoBERT 使用[ CLS ]令牌进行相关性评分,但它忽略了其他token的表示。

要实现交叉编码器reranking,可以参照以下步骤:

  1. 安装必需库,提供使用交叉编码器所必需的工具。pip install -U sentence-transformers
  2. 导入并加载交叉编码器, 器并加载预先训练好的模型
  3. 文档对进行打分: 创建查询和检索到的文档对,然后使用交叉编码器为它们打分
  4. 文档reranking: 根据文档的分数重新排序文档,优先排序最相关的文档
from sentence_transformers import CrossEncoder 
cross_encoder = CrossEncoder('cross-encoder/ms-marco-MiniLM-L-6-v2'
pairs = [[query, doc] 
for doc in retrieved_documents] 
  scores = cross_encoder.predict(pairs) 
  print("Scores:") 
for score in scores:     
  print(score)
print("New Ordering:") 
for o in np.argsort(scores)[::-1]:     
  print(o+1)

MonoBERT 的一个局限是它难以处理较长的输入文本。monoBERT有一个固定的输入长度,这意味着它最适合于较短的文本。因此,虽然 monoBERT 可以有效地对段落长度的文本进行排序,但它还是难以处理完整的新闻文章等较长的文档。它只能处理多达512个token的序列。由于它依赖于位置嵌入来理解令牌的顺序,任何长于512个令牌的输入都会丢失信息,并被视为一个随机的单词集合,从而导致模型失去对文本流动的感知。

BERT 的长度限制给长文本排序带来了两大挑战——

(1)训练

在训练过程中最大的问题是,我们应该将文档的哪一部分输入到模型中?设想一个文档,其中有几个相关的句子分散在整个文档中,或者整个文档感觉像一个完整的包一样相关。我们如何训练 monoBERT 来处理这些不同的情况?如果想建立不同层次的相关性模型,就更为棘手。

由于查询通常很短,而文档很长,我们必须截断文档以适应模型。但如果我们切掉最重要的部分呢?虽然我们可以使用一些技巧,比如只向 BERT 提供文档中包含查询术语的部分,但这仍然让我们猜测我们向模型提供的训练数据是否实际上是文档中最相关的部分。

(2)推理

在推理过程中,当根据搜索查询对文档进行排序时,我们面临相同的长度限制。如果一个文档太长,我们不能简单地将它全部输入 BERT。我们必须将文档分割成块并决定如何处理它们。例如,我们应该把它分成固定大小的部分,还是像句子一样使用自然分割?这些块应该重叠多少?

一旦我们有了块,我们仍然需要一种方法来组合来自文档不同部分的相关性得分。有两种常见的方法: 

  • 分数聚合: 我们得到每个组块的相关性分数并将它们组合(例如,通过获得最高分)。
  • 表征聚合: 我们首先结合每个段落的表征,然后预测一个单一的相关分数。

为了处理 BERT 的长度限制,Dai 和 Callan 在2019年提出了一个解决方案,基本想法是这样的:

  • 训练: 将文件分成重叠的段落,并将相关文件中的每一段落视为相关文件,将不相关文件中的每一段落视为不相关文件。
  • 推理: 使用与上面相同的方法,将文档分割成小块,评估每个小块的相关性,然后组合得分。

具体而言,文档被划分为150个单词的片段,每个片段之间有75个单词的重叠。对于每个段 ,模型像 monoBERT 一样处理它: 查询和段被合并并输入 BERT,使用最终的[ CLS ]标记对段进行评分。在给每篇文章打分之后,我们可以用三种方法把分数组合起来:

  1. BERT–MaxP: 从任何一篇文档中取得最高分。
  2. BERT–FirstP: 使用第一段的分数。
  3. BERT–SumP: 把所有段落的分数加起来。

通过将文档分割成可管理的块,然后聚合结果,我们可以对整个文档进行排序,同时保持在 BERT 的输入长度限制内。

在 monoBERT和 BERT-MaxP 等早期模型中,只使用[ CLS ]标记作为 BERT 用于汇总输入的特殊标记。它用于计算文档与查询的匹配程度。但是,BERT 为查询和文档中的所有单词生成了上下文嵌入。忽略这些嵌入看起来像是错失良机,这正是 MacAvaney 在2019在开发 CEDR (Leveraging Contextual Embedding Ranking)所解决的问题。

图片图片

CEDR 通过保留和使用 BERT 生成的上下文嵌入,这些嵌入可以提供关于文档不同部分与查询相关性的更细微信号。

为了处理超过 BERT 512 token限制的文档,CEDR 将文档分成更小的、可管理的块。这些块(包括查询和特殊的分隔符标记)由 BERT 逐个处理。处理完所有数据块后,CEDR 从每个数据块获取[ CLS ]表示,并对它们进行平均,以创建文档级[ CLS ]表示(一种称为平均池的技术)。CEDR 不仅仅使用[ CLS ]令牌,还保留每个块的上下文嵌入,将这些内容连接起来,形成整个文档的上下文嵌入完整序列。

一旦创建了文档级嵌入,CEDR 通过比较每个文档项的嵌入和每个查询项的嵌入来构造相似矩阵。这些矩阵捕获文档不同部分与查询不同部分的匹配程度,比单独使用[ CLS ]令牌提供了更深入的相关性理解。

通过将 BERT 强大的嵌入到现有的神经排序模型中,CEDR 可以一块一块地处理较长的文档,同时仍然保持模型端到端的可训练性。这不仅解决了 BERT 的长度限制,而且允许 CEDR 充分利用 BERT 提供的丰富上下文信息,使其在文档排序任务中具有优势。

4.2. 提炼查询和文档的表示

信息检索中最大的挑战之一是词汇不匹配问题,即搜索内容和文档使用不同的词来描述同一个概念。

依赖于精确匹配的传统ranking模型,如 BM25,如果相关文档不包含查询中的确切单词,则无论相关性如何,都不会检索到该文档。基于神经网络的ranking模型,特别是那些使用连续向量表示的模型,通过学习“软”匹配提供了一个潜在的解决方案。他们不依赖于精确的词语匹配,而是能够识别词语之间的语义关系。

然而,在这些模型中仍然存在一个主要的瓶颈即初始候选项的生成阶段。大多数Ranking系统使用多阶段过程,第一阶段检索候选文档(通常使用像 BM25这样的精确匹配系统) ,第二阶段使用更强大的模型(如 BERT)对候选文档进行Reranking。但是,如果第一阶段由于缺乏匹配的术语而无法检索到相关文档,那么无论Reranking多么优秀,都无法检索到一开始没有出现的内容。

虽然相关文档与查询没有重叠的情况并不常见,但遗漏关键术语的情况很常见。这个问题的强力解决方案是简单地增加在第一阶段检索到的候选文档的数量,希望相关文档最终会出现在更深入的结果中。但这种解决方案有其自身的缺点,即延迟的增加。随着需要处理的候选文档越来越多,Reranking变得越来越慢。

真正的解决方案是改进或增强查询和文档的表示方式,这样它们就能更紧密地与用户的搜索意图保持一致。这可以通过诸如查询扩展和文档扩展等技术来实现。文档扩展通过向文档添加额外的术语来更充分地表示其内容或将其与潜在查询联系起来。它将同义词或相关术语添加到查询本身,可以增加查找可能使用不同单词的相关文档的机会。查询扩展则相反,它将同义词或相关术语添加到查询本身,同样可以增加找到可能使用不同单词的相关文档的机会。

这两种方法都有助于解决词汇表不匹配的问题,因为它们都增加了查询和相关文档之间匹配的可能性。

在大模型应用中,可以通过提示词完成Query 变换(详见大模型应用系列:Query 变换的示例浅析)。其中,一个Query改写的示例如下:

rewrite_prompt= ChatPromptTemplate.from_template("""Provide a better search query for web search engine to answer the given question, end the queries with ’**’. Question: {x} Answer:""")

def parse_rewriter_output(message):
return message.content.strip('"').strip("**")
rewriter = rewrite_prompt| llm| parse_rewriter_output

@chain
def qa_rrr(input):
    # rewrite the query
    new_query = rewriter.invoke(input)
    # fetch relevant documents
docs = retriever.get_relevant_documents(new_query)
# format prompt
formatted = prompt.invoke({"context": docs, "question": input})
# generate answer
answer = llm.invoke(formatted)
return answer
# run
qa_rrr.invoke("what is langchain?")

请注意,示例中用一个LLM将用户初始查询重写为更清晰的查询,然后被传递给检索器以获取最相关的文档。这种方法的缺点是,它在链中引入了额外的延迟,需要按顺序执行两个LLM调用。

与query变换相比,使用查询预测进行文档扩展提供了一种不同的方法。文档扩展并不新鲜,它已经存在了几十年,但是2019年 Nogueira 等人引入 doc2query 后,使用神经网络进行了演进。

Doc2query 使用序列到序列模型(一种为语言翻译等任务设计的神经网络) ,获取一段文本并生成与文档相关的查询。这些查询基于真实世界的数据,这些数据中成对的查询和相关文档被用来训练模型。一旦模型得到训练,它就可以预测语料库中每个文档的有哪些查询。然后,这些预测的查询将被添加到原始的文档文本中。这些“扩展文档”现在不仅包含原始内容,还包含潜在的搜索查询,这提高了它们在初始搜索阶段被检索到的机会。在对这些扩展文档进行索引和检索时,系统能够更好地检索相关信息。这种技术可以无缝地集成到任何多阶段排序流水线的第一阶段检索中,并且可以与我们前面讨论的Reranking模型完美地一起工作。

Doc2query的实现示例代码如下所示:

sample_doc = "xxx"

import pyterrier_doc2query
doc2query = pyterrier_doc2query.Doc2Query()
doc2query([{"docno" : "d1", "text" : sample_doc}])

Doc2Query 对象有一个 change ()函数,它接受每个文档的文本,并为该文本提出问题。生成的数据框架将有一个额外的“ querygen”列,其中包含生成的查询。有很多方法可以将 Doc2query 引入 PyTerrier 检索过程。我们可以通过传递另一个huggingface 模型名称(或文件系统上模型的路径)作为第一个参数来加载另一个T5模型。

doc2query = pyterrier_doc2query.Doc2Query('some/other/model')
import pyterrier as pt 
pt.init() 
dataset = pt.get_dataset("irds:abc") 
import pyterrier_doc2query 
doc2query = pyterrier_doc2query.Doc2Query(append=True) # append generated queries to the orignal document text 
indexer = doc2query >> pt.IterDictIndexer(index_loc) 
indexer.index(dataset.get_corpus_iter())

4.3 Reranking的稠密表达

深度学习给文本排名带来的最大突破之一,是从依赖稀疏信号(主要依赖精确的关键词匹配)转向能够捕捉单词背后含义的连续密集表示。这一飞跃使我们能够更自然、更灵活地建立相关性模型,解决诸如词汇不匹配这样的挑战。

在基于稠密表达的检索技术中,Ranking直接在矢量表示(通常由Transformer生成)上执行。基于稠密表达检索通过比较语义内容,本质上就是比较嵌入在这些向量中的“意义”,而不是仅仅匹配出现在查询和文本中的词,这代表了面向基于关键字检索的一个重大转变。

自从词嵌入出现以来,稠密表达已经显示出它们的潜力,这引发了我们现在所说的自然语言处理(NLP)中的“神经网络革命”。单词嵌入表明机器能够理解单词之间的关系,证明语言可以用一种捕捉意义的方式来表示,而不仅仅是表面量上的匹配。

然而,当我们把这个概念从单个单词扩展到更大的文本块(如短语、句子或整个文档)时,事情就变得复杂起来,同样的挑战也出现在文本ranking/reranking中。

5. 基于大模型的Reranking实现示例

随着大模型应用的普及,我们可以利用 GPT-4或国内的其他大语言模型以更细致的方式评估文档的相关性,实现Reranking。基于 LLM 的Reranking工作流程如下:

  1. 初始检索: 系统根据查询检索一组初始文档。
  2. 查询/文档对的创建: 每个检索到的文档都与查询配对。
  3. 得分: LLM 评估每一对并分配一个相关得分。
  4. reranking: 根据分数对文档进行排序。
  5. 选择: 选择得分最高的文档作为最终答复。

基于 LLM 的Reranking实现示例如下:

from langchain.docstore.document import Document
from langchain_openai import ChatOpenAI
from typing import List
from pydantic import BaseModel, Field
from langchain import PromptTemplate

class RatingScore(BaseModel):
    relevance_score: float = Field(..., descriptinotallow="The relevance score of a document to a query.")

def rerank_documents(query: str, docs: List[Document], top_n: int = 3) -> List[Document]:
    prompt_template = PromptTemplate(
        input_variables=["query", "doc"],
        template="""On a scale of 1-10, rate the relevance of the following document to the query. Consider the specific context and intent of the query, not just keyword matches.
        Query: {query}
        Document: {doc}
        Relevance Score:"""
    )
    
    llm = ChatOpenAI(temperature=0, model_name="gpt-4", max_tokens=4000)
    llm_chain = prompt_template | llm.with_structured_output(RatingScore)
    
    scored_docs = []
    for doc in docs:
        input_data = {"query": query, "doc": doc.page_content}
        score = llm_chain.invoke(input_data).relevance_score
        try:
            score = float(score)
        except ValueError:
            score = 0  # Default score if parsing fails
        scored_docs.append((doc, score))
    
    reranked_docs = sorted(scored_docs, key=lambda x: x[1], reverse=True)
    return [doc for doc, _ in reranked_docs[:top_n]]

在本示例中,LLM 评估每个文档与查询的相关性,对它们进行评分,并根据这些评分选择最高的文档。

6. 小结

通过回顾信息检索的历史,我们可以了解到从Ranking到Reranking的演进过程。如今,Reranking是大模型应用中RAG 系统的一个非常关键的步骤,其核点是提高最初检索到的文件的相关性和质量。在最初的检索过程之后,对这些文档进行重新排序和重新组织,目标是确定最相关信息的优先次序,确保在作出回应或决策时使用尽可能好的数据。

责任编辑:武晓燕 来源: 喔家ArchiSelf
相关推荐

2024-06-18 08:21:31

2024-08-26 11:31:15

2023-12-29 13:45:57

2024-05-28 09:24:32

2024-02-05 14:12:37

大模型RAG架构

2024-08-05 09:18:21

2024-05-06 07:58:23

MoE模型系统

2015-04-20 14:27:40

大数据奇特应用

2024-06-17 07:46:01

2015-11-26 10:20:17

F5应用交付

2023-02-28 13:09:53

训练模型

2017-07-11 10:19:24

浅层模型机器学习优化算法

2024-11-04 00:24:56

2023-06-24 19:59:40

2023-11-06 10:07:22

模型人工智能数字

2024-01-02 07:15:59

大模型数据库企业知识管家

2015-12-18 16:19:47

私有云从VMwareOpenStack

2024-04-02 07:25:19

大语言模型青少年编程NLG
点赞
收藏

51CTO技术栈公众号