当前位置: 首页 > news >正文

TASK05【Datawhale 组队学习】系统评估与优化

目录

  • 如何评估LLM应用
    • 验证评估的一般思路
    • 大模型评估方法
      • 量化评估
      • 多维评估
      • 构造客观题
      • 计算答案相似度
      • 使用大模型进行评估
      • 混合评估
  • 评估并优化生成部分
    • 提升直观回答质量
    • 标明知识来源,提高可信度
    • 构造思维链
    • 增加一个指令解析
  • 评估并优化检索部分
    • 优化检索的思路

如何评估LLM应用

在这里插入图片描述

验证评估的一般思路

我们需要一种自动评估方法,实现对该验证集上性能的整体评估。
基于 RAG 范式开发的大模型应用一般包括两个核心部分:检索和生成,所以,我们的评估优化也会分别聚焦到这两个部分,分别以优化系统检索精度和在确定给定材料下的生成质量。

验证迭代是构建以 LLM 为中心的应用程序所必不能少的重要步骤,通过不断寻找 Bad Case,针对性调整 Prompt 或优化检索性能,来推动应用达到我们目标中的性能与精度。

接下来简要介绍大模型开发评估的几种方法,并概括性介绍从少数 Bad Case 针对性优化到整体自动化评估的一般思路。

大模型评估方法

找到 Bad Cases,并不断针对性优化 Prompt 或检索架构来解决 Bad Cases,从而优化系统的表现
将找到的每一个 Bad Case 都加入到我们的验证集中,每一次优化之后,我们会重新对验证集中所有验证案例进行验证,从而保证优化后的 系统不会在原有 Good Case 上失去能力或表现降级。

(为什么用自动评估)当验证集体量较小时,我们可以采用人工评估的方法,即对验证集中的每一个验证案例,我们需要采用自动评估的方法,自动评估系统对每一个验证案例的输出质量,从而评估系统的整体性能。

量化评估

对每一个验证案例的回答都给出打分,最后计算所有验证案例的平均分得到本版本系统的得分。量化的量纲可以是05,也可以是0100,可以根据个人风格和业务实际情况而定。
量化后的评估指标应当有一定的评估规范,例如在满足条件 A 的情况下可以打分为 y 分,以保证不同评估员之间评估的相对一致。

exp:
① 《南瓜书》的作者是谁?
② 应该如何使用南瓜书?

template_v1 = """使用以下上下文来回答最后的问题。如果你不知道答案,就说你不知道,不要试图编造答
案。最多使用三句话。尽量使答案简明扼要。总是在回答的最后说“谢谢你的提问!”。
{context}
问题: {question}
"""

在这里插入图片描述

template_v2 = """使用以下上下文来回答最后的问题。如果你不知道答案,就说你不知道,不要试图编造答
案。你应该使答案尽可能详细具体,但不要偏题。如果答案比较长,请酌情进行分段,以提高答案的阅读体验。
{context}
问题: {question}
有用的回答:"""

在这里插入图片描述
在这里插入图片描述
版本 A 的 prompt 在案例①上有着更好的效果,但版本 B 的 prompt 在案例②上效果更佳。

如果我们不量化评估指标,仅使用相对优劣的评估的话,我们无法判断版本 A 与版本 B 哪一个 prompt 更好,从而要找到一个 prompt 在所有案例上表现都更优才能进一步迭代;然而,这很明显是非常困难且不利于我们迭代优化的。

多维评估

大模型是典型的生成模型,即其回答为一个由模型生成的语句。
一般而言,大模型的回答需要在多个维度上进行评估。
因此,我们往往需要从多个维度出发,设计每个维度的评估指标,在每个维度上都进行打分,从而综合评估系统性能。同时需要注意的是,多维评估应当和量化评估有效结合,对每一个维度,可以设置相同的量纲也可以设置不同的量纲,应充分结合业务实际。

