系统评估和优化——Datawhale笔记

评估优化生成部分

在前面的章节中,我们讲到了如何评估一个基于 RAG 框架的大模型应用的整体性能。通过针对性构造验证集,可以采用多种方法从多个维度对系统性能进行评估。但是,评估的目的是为了更好地优化应用效果,要优化应用性能,我们需要结合评估结果,对评估出的 Bad Case 进行拆分,并分别对每一部分做出评估和优化。

RAG 全称为检索增强生成,因此,其有两个核心部分:检索部分和生成部分。检索部分的核心功能是保证系统根据用户 query 能够查找到对应的答案片段,而生成部分的核心功能即是保证系统在获得了正确的答案片段之后,可以充分发挥大模型能力生成一个满足用户要求的正确回答。

优化一个大模型应用,我们往往需要从这两部分同时入手,分别评估检索部分和优化部分的性能,找出 Bad Case 并针对性进行性能的优化。而具体到生成部分,在已限定使用的大模型基座的情况下,我们往往会通过优化 Prompt Engineering 来优化生成的回答。在本章中,我们将首先结合我们刚刚搭建出的大模型应用实例——个人知识库助手,向大家讲解如何评估分析生成部分性能,针对性找出 Bad Case,并通过优化 Prompt Engineering 的方式来优化生成部分。

import sys
sys.path.append("../C3 搭建知识库") # 将父目录放入系统路径中
​
# 使用智谱 Embedding API,注意,需要将上一章实现的封装代码下载到本地
from zhipuai_embedding import ZhipuAIEmbeddings
​
from langchain.vectorstores.chroma import Chroma
from langchain_openai import ChatOpenAI
from dotenv import load_dotenv, find_dotenv
import os
​
_ = load_dotenv(find_dotenv())    # read local .env file
zhipuai_api_key = os.environ['ZHIPUAI_API_KEY']
OPENAI_API_KEY = os.environ["OPENAI_API_KEY"]
​
# 定义 Embeddings
embedding = ZhipuAIEmbeddings()
​
# 向量数据库持久化路径
persist_directory = '../../data_base/vector_db/chroma'
​
# 加载数据库
vectordb = Chroma(
    persist_directory=persist_directory,  # 允许我们将persist_directory目录保存到磁盘上
    embedding_function=embedding
)
​
# 使用 OpenAI GPT-3.5 模型
llm = ChatOpenAI(model_name = "gpt-3.5-turbo", temperature = 0)

可以先使用初始化的Prompt创建一个基于模板的检索链:

from langchain.prompts import PromptTemplate
from langchain.chains import RetrievalQA
​
​
template_v1 = """使用以下上下文来回答最后的问题。如果你不知道答案,就说你不知道,不要试图编造答
案。最多使用三句话。尽量使答案简明扼要。总是在回答的最后说“谢谢你的提问!”。
{context}
问题: {question}
"""
​
QA_CHAIN_PROMPT = PromptTemplate(input_variables=["context","question"],
                                 template=template_v1)
​
​
​
qa_chain = RetrievalQA.from_chain_type(llm,
                                       retriever=vectordb.as_retriever(),
                                       return_source_documents=True,
                                       chain_type_kwargs={"prompt":QA_CHAIN_PROMPT})

测试一下结果:

question = "什么是南瓜书"
result = qa_chain({"query": question})
print(result["result"])
南瓜书是对《机器学习》(西瓜书)中比较难理解的公式进行解析和补充推导细节的书籍。南瓜书的最佳使用方法是以西瓜书为主线,遇到推导困难或看不懂的公式时再来查阅南瓜书。谢谢你的提问!

1.提升直观回答质量

寻找 Bad Case 的思路有很多,最直观也最简单的就是评估直观回答的质量,结合原有资料内容,判断在什么方面有所不足。例如,上述的测试我们可以构造成一个 Bad Case:

问题:什么是南瓜书
初始回答:南瓜书是对《机器学习》(西瓜书)中难以理解的公式进行解析和补充推导细节的一本书。谢谢你的提问!
存在不足:回答太简略,需要回答更具体;谢谢你的提问感觉比较死板,可以去掉

我们再针对性地修改 Prompt 模板,加入要求其回答具体,并去掉“谢谢你的提问”的部分:

template_v2 = """使用以下上下文来回答最后的问题。如果你不知道答案,就说你不知道,不要试图编造答
案。你应该使答案尽可能详细具体,但不要偏题。如果答案比较长,请酌情进行分段,以提高答案的阅读体验。
{context}
问题: {question}
有用的回答:"""
​
QA_CHAIN_PROMPT = PromptTemplate(input_variables=["context","question"],
                                 template=template_v2)
qa_chain = RetrievalQA.from_chain_type(llm,
                                       retriever=vectordb.as_retriever(),
                                       return_source_documents=True,
                                       chain_type_kwargs={"prompt":QA_CHAIN_PROMPT})
​
question = "什么是南瓜书"
result = qa_chain({"query": question})
print(result["result"])
南瓜书是一本针对周志华老师的《机器学习》(西瓜书)的补充解析书籍。它旨在对西瓜书中比较难理解的公式进行解析,并补充具体的推导细节,以帮助读者更好地理解机器学习领域的知识。南瓜书的内容是以西瓜书为前置知识进行表述的,最佳使用方法是在遇到自己推导不出来或者看不懂的公式时来查阅。南瓜书的编写团队致力于帮助读者成为合格的“理工科数学基础扎实点的大二下学生”,并提供了在线阅读地址和最新版PDF获取地址供读者使用。

可以看到,改进后的 v2 版本能够给出更具体、详细的回答,解决了之前的问题。但是我们可以进一步思考,要求模型给出具体、详细的回答,是否会导致针对一些有要点的回答没有重点、模糊不清?我们测试以下问题:

question = "使用大模型时,构造 Prompt 的原则有哪些"
result = qa_chain({"query": question})
print(result["result"])
在使用大型语言模型时,构造Prompt的原则主要包括编写清晰、具体的指令和给予模型充足的思考时间。首先,Prompt需要清晰明确地表达需求,提供足够的上下文信息,以确保语言模型准确理解用户的意图。这就好比向一个对人类世界一无所知的外星人解释事物一样,需要详细而清晰的描述。过于简略的Prompt会导致模型难以准确把握任务要求。
​
其次,给予语言模型充足的推理时间也是至关重要的。类似于人类解决问题时需要思考的时间,模型也需要时间来推理和生成准确的结果。匆忙的结论往往会导致错误的输出。因此,在设计Prompt时,应该加入逐步推理的要求,让模型有足够的时间进行逻辑思考,从而提高结果的准确性和可靠性。
​
通过遵循这两个原则,设计优化的Prompt可以帮助语言模型充分发挥潜力,完成复杂的推理和生成任务。掌握这些Prompt设计原则是开发者成功应用语言模型的重要一步。在实际应用中,不断优化和调整Prompt,逐步逼近最佳形式,是构建高效、可靠模型交互的关键策略。

可以看到,针对我们关于 LLM 课程的提问,模型回答确实详细具体,也充分参考了课程内容,但回答使用首先、其次等词开头,同时将整体答案分成了4段,导致答案不是特别重点清晰,不容易阅读。因此,我们构造以下 Bad Case:

问题:使用大模型时,构造 Prompt 的原则有哪些
初始回答:略
存在不足:没有重点,模糊不清

针对该 Bad Case,我们可以改进 Prompt,要求其对有几点的答案进行分点标号,让答案清晰具体:

template_v3 = """使用以下上下文来回答最后的问题。如果你不知道答案,就说你不知道,不要试图编造答
案。你应该使答案尽可能详细具体,但不要偏题。如果答案比较长,请酌情进行分段,以提高答案的阅读体验。
如果答案有几点,你应该分点标号回答,让答案清晰具体
{context}
问题: {question}
有用的回答:"""
​
QA_CHAIN_PROMPT = PromptTemplate(input_variables=["context","question"],
                                 template=template_v3)
qa_chain = RetrievalQA.from_chain_type(llm,
                                       retriever=vectordb.as_retriever(),
                                       return_source_documents=True,
                                       chain_type_kwargs={"prompt":QA_CHAIN_PROMPT})
​
question = "使用大模型时,构造 Prompt 的原则有哪些"
result = qa_chain({"query": question})
print(result["result"])

2.标明知识来源,提高可信度

由于大模型存在幻觉问题,有时我们会怀疑模型回答并非源于已有知识库内容,这对一些需要保证真实性的场景来说尤为重要,例如:

question = "强化学习的定义是什么"
result = qa_chain({"query": question})
print(result["result"])
强化学习是一种机器学习方法,旨在让智能体通过与环境的交互学习如何做出一系列好的决策。在强化学习中,智能体会根据环境的状态选择一个动作,然后根据环境的反馈(奖励)来调整其策略,以最大化长期奖励。强化学习的目标是在不确定的情况下做出最优的决策,类似于让一个小孩通过不断尝试来学会走路的过程。强化学习的应用范围广泛,包括游戏玩法、机器人控制、交通优化等领域。在强化学习中,智能体和环境之间不断交互,智能体根据环境的反馈来调整其策略,以获得最大的奖励。

我们可以要求模型在生成回答时注明知识来源,这样可以避免模型杜撰并不存在于给定资料的知识,同时,也可以提高我们对模型生成答案的可信度:

template_v4 = """使用以下上下文来回答最后的问题。如果你不知道答案,就说你不知道,不要试图编造答
案。你应该使答案尽可能详细具体,但不要偏题。如果答案比较长,请酌情进行分段,以提高答案的阅读体验。
如果答案有几点,你应该分点标号回答,让答案清晰具体。
请你附上回答的来源原文,以保证回答的正确性。
{context}
问题: {question}
有用的回答:"""
​
QA_CHAIN_PROMPT = PromptTemplate(input_variables=["context","question"],
                                 template=template_v4)
qa_chain = RetrievalQA.from_chain_type(llm,
                                       retriever=vectordb.as_retriever(),
                                       return_source_documents=True,
                                       chain_type_kwargs={"prompt":QA_CHAIN_PROMPT})
​
question = "强化学习的定义是什么"
result = qa_chain({"query": question})
print(result["result"])
强化学习是一种机器学习方法,旨在让智能体通过与环境的交互学习如何做出一系列好的决策。在这个过程中,智能体会根据环境的反馈(奖励)来调整自己的行为,以最大化长期奖励的总和。强化学习的目标是在不确定的情况下做出最优的决策,类似于让一个小孩通过不断尝试来学会走路的过程。强化学习的交互过程由智能体和环境两部分组成,智能体根据环境的状态选择动作,环境根据智能体的动作输出下一个状态和奖励。强化学习的应用非常广泛,包括游戏玩法、机器人控制、交通管理等领域。【来源:蘑菇书一语二语二强化学习教程】。

但是,附上原文来源往往会导致上下文的增加以及回复速度的降低,我们需要根据业务场景酌情考虑是否要求附上原文。

3.构造思维链

大模型往往可以很好地理解并执行指令,但模型本身还存在一些能力的限制,例如大模型的幻觉、无法理解较为复杂的指令、无法执行复杂步骤等。我们可以通过构造思维链,将 Prompt 构造成一系列步骤来尽量减少其能力限制,例如,我们可以构造一个两步的思维链,要求模型在第二步做出反思,以尽可能消除大模型的幻觉问题。

我们首先有这样一个 Bad Case:

问题:我们应该如何去构造一个 LLM 项目
初始回答:略
存在不足:事实上,知识库中中关于如何构造LLM项目的内容是使用 LLM API 去搭建一个应用,模型的回答看似有道理,实则是大模型的幻觉,将部分相关的文本拼接得到,存在问题
question = "我们应该如何去构造一个LLM项目"
result = qa_chain({"query": question})
print(result["result"])
构建一个LLM项目需要考虑以下几个步骤:
​
1. 确定项目目标和需求:首先要明确你的项目是为了解决什么问题或实现什么目标,确定需要使用LLM的具体场景和任务。
​
2. 收集和准备数据:根据项目需求,收集和准备适合的数据集,确保数据的质量和多样性,以提高LLM的性能和效果。
​
3. 设计Prompt和指令微调:根据项目需求设计合适的Prompt,确保指令清晰明确,可以引导LLM生成符合预期的文本。
​
4. 进行模型训练和微调:使用基础LLM或指令微调LLM对数据进行训练和微调,以提高模型在特定任务上的表现和准确性。
​
5. 测试和评估模型:在训练完成后,对模型进行测试和评估,检查其在不同场景下的表现和效果,根据评估结果进行必要的调整和优化。
​
6. 部署和应用模型:将训练好的LLM模型部署到实际应用中,确保其能够正常运行并实现预期的效果,持续监测和优化模型的性能。
​
来源:根据提供的上下文内容进行总结。