(exp)本项目中,我们可以设计如下几个维度的评估:
① 知识查找正确性。该维度需要查看系统从向量数据库查找相关知识片段的中间结果,评估系统查找到的知识片段是否能够对问题做出回答。该维度为0-1评估,即打分为0指查找到的知识片段不能做出回答,打分为1指查找到的知识片段可以做出回答。

② 回答一致性。该维度评估系统的回答是否针对用户问题展开,是否有偏题、错误理解题意的情况,该维度量纲同样设计为0~1,0为完全偏题,1为完全切题,中间结果可以任取。

③ 回答幻觉比例。该维度需要综合系统回答与查找到的知识片段,评估系统的回答是否出现幻觉,幻觉比例有多高。该维度同样设计为0~1,0为全部是模型幻觉,1为没有任何幻觉。

④ 回答正确性。该维度评估系统回答是否正确,是否充分解答了用户问题,是系统最核心的评估指标之一。该维度可以在0~1之间任意打分。

上述四个维度都围绕知识、回答的正确性展开,与问题高度相关;接下来几个维度将围绕大模型生成结果的拟人性、语法正确性展开,与问题相关性较小:

⑤ 逻辑性。该维度评估系统回答是否逻辑连贯,是否出现前后冲突、逻辑混乱的情况。该维度为0-1评估。

⑥ 通顺性。该维度评估系统回答是否通顺、合乎语法,可以在0~1之间任意打分。

⑦ 智能性。该维度评估系统回答是否拟人化、智能化,是否能充分让用户混淆人工回答与智能回答。该维度可以在0~1之间任意打分。
在这里插入图片描述

print("问题:")
question = "应该如何使用南瓜书?"
print(question)
print("模型回答:")
result = qa_chain.invoke(question)
print(result["answer"])
print(result["context"])
#系统查找到的知识片段

在这里插入图片描述

我们做出相应评估:

① 知识查找正确性——1

② 回答一致性——0.8(解答了问题,但是类似于“反馈”的话题偏题了)

③ 回答幻觉比例——1

④ 回答正确性——0.8(理由同上)

⑤ 逻辑性——0.7(后续内容与前面逻辑连贯性不强)

⑥ 通顺性——0.6(最后总结啰嗦且无效)

⑦ 智能性——0.5(具有 AI 回答的显著风格)

综合上述七个维度,我们可以全面、综合地评估系统在每个案例上的表现,综合考虑所有案例的得分,就可以评估系统在每个维度的表现。如果将所有维度量纲统一,那么我们还可以计算所有维度的平均得分来评估系统的得分。我们也可以针对不同维度的不同重要性赋予权值,再计算所有维度的加权平均来代表系统得分。

评估系统表现时,通常需要从多个维度进行综合考量,如准确性、效率、稳定性等。为了全面评估,每个案例在每个维度上都需要单独评分,进而计算平均得分或加权平均得分。
两个版本的系统在10个案例上评估7个维度,需要140次评估;而成熟系统的验证案例和版本数量更多,评估次数可能达到上万次。
我们需要一种自动评估模型回答的方法。

构造客观题

大模型评估之所以复杂,一个重要原因在于生成模型的答案很难判别,即客观题评估判别很简单,主观题评估判别则很困难。

没有标准答案的问题,实现自动评估就显得难度尤大。

在牺牲一定评估准确性的情况下,我们可以将复杂的没有标准答案的主观题进行转化,从而变成有标准答案的问题,进而通过简单的自动评估来实现。

此处介绍两种方法:构造客观题与计算标准答案相似度。

【问答题】南瓜书的作者是谁?

我们可以将该主观题构造为如下客观题:

【多项选择题】南瓜书的作者是谁? A 周志明 B 谢文睿 C 秦州 D 贾彬彬

要求模型回答该客观题,我们给定标准答案为 BCD,将模型给出答案与标准答案对比即可实现评估打分。根据以上思想,我们可以构造出一个 Prompt 问题模板:

prompt_template = '''
请你做如下选择题:
题目:南瓜书的作者是谁?
选项:A 周志明 B 谢文睿 C 秦州 D 贾彬彬
你可以参考的知识片段:```{}```
请仅返回选择的选项
如果你无法做出选择,请返回空

由于大模型的不稳定性,即使我们要求其只给出选择选项,系统可能也会返回一大堆文字,其中详细解释了为什么选择如下选项。

因此,我们需要将选项从模型回答中抽取出来。同时,我们需要设计一个打分策略。一般情况下,我们可以使用多选题的一般打分策略:全选1分,漏选0.5分,错选不选不得分:

def multi_select_score_v1(true_answer : str, generate_answer : str) -> float:# true_anser : 正确答案,str 类型,例如 'BCD'# generate_answer : 模型生成答案,str 类型true_answers = list(true_answer)'''为便于计算,我们假设每道题都只有 A B C D 四个选项'''# 先找出错误答案集合false_answers = [item for item in ['A', 'B', 'C', 'D'] if item not in true_answers]# 如果生成答案出现了错误答案for one_answer in false_answers:if one_answer in generate_answer:return 0# 再判断是否全选了正确答案if_correct = 0for one_answer in true_answers:if one_answer in generate_answer:if_correct += 1continueif if_correct == 0:# 不选return 0elif if_correct == len(true_answers):# 全选return 1else:# 漏选return 0.5

基于上述打分函数,我们可以测试四个回答:
① B C
② 除了 A 周志华之外,其他都是南瓜书的作者
③ 应该选择 B C D
④ 我不知道

在这里插入图片描述
我们要求模型在不能回答的情况下不做选择,而不是随便选。但是在我们的打分策略中,错选和不选均为0分,这样其实鼓励了模型的幻觉回答,因此我们可以根据情况调整打分策略,让错选扣一分:

在这里插入图片描述
在这里插入图片描述

计算答案相似度

NLP 一般对生成问题采用人工构造标准答案并计算回答与标准答案相似度的方法来实现自动评估。
例如,对问题:
南瓜书的目标是什么?
我们可以首先人工构造一个标准回答:

周志华老师的《机器学习》(西瓜书)是机器学习领域的经典入门教材之一,周老师为了使尽可能多的读者通过西瓜书对机器学习有所了解, 所以在书中对部分公式的推导细节没有详述,但是这对那些想深究公式推导细节的读者来说可能“不太友好”,本书旨在对西瓜书里比较难理解的公式加以解析,以及对部分公式补充具体的推导细节。
接着对模型回答计算其与该标准回答的相似程度,越相似则我们认为答案正确程度越高。

计算相似度的方法有很多,我们一般可以使用 BLEU 来计算相似度
BLEU详解

from nltk.translate.bleu_score import sentence_bleu
import jiebadef bleu_score(true_answer : str, generate_answer : str) -> float:# true_anser : 标准答案,str 类型# generate_answer : 模型生成答案,str 类型true_answers = list(jieba.cut(true_answer))# print(true_answers)generate_answers = list(jieba.cut(generate_answer))# print(generate_answers)bleu_score = sentence_bleu(true_answers, generate_answers)return bleu_score
true_answer = '周志华老师的《机器学习》(西瓜书)是机器学习领域的经典入门教材之一,周老师为了使尽可能多的读者通过西瓜书对机器学习有所了解, 所以在书中对部分公式的推导细节没有详述,但是这对那些想深究公式推导细节的读者来说可能“不太友好”,本书旨在对西瓜书里比较难理解的公式加以解析,以及对部分公式补充具体的推导细节。'print("答案一:")
answer1 = '周志华老师的《机器学习》(西瓜书)是机器学习领域的经典入门教材之一,周老师为了使尽可能多的读者通过西瓜书对机器学习有所了解, 所以在书中对部分公式的推导细节没有详述,但是这对那些想深究公式推导细节的读者来说可能“不太友好”,本书旨在对西瓜书里比较难理解的公式加以解析,以及对部分公式补充具体的推导细节。'
print(answer1)
score = bleu_score(true_answer, answer1)
print("得分:", score)
print("答案二:")
answer2 = '本南瓜书只能算是我等数学渣渣在自学的时候记下来的笔记,希望能够帮助大家都成为一名合格的“理工科数学基础扎实点的大二下学生”'
print(answer2)
score = bleu_score(true_answer, answer2)
print("得分:", score)

在这里插入图片描述
答案与标准答案一致性越高,则评估打分就越高。通过此种方法,我们同样只需对验证集中每一个问题构造一个标准答案,之后就可以实现自动、高效的评估。

方法存在问题
① 需要人工构造标准答案。对于一些垂直领域而言,构造标准答案可能是一件困难的事情;
② 通过相似度来评估,可能存在问题。例如,如果生成回答与标准答案高度一致但在核心的几个地方恰恰相反导致答案完全错误,bleu 得分仍然会很高;
③ 通过计算与标准答案一致性灵活性很差,如果模型生成了比标准答案更好的回答,但评估得分反而会降低;
④ 无法评估回答的智能性、流畅性。如果回答是各个标准答案中的关键词拼接出来的,我们认为这样的回答是不可用无法理解的,但 bleu 得分会较高。

使用大模型进行评估

以 GPT-4 为代表的大模型为我们提供了一种新的方法:使用大模型进行评估。我们可以通过构造 Prompt Engineering 让大模型充当一个评估者的角色,从而替代人工评估的评估员;同时大模型可以给出类似于人工评估的结果,因此可以采取人工评估中的多维度量化评估的方式,实现快速全面的评估。

prompt = '''
你是一个模型回答评估员。
接下来,我将给你一个问题、对应的知识片段以及模型根据知识片段对问题的回答。
请你依次评估以下维度模型回答的表现,分别给出打分:① 知识查找正确性。评估系统给定的知识片段是否能够对问题做出回答。如果知识片段不能做出回答,打分为0;如果知识片段可以做出回答,打分为1。② 回答一致性。评估系统的回答是否针对用户问题展开,是否有偏题、错误理解题意的情况,打分分值在0~1之间,0为完全偏题,1为完全切题。③ 回答幻觉比例。该维度需要综合系统回答与查找到的知识片段,评估系统的回答是否出现幻觉,打分分值在0~1之间,0为全部是模型幻觉,1为没有任何幻觉。④ 回答正确性。该维度评估系统回答是否正确,是否充分解答了用户问题,打分分值在0~1之间,0为完全不正确,1为完全正确。⑤ 逻辑性。该维度评估系统回答是否逻辑连贯,是否出现前后冲突、逻辑混乱的情况。打分分值在0~1之间,0为逻辑完全混乱,1为完全没有逻辑问题。⑥ 通顺性。该维度评估系统回答是否通顺、合乎语法。打分分值在0~1之间,0为语句完全不通顺,1为语句完全通顺没有任何语法问题。⑦ 智能性。该维度评估系统回答是否拟人化、智能化,是否能充分让用户混淆人工回答与智能回答。打分分值在0~1之间,0为非常明显的模型回答,1为与人工回答高度一致。你应该是比较严苛的评估员,很少给出满分的高评估。
用户问题:```{}```
待评估的回答:```{}```
给定的知识片段:```{}```
你应该返回给我一个可直接解析的 Python 字典,字典的键是如上维度,值是每一个维度对应的评估打分。
不要输出任何其他内容。
'''

测试

question = "应该如何使用南瓜书?"
result = qa_chain.invoke(question)
answer = result["answer"]
knowledge = result["context"]response = get_completion(prompt.format(question, answer, knowledge))
response

在这里插入图片描述

存在的问题

① 我们的目标是迭代改进 Prompt 以提升大模型表现,因此我们所选用的评估大模型需要有优于我们所使用的大模型基座的性能,例如,目前性能最强大的大模型仍然是 GPT-4,推荐使用 GPT-4 来进行评估,效果最好。

② 大模型具有强大的能力,但同样存在能力的边界。如果问题与回答太复杂、知识片段太长或是要求评估维度太多,即使是 GPT-4 也会出现错误评估、错误格式、无法理解指令等情况,针对这些情况,我们建议考虑如下方案来提升大模型表现:

  1. 改进 Prompt Engineering。以类似于系统本身 Prompt Engineering 改进的方式,迭代优化评估 Prompt Engineering,尤其是注意是否遵守了 Prompt Engineering 的基本准则、核心建议等;

  2. 拆分评估维度。如果评估维度太多,模型可能会出现错误格式导致返回无法解析,可以考虑将待评估的多个维度拆分,每个维度调用一次大模型进行评估,最后得到统一结果;

  3. 合并评估维度。如果评估维度太细,模型可能无法正确理解以至于评估不正确,可以考虑将待评估的多个维度合并,例如,将逻辑性、通顺性、智能性合并为智能性等;

  4. 提供详细的评估规范。如果没有评估规范,模型很难给出理想的评估结果。可以考虑给出详细、具体的评估规范,从而提升模型的评估能力;

  5. 提供少量示例。模型可能难以理解评估规范,此时可以给出少量评估的示例,供模型参考以实现正确评估。

混合评估

上述评估方法都不是孤立、对立的,相较于独立地使用某一种评估方法,我们更推荐将多种评估方法混合起来,对于每一种维度选取其适合的评估方法,兼顾评估的全面、准确和高效。
针对本项目个人知识库助手,我们可以设计以下混合评估方法:

  1. 客观正确性。客观正确性指对于一些有固定正确答案的问题,模型可以给出正确的回答。我们可以选取部分案例,使用构造客观题的方式来进行模型评估,评估其客观正确性。

  2. 主观正确性。主观正确性指对于没有固定正确答案的主观问题,模型可以给出正确的、全面的回答。我们可以选取部分案例,使用大模型评估的方式来评估模型回答是否正确。

  3. 智能性。智能性指模型的回答是否足够拟人化。由于智能性与问题本身弱相关,与模型、Prompt 强相关,且模型判断智能性能力较弱,我们可以少量抽样进行人工评估其智能性。

  4. 知识查找正确性。知识查找正确性指对于特定问题,从知识库检索到的知识片段是否正确、是否足够回答问题。知识查找正确性推荐使用大模型进行评估,即要求模型判别给定的知识片段是否足够回答问题。同时,该维度评估结果结合主观正确性可以计算幻觉情况,即如果主观回答正确但知识查找不正确,则说明产生了模型幻觉。

评估并优化生成部分

我们需要结合评估结果,对评估出的 Bad Case 进行拆分,并分别对每一部分做出评估和优化。

RAG 全称为检索增强生成,因此,其有两个核心部分:检索部分和生成部分。

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

分别评估检索部分和优化部分的性能,找出 Bad Case 并针对性进行性能的优化。而具体到生成部分,在已限定使用的大模型基座的情况下,我们往往会通过优化 Prompt Engineering 来优化生成的回答。
在这里插入图片描述

from zhipuai import ZhipuAI
import os
from dotenv import load_dotenv, find_dotenv# 读取本地/项目的环境变量。# find_dotenv() 寻找并定位 .env 文件的路径
# load_dotenv() 读取该 .env 文件,并将其中的环境变量加载到当前的运行环境中  
# 如果你设置的是全局的环境变量,这行代码则没有任何作用。def gen_glm_params(prompt):'''构造 GLM 模型请求参数 messages请求参数:prompt: 对应的用户提示词'''messages = [{"role": "user", "content": prompt}]return messagesdef get_completion(prompt, model="glm-4-plus", temperature=0.95):'''获取 GLM 模型调用结果请求参数:prompt: 对应的提示词model: 调用的模型,默认为 glm-4,也可以按需选择 glm-3-turbo 等其他模型temperature: 模型输出的温度系数,控制输出的随机程度,取值范围是 0.0-1.0。温度系数越低,输出内容越一致。'''messages = gen_glm_params(prompt)response = client.chat.completions.create(model=model,messages=messages,temperature=temperature)if len(response.choices) > 0:return response.choices[0].message.contentreturn "generate answer error"from zhipuai_embedding import ZhipuAIEmbeddings
import os
os.environ["ZHIPUAI_API_KEY"]=""
api_key=os.environ["ZHIPUAI_API_KEY"]
# 定义 Embeddings
# embedding = OpenAIEmbeddings() 
embedding = ZhipuAIEmbeddings()
# embedding = QianfanEmbeddingsEndpoint()# 定义持久化路径
persist_directory = './chroma'from langchain_community.vectorstores import Chroma# 加载数据库
vectordb = Chroma(persist_directory=persist_directory,  # 允许我们将persist_directory目录保存到磁盘上embedding_function=embedding
)from langchain.prompts import PromptTemplate
from langchain_core.runnables import RunnableParallel, RunnableLambda, RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
template_v1 = """使用以下上下文来回答最后的问题。如果你不知道答案,就说你不知道,不要试图编造答
案。最多使用三句话。尽量使答案简明扼要。总是在回答的最后说“谢谢你的提问!”。
{context}
问题: {question}
"""QA_CHAIN_PROMPT = PromptTemplate.from_template(template_v1)def combine_docs(docs):return "\n\n".join(doc.page_content for doc in docs)retrievel_chain = vectordb.as_retriever() | RunnableLambda(combine_docs)qa_chain = (RunnableParallel(context=retrievel_chain, question=RunnablePassthrough())| QA_CHAIN_PROMPT| llm| StrOutputParser()
)question = "什么是南瓜书"
result = qa_chain.invoke(question)
print(result)

提升直观回答质量

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

问题:什么是南瓜书
初始回答:南瓜书是对《机器学习》(西瓜书)中难以理解的公式进行解析和补充推导细节的一本书。谢谢你的提问!
存在不足:回答太简略,需要回答更具体;谢谢你的提问感觉比较死板,可以去掉
template_v2 = """使用以下上下文来回答最后的问题。如果你不知道答案,就说你不知道,不要试图编造答
案。你应该使答案尽可能详细具体,但不要偏题。如果答案比较长,请酌情进行分段,以提高答案的阅读体验。
{context}
问题: {question}
有用的回答:"""
QA_CHAIN_PROMPT = PromptTemplate.from_template(template_v2)

在这里插入图片描述

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

question = "使用大模型时,构造 Prompt 的原则有哪些"
result = qa_chain.invoke(question)
print(result)

在这里插入图片描述

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

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

在这里插入图片描述

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

question = "强化学习的定义是什么"
result = qa_chain.invoke(question)
print(result)

在这里插入图片描述

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

template_v4 = """使用以下上下文来回答最后的问题。如果你不知道答案,就说你不知道,不要试图编造答
案。你应该使答案尽可能详细具体,但不要偏题。如果答案比较长,请酌情进行分段,以提高答案的阅读体验。
如果答案有几点,你应该分点标号回答,让答案清晰具体。
请你附上回答的来源原文,以保证回答的正确性。
{context}
问题: {question}
有用的回答:"""QA_CHAIN_PROMPT = PromptTemplate(input_variables=["context","question"],template=template_v4)
qa_chain = (RunnableParallel(context=retrievel_chain, question=RunnablePassthrough())| QA_CHAIN_PROMPT| llm| StrOutputParser()
)question = "强化学习的定义是什么"
result = qa_chain.invoke(question)
print(result)

在这里插入图片描述
油盐不进。。。

构造思维链

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

问题:我们应该如何去构造一个 LLM 项目
初始回答:略
存在不足:事实上,知识库中中关于如何构造LLM项目的内容是使用 LLM API 去搭建一个应用,模型的回答看似有道理,实则是大模型的幻觉,将部分相关的文本拼接得到,存在问题

在这里插入图片描述

template_v4 = """
请你依次执行以下步骤:
① 使用以下上下文来回答最后的问题。如果你不知道答案,就说你不知道,不要试图编造答案。
你应该使答案尽可能详细具体,但不要偏题。如果答案比较长,请酌情进行分段,以提高答案的阅读体验。
如果答案有几点,你应该分点标号回答,让答案清晰具体。
上下文:
{context}
问题: 
{question}
有用的回答:
② 基于提供的上下文,反思回答中有没有不正确或不是基于上下文得到的内容,如果有,回答你不知道
确保你执行了每一个步骤,不要跳过任意一个步骤。
"""

在这里插入图片描述

增加一个指令解析

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

question = "LLM的分类是什么?给我返回一个 Python List"
result = qa_chain.invoke(question)
print(result)

在这里插入图片描述
针对该问题,一个存在的解决方案是,在我们的检索 LLM 之前,增加一层 LLM 来实现指令的解析,将用户问题的格式要求和问题内容拆分开来。这样的思路其实就是目前大火的 Agent 机制的雏形,即针对用户指令,设置一个 LLM(即 Agent)来理解指令,判断指令需要执行什么工具,再针对性调用需要执行的工具,其中每一个工具可以是基于不同 Prompt Engineering 的 LLM,也可以是例如数据库、API 等。

prompt_input = '''
请判断以下问题中是否包含对输出的格式要求,并按以下要求输出:
请返回给我一个可解析的Python列表,列表第一个元素是对输出的格式要求,应该是一个指令;第二个元素是去掉格式要求的问题原文
如果没有格式要求,请将第一个元素置为空
需要判断的问题:```{}```
不要输出任何其他内容或格式,确保返回结果可解析。
'''response = get_completion(prompt_input.format(question))
response

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

prompt_output = '''
请根据回答文本和输出格式要求,按照给定的格式要求对问题做出回答
需要回答的问题:
```{}```回答文本:
```{}```输出格式要求:
```{}```'''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.invoke(new_question)
result_context = result
# 接着调用输出格式解析
response = get_completion(prompt_output.format(new_question, result_context, rule))
response

在这里插入图片描述

不管是 Agent 机制还是 Parser 机制(也就是限定输出格式),LangChain 都提供了成熟的工具链供使用

通过上述讲解的思路,结合实际业务情况,我们可以不断发现 Bad Case 并针对性优化 Prompt,从而提升生成部分的性能。但是,上述优化的前提是检索部分能够检索到正确的答案片段,也就是检索的准确率和召回率尽可能高。

评估并优化检索部分

生成的前提是检索,只有当我们应用的检索部分能够根据用户 query 检索到正确的答案文档时,大模型的生成结果才可能是正确的。
检索部分的检索精确率和召回率其实更大程度影响了应用的整体性能。

检索部分的优化是一个更工程也更深入的命题,我们往往需要使用到很多高级的、源于搜索的进阶技巧并探索更多实用工具,甚至手写一些工具来进行优化。
在这里插入图片描述
针对用户输入的一个 query,系统会将其转化为向量并在向量数据库中匹配最相关的文本段,然后根据我们的设定选择 3~5 个文本段落和用户的 query 一起交给大模型,再由大模型根据检索到的文本段落回答用户 query 中提出的问题。

检索部分: 向量数据库检索相关文本段落的部分
生成部分:大模型根据检索到的文本段落进行答案生成的部分
检索部分的核心功能是找到存在于知识库中、能够正确回答用户 query 中的提问的文本段落。

对于 N 个给定 query,我们保证每一个 query 对应的正确答案都存在于知识库中。假设对于每一个 query,系统找到了 K 个文本片段,如果正确答案在 K 个文本片段之一,那么我们认为检索成功;如果正确答案不在 K 个文本片段之一,我们任务检索失败。那么,系统的检索准确率可以被简单地计算为:

a c c u r a c y = M N accuracy = \frac{M}{N} accuracy=NM

其中,M 是成功检索的 query 数。
通过上述准确率,我们可以衡量系统的检索能力
对于系统能成功检索到的 query,我们才能进一步优化 Prompt 来提高系统性能。
对于系统检索失败的 query,我们就必须改进检索系统来优化检索效果。

评估方式的不足:

  • 有的 query 可能需要联合多个知识片段才能做出回答,对于这种 query,我们如何评估?
  • 检索到的知识片段彼此之间的顺序其实会对大模型的生成带来影响,我们是否应该将检索片段的排序纳入考虑?
  • 除去检索到正确的知识片段之外,我们的系统还应尽量避免检索到错误的、误导性知识片段,否则大模型的生成结果很可能被错误片段误导。我们是否应当将检索到的错误片段纳入指标计算?

可以将检索部分建模为一个经典的搜索任务。
搜索场景的任务是,针对用户给定的检索 query,从给定范围的内容(一般是网页)中找到相关的内容并进行排序,尽量使排序靠前的内容能够满足用户需求。

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

优化检索的思路

当我们对系统的检索效果做出合理评估,找到对应的 Bad Case 之后,我们就可以将 Bad Case 拆解到多个维度来针对性优化检索部分。

虽然在上文评估部分,我们强调了评估检索效果的验证 query 一定要保证其正确答案存在于知识库之中,但是在此处,我们默认知识库构建也作为检索部分的一部分,因此,我们也需要在这一部分解决由于知识库构建有误带来的 Bad Case。

知识片段被割裂导致答案丢失

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

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

query 提问需要长上下文概括回答
部分 query 提出的问题需要检索部分跨越很长的上下文来做出概括性回答,也就是需要跨越多个 chunk 来综合回答问题。但是由于模型上下文限制,我们往往很难给出足够的 chunk 数。
方法:优化知识库构建方式。针对可能需要此类回答的文档,我们可以增加一个步骤,通过使用 LLM 来对长文档进行概括总结,或者预设提问让 LLM 做出回答,从而将此类问题的可能答案预先填入知识库作为单独的 chunk,来一定程度解决该问题。

关键词误导
对于一个用户 query,系统检索到的知识片段有很多与 query 强相关的关键词,但知识片段本身并非针对 query 做出的回答。这种情况一般源于 query 中有多个关键词,其中次要关键词的匹配效果影响了主要关键词
方法:对用户 query 进行改写,这也是目前很多大模型应用的常用思路。即对于用户输入 query,我们首先通过 LLM 来将用户 query 改写成一种合理的形式,去除次要关键词以及可能出现的错字、漏字的影响。具体改写成什么形式根据具体业务而定,可以要求 LLM 对 query 进行提炼形成 Json 对象,也可以要求 LLM 对 query 进行扩写等。

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

方法:优化向量模型或是构建倒排索引。

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

相关文章:

  • Android四大组件学习总结
  • 【Python】Python 装饰器的用法总结
  • 氢气传感器维护常见问题及解决方法
  • Q网络(Q-Network)简介
  • 49页 @《人工智能生命体 新启点》中國龍 原创连载
  • 今日学习:AOP数据脱敏|线程池|方法引用的实例|背包(0-1)及子集
  • linux_cmake的笔记
  • 2025年——ComfyUI_连接HuggingFace及更改缓存路径
  • rosbridge_suit、roslibpy 源码阅读与简单测试 —— 图片编解码与传输
  • linux初识--基础指令
  • 并发编程艺术--底层原理
  • 中科驭数携DPU全栈产品亮相福州数博会,赋能智算时代算力基建
  • 关于 Web 风险点原理与利用:6. 逻辑风险点
  • SpringBoot3整合WebSocket
  • AI大模型和SpringAI简介
  • 各类Agent技术的发展现状和核心痛点
  • 银基固态电池硬件解析
  • 暗黑科技感风格智慧工地监管系统
  • C++ 结构体封装模式与 Promise 链式调用:设计思想的异曲同工
  • Python实例题:使用Python实现深度神经网络
  • 深圳网站建设 东莞网站建设/软文营销代理
  • 邯山区建设局网站/360浏览器网页版入口
  • 58网站怎么样做效果会更好/哪里可以学seo课程
  • 做爰午夜福利全过程视频网站/企业营销
  • 网站结构怎么做适合优化/云南网站建设百度
  • 免费域名空间申请/seo排名怎么看