对此,我们可以优化 Prompt,将之前的 Prompt 变成两个步骤,要求模型在第二个步骤中做出反思:

template_v4 = """
请你依次执行以下步骤:
① 使用以下上下文来回答最后的问题。如果你不知道答案,就说你不知道,不要试图编造答案。
你应该使答案尽可能详细具体,但不要偏题。如果答案比较长,请酌情进行分段,以提高答案的阅读体验。
如果答案有几点,你应该分点标号回答,让答案清晰具体。
上下文:
{context}
问题: 
{question}
有用的回答:
② 基于提供的上下文,反思回答中有没有不正确或不是基于上下文得到的内容,如果有,回答你不知道
确保你执行了每一个步骤,不要跳过任意一个步骤。
"""
​
QA_CHAIN_PROMPT = PromptTemplate(input_variables=["context","question"],
                                 template=template_v4)
qa_chain = RetrievalQA.from_chain_type(llm,
                                       retriever=vectordb.as_retriever(),
                                       return_source_documents=True,
                                       chain_type_kwargs={"prompt":QA_CHAIN_PROMPT})
​
question = "我们应该如何去构造一个LLM项目"
result = qa_chain({"query": question})
print(result["result"])
根据上下文中提供的信息,构造一个LLM项目需要考虑以下几个步骤:
​
1. 确定项目目标:首先要明确你的项目目标是什么,是要进行文本摘要、情感分析、实体提取还是其他任务。根据项目目标来确定LLM的使用方式和调用API接口的方法。
​
2. 设计Prompt:根据项目目标设计合适的Prompt,Prompt应该清晰明确,指导LLM生成符合预期的结果。Prompt的设计需要考虑到任务的具体要求,比如在文本摘要任务中,Prompt应该包含需要概括的文本内容。
​
3. 调用API接口:根据设计好的Prompt,通过编程调用LLM的API接口来生成结果。确保API接口的调用方式正确,以获取准确的结果。
​
4. 分析结果:获取LLM生成的结果后,进行结果分析,确保结果符合项目目标和预期。如果结果不符合预期,可以调整Prompt或者其他参数再次生成结果。
​
5. 优化和改进:根据分析结果的反馈,不断优化和改进LLM项目,提高项目的效率和准确性。可以尝试不同的Prompt设计、调整API接口的参数等方式来优化项目。
​
通过以上步骤,可以构建一个有效的LLM项目,利用LLM的强大功能来实现文本摘要、情感分析、实体提取等任务,提高工作效率和准确性。如果有任何不清楚的地方或需要进一步的指导,可以随时向相关领域的专家寻求帮助。

可以看出,要求模型做出自我反思之后,模型修复了自己的幻觉,给出了正确的答案。我们还可以通过构造思维链完成更多功能,此处就不再赘述了,欢迎读者尝试。

4.增加一个指令解析

我们往往会面临一个需求,即我们需要模型以我们指定的格式进行输出。但是,由于我们使用了 Prompt Template 来填充用户问题,用户问题中存在的格式要求往往会被忽略,例如:

question = "LLM的分类是什么?给我返回一个 Python List"
result = qa_chain({"query": question})
print(result["result"])
根据上下文提供的信息,LLM(Large Language Model)的分类可以分为两种类型,即基础LLM和指令微调LLM。基础LLM是基于文本训练数据,训练出预测下一个单词能力的模型,通常通过在大量数据上训练来确定最可能的词。指令微调LLM则是对基础LLM进行微调,以更好地适应特定任务或场景,类似于向另一个人提供指令来完成任务。
​
根据上下文,可以返回一个Python List,其中包含LLM的两种分类:["基础LLM", "指令微调LLM"]。

可以看到,虽然我们要求模型给返回一个 Python List,但该输出要求被包裹在 Template 中被模型忽略掉了。针对该问题,我们可以构造一个 Bad Case:

问题:LLM的分类是什么?给我返回一个 Python List
初始回答:根据提供的上下文,LLM的分类可以分为基础LLM和指令微调LLM。
存在不足:没有按照指令中的要求输出

针对该问题,一个存在的解决方案是,在我们的检索 LLM 之前,增加一层 LLM 来实现指令的解析,将用户问题的格式要求和问题内容拆分开来。这样的思路其实就是目前大火的 Agent 机制的雏形,即针对用户指令,设置一个 LLM(即 Agent)来理解指令,判断指令需要执行什么工具,再针对性调用需要执行的工具,其中每一个工具可以是基于不同 Prompt Engineering 的 LLM,也可以是例如数据库、API 等。LangChain 中其实有设计 Agent 机制,但本教程中我们就不再赘述了,这里只基于 OpenAI 的原生接口简单实现这一功能:

response = client.chat.completions.create(
        model=model,
        messages=gen_gpt_messages(prompt),
        temperature=temperature,
    ) #设置temperature为0
    
prompt_input = '''
请判断以下问题中是否包含对输出的格式要求,并按以下要求输出:
请返回给我一个可解析的Python列表,列表第一个元素是对输出的格式要求,应该是一个指令;第二个元素是去掉格式要求的问题原文
如果没有格式要求,请将第一个元素置为空
需要判断的问题:
~~~
{}
~~~
不要输出任何其他内容或格式,确保返回结果可解析。
'''
'```\n["给我返回一个 Python List", "LLM的分类是什么?"]\n```'

可以看到,通过上述 Prompt,LLM 可以很好地实现输出格式的解析,接下来,我们可以再设置一个 LLM 根据输出格式要求,对输出内容进行解析:

prompt_output = '''
请根据回答文本和输出格式要求,按照给定的格式要求对问题做出回答
需要回答的问题:
~~~
{}
~~~
回答文本:
~~~
{}
~~~
输出格式要求:
~~~
{}
~~~
'''

然后我们可以将两个 LLM 与检索链串联起来:

question = 'LLM的分类是什么?给我返回一个 Python List'
# 首先将格式要求与问题拆分
input_lst_s = get_completion(prompt_input.format(question))
# 找到拆分之后列表的起始和结束字符
start_loc = input_lst_s.find('[')
end_loc = input_lst_s.find(']')
rule, new_question = eval(input_lst_s[start_loc:end_loc+1])
# 接着使用拆分后的问题调用检索链
result = qa_chain({"query": new_question})
result_context = result["result"]
# 接着调用输出格式解析
response = get_completion(prompt_output.format(new_question, result_context, rule))
response
"['基础LLM', '指令微调LLM']"

可以看到,经过如上步骤,我们就成功地实现了输出格式的限定。当然,在上面代码中,核心为介绍 Agent 思想,事实上,不管是 Agent 机制还是 Parser 机制(也就是限定输出格式),LangChain 都提供了成熟的工具链供使用,欢迎感兴趣的读者深入探讨,此处就不展开讲解了。

通过上述讲解的思路,结合实际业务情况,我们可以不断发现 Bad Case 并针对性优化 Prompt,从而提升生成部分的性能。

评估优化检索部分

生成的前提是检索,只有当我们应用的检索部分能够根据用户 query 检索到正确的答案文档时,大模型的生成结果才可能是正确的。因此,检索部分的检索精确率和召回率其实更大程度影响了应用的整体性能。但是,检索部分的优化是一个更工程也更深入的命题,我们往往需要使用到很多高级的、源于搜索的进阶技巧并探索更多实用工具,甚至手写一些工具来进行优化。因此,在本章中,我们仅大致讨论检索部分评估与优化的思路,而不深入展开代码实践。

评估检索效果

首先回顾整个RAG系统

img

针对用户输入的一个 query,系统会将其转化为向量并在向量数据库中匹配最相关的文本段,然后根据我们的设定选择 3~5 个文本段落和用户的 query 一起交给大模型,再由大模型根据检索到的文本段落回答用户 query 中提出的问题。在这一整个系统中,我们将向量数据库检索相关文本段落的部分称为检索部分,将大模型根据检索到的文本段落进行答案生成的部分称为生成部分。

因此,检索部分的核心功能是找到存在于知识库中、能够正确回答用户 query 中的提问的文本段落。因此,我们可以定义一个最直观的准确率在评估检索效果:对于 N 个给定 query,我们保证每一个 query 对应的正确答案都存在于知识库中。假设对于每一个 query,系统找到了 K 个文本片段,如果正确答案在 K 个文本片段之一,那么我们认为检索成功;如果正确答案不在 K 个文本片段之一,我们任务检索失败。那么,系统的检索准确率可以被简单地计算为:

𝑎𝑐𝑐𝑢𝑟𝑎𝑐𝑦 = 𝑀 / 𝑁

其中,M 是成功检索的 query 数。

通过上述准确率,我们可以衡量系统的检索能力,对于系统能成功检索到的 query,我们才能进一步优化 Prompt 来提高系统性能。对于系统检索失败的 query,我们就必须改进检索系统来优化检索效果。但是注意,当我们在计算如上定义的准确率时,一定要保证我们的每一个验证 query 的正确答案都确实存在于知识库中;如果正确答案本就不存在,那我们应该将 Bad Case 归因到知识库构建部分,说明知识库构建的广度和处理精度还有待提升。

当然,这只是最简单的一种评估方式,事实上,这种评估方式存在很多不足。例如:

  • 有的 query 可能需要联合多个知识片段才能做出回答,对于这种 query,我们如何评估?

  • 检索到的知识片段彼此之间的顺序其实会对大模型的生成带来影响,我们是否应该将检索片段的排序纳入考虑?

  • 除去检索到正确的知识片段之外,我们的系统还应尽量避免检索到错误的、误导性知识片段,否则大模型的生成结果很可能被错误片段误导。我们是否应当将检索到的错误片段纳入指标计算?

上述问题都不存在标准答案,需要针对项目实际针对的业务、评估的成本来综合考虑。

除去通过上述方法来评估检索效果外,我们还可以将检索部分建模为一个经典的搜索任务。让我们来看看经典的搜索场景。搜索场景的任务是,针对用户给定的检索 query,从给定范围的内容(一般是网页)中找到相关的内容并进行排序,尽量使排序靠前的内容能够满足用户需求。

其实我们的检索部分的任务和搜索场景非常类似,同样是针对用户 query,只不过我们相对更强调召回而非排序,以及我们检索的内容不是网页而是知识片段。因此,我们可以类似地将我们的检索任务建模为一个搜索任务,那么,我们就可以引入搜索算法中经典的评估思路(如准确率、召回率等)和优化思路(例如构建索引、重排等)来更充分地评估优化我们的检索效果。

优化检索思路

上文陈述来评估检索效果的几种一般思路,当我们对系统的检索效果做出合理评估,找到对应的 Bad Case 之后,我们就可以将 Bad Case 拆解到多个维度来针对性优化检索部分。注意,虽然在上文评估部分,我们强调了评估检索效果的验证 query 一定要保证其正确答案存在于知识库之中,但是在此处,我们默认知识库构建也作为检索部分的一部分,因此,我们也需要在这一部分解决由于知识库构建有误带来的 Bad Case。在此,我们分享一些常见的 Bad Case 归因和可行的优化思路。

1.知识片吨被割裂导致答案丢失

该问题一般表现为,对于一个用户 query,我们可以确定其问题一定是存在于知识库之中的,但是我们发现检索到的知识片段将正确答案分割开了,导致不能形成一个完整、合理的答案。该种问题在需要较长回答的 query 上较为常见。

该类问题的一般优化思路是,优化文本切割方式。我们在 搭建知识库 那章中使用到的是最原始的分割方式,即根据特定字符和 chunk 大小进行分割,但该类分割方式往往不能照顾到文本语义,容易造成同一主题的强相关上下文被切分到两个 chunk 总。对于一些格式统一、组织清晰的知识文档,我们可以针对性构建更合适的分割规则;对于格式混乱、无法形成统一的分割规则的文档,我们可以考虑纳入一定的人力进行分割。我们也可以考虑训练一个专用于文本分割的模型,来实现根据语义和主题的 chunk 切分。

2.query提问需要长上下文概括回答

该问题也是存在于知识库构建的一个问题。即部分 query 提出的问题需要检索部分跨越很长的上下文来做出概括性回答,也就是需要跨越多个 chunk 来综合回答问题。但是由于模型上下文限制,我们往往很难给出足够的 chunk 数。

该类问题的一般优化思路是,优化知识库构建方式。针对可能需要此类回答的文档,我们可以增加一个步骤,通过使用 LLM 来对长文档进行概括总结,或者预设提问让 LLM 做出回答,从而将此类问题的可能答案预先填入知识库作为单独的 chunk,来一定程度解决该问题。

3.关键词误导

该问题一般表现为,对于一个用户 query,系统检索到的知识片段有很多与 query 强相关的关键词,但知识片段本身并非针对 query 做出的回答。这种情况一般源于 query 中有多个关键词,其中次要关键词的匹配效果影响了主要关键词。

该类问题的一般优化思路是,对用户 query 进行改写,这也是目前很多大模型应用的常用思路。即对于用户输入 query,我们首先通过 LLM 来将用户 query 改写成一种合理的形式,去除次要关键词以及可能出现的错字、漏字的影响。具体改写成什么形式根据具体业务而定,可以要求 LLM 对 query 进行提炼形成 Json 对象,也可以要求 LLM 对 query 进行扩写等。

4.匹配关系不合理

该问题是较为常见的,即匹配到的强相关文本段并没有包含答案文本。该问题的核心问题在于,我们使用的向量模型和我们一开始的假设不符。在讲解 RAG 的框架时,我们有提到,RAG 起效果是有一个核心假设的,即我们假设我们匹配到的强相关文本段就是问题对应的答案文本段。但是很多向量模型其实构建的是“配对”的语义相似度而非“因果”的语义相似度,例如对于 query-“今天天气怎么样”,会认为“我想知道今天天气”的相关性比“天气不错”更高。

该类问题的一般优化思路是,优化向量模型或是构建倒排索引。我们可以选择效果更好的向量模型,或是收集部分数据,在自己的业务上微调一个更符合自己业务的向量模型。我们也可以考虑构建倒排索引,即针对知识库的每一个知识片段,构建一个能够表征该片段内容但和 query 的相对相关性更准确的索引,在检索时匹配索引和 query 的相关性而不是全文,从而提高匹配关系的准确性。

优化检索部分的思路还有很多,事实上,检索部分的优化往往是 RAG 应用开发的核心工程部分。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/584947.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

若依:Linux Centos 7.9 安装部署RuoYi前后端集成版

目录 1.虚拟机操作系统版本 2.删除旧的jdk 3.下载JDK 17 : 4.下载 mvn 3.9.6: 5.下载mysql:5.7.44版本 6.git下载若依: 7.修改数据库连接: 8.mvn 清理和打包 9.启动若依: 1.虚拟机操作系统版本 2.删除旧的jd…

JAVA 中间件之 Mycat2

Mycat2应用与实战教程 1.Mycat2概述 1.1 什么是MyCat 官网: http://mycatone.top/ Mycat 是基于 java 语言编写的数据库中间件,是一个实现了 MySQL 协议的服务器,前端用户可以把它看作是一个数据库代理,用 MySQL 客户端工具和…

灵魂面甲Soulmask免费加速器 灵魂面甲加速器

加入《灵魂面甲》,你将披上炫目的面甲盔甲,手执利刃神兵,运用你的独门技艺去征服这个世界。每一件面甲不仅是防护的象征,更赋予你不同的能力加成,炫酷非凡。无论你是热爱探索新世界的冒险家,是沉浸于作物栽…

线阵相机和面阵相机简介

线阵相机 线阵相机,顾名思义就是所探测的物体要在一个很长的界面上。线阵相机的传感器只有一行感光像素,所以线阵相机一般具有非常高的扫描频率和分辨率。 线阵相机特点 线阵相机使用的线扫描传感器通常只有一行感光单元(少数彩色线阵使用…

boa交叉编译(移植到arm)

参考:CentOS7 boa服务器的搭建和配置-CSDN博客 以下操作在宿主机/编译平台操作: 1. 先执行[参考]1到3、 4.2、4.3、4.4、4.5 2. 修改MakeFile # 由以下: CC gcc CPP gcc -E # 改为: CC arm-linux-gnueabihf-gcc CPP arm-l…

java线上问题排查之磁盘和网络查看分析(二)

一、磁盘&IO df -lh 查看磁盘使用情况 Filesystem:文件系统 Size:容量 Used:已用 Avail:可用 Use%:已用百分比 Mounted on:挂载点 二、网络 查看TCP连接情况 常见问题 tcp队列溢出 netstat -s |e…

java 远程debug

java -agentlib:jdwptransportdt_socket,servery,suspendn,address50050 -Xmx1536m -XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPath./ -jar ${JAR_NAME} >/dev/null 2>&1 &参数说明 -agentlib:jdwptransportdt_socket,servery,suspendn,address50050: 这个参数…

Django-admin单例模式和懒加载

Django-admin单例模式和懒加载 单例模式 class Foo:def __init__(self):self.name "张三"def __new__(cls, *args, **kwargs):empty_object super().__new__(cls)return empty_objectobj1 Foo() obj2 Foo()当我们实例化对象时,就会在内存开一个空间…

深度干货 | 如何兼顾性能与可靠性?一文解析YashanDB主备高可用技术

2024【崖山论”见“】已强势回归!即日起,将不定期把 Meetup 中YashanDB 技术专家的精彩分享整理成文章,方便大家学习回顾。今天带来第一篇主备高可用技术文章。 背景 数据库高可用(High Availability,HA&#xff09…

使用 Docker 自建一款怀旧游戏之 - 扫雷

1)扫雷 简介 扫雷 是一种经典的单人电脑游戏,最初由微软公司在 1990 年代开发并内置在 Windows 操作系统中。游戏的目标是在一个由方块组成的网格上揭开所有非地雷的方块,而不触发地雷。每个方块上都标有数字,表示周围 8 个方块中…

LeetCode55:跳跃游戏

题目描述 给你一个非负整数数组 nums ,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个下标,如果可以,返回 true ;否则,返回 false 。 解题思想 每次…

深入OceanBase分布式数据库:MySQL 模式下的 SQL 基本操作

码到三十五 : 个人主页 OceanBase与MySQL模式下兼容性序 在当今的大数据时代,数据库技术的选择对于企业的信息化发展至关重要。OceanBase作为一种高性能、高可用的分布式关系数据库,在与MySQL模式的兼容性方面展现出了显著的优势&#xff0c…

java语言开发的商城系统有哪些?

最近,有小伙伴问我有没有靠谱的java商城系统,经过我一顿扒拉,终于给大家整理出来了。 目前java语言开发的商城系统主要有shop、javashop、ejavashop、yuanfeng、mall4j、lilishop等。在没有深入了解这些系统前,我们可以从产品推出…

面包屑-文件夹

1.需求: 类似于 百度网盘、阿里云盘的 云文件夹管理功能 2.问题点 1.页面刷新 导致面包屑子级未持久化 2.浏览器的 前进、后退 ;面包屑未能 跳转到指定 子级 3.数据不同步问题 3.解决方法 1.后端提供 根据 id 查询面包屑 text 的 api【这里并没有…

【已解决】如何打开ZIP格式的压缩文件?

压缩文件格式的种类比较多,ZIP是最常见的压缩格式之一。那收到ZIP压缩文件,要如何打开呢?不清楚的小伙伴一起来看看吧! 方法一:使用系统自带的解压缩功能 很多电脑操作系统都内置了解压缩功能,并且支持大…

Matlab|含sop的33节点配电网优化

目录 1 主要内容 2 部分代码 3 程序结果 4 下载链接 1 主要内容 程序以IEEE33节点为例,分析含sop的配电网优化,包括sop有功约束、无功约束和容量约束,非线性部分通过转换为旋转锥约束进行编程,并且包括33节点配电网潮流及对应…

微信小程序个人中心、我的界面(示例四)

微信小程序个人中心、我的界面,九宫格简单布局(示例四) 微信小程序个人中心、我的界面,超简洁的九宫格界面布局,代码粘贴即用。更多微信小程序界面示例,请进入我的主页哦! 1、js代码 Page({…

《深入解析WIndows操作系统》第9章读书笔记

1、闪存类型:常见的闪存类型有NOR和NAND。NOR闪存在操作上最接近RAM,它的每个字节都可以被独立地寻址,而NAND闪存则被组织成以块为单位,就像磁盘一样。NOR类型的闪存用来设计保存计算机主板上的BIOS,而NAND类型的闪存被…

【哈希】Leetcode 217. 存在重复元素

题目讲解 217. 存在重复元素 算法讲解 使用set集合完成元素的存储,当我们将当前元素插入到集合单中,如果insert的返回值的pair.second等于false说明当前元素已经存在,反之元素在集合中存在 class Solution { public:bool containsDuplica…

【触摸案例-手势解锁案例-九宫格 Objective-C语言】

一、手势解锁案例,九宫格,我们先来分析一下怎么实现: 首先呢,我们先来运行一下, 这一块儿,上面的这九个东西,肯定是要有一个九宫格的一个算法的问题,然后呢,上边的这九个小圆圈儿,这是什么东西,Button,为什么是Button,因为可以点,是吗,就因为这个?实际上,你用…