Press "Enter" to skip to content

使用LangChain表达语言和LLM的验证实现链 (shǐyòng LangChain biǎodá yǔyán hé LLM de yànzhèng shíxiàn liàn)

介绍

在人工智能(AI)领域中,对精准度和可靠性的不断追求带来了突破性的创新。这些策略对于引领生成模型提供相关答案至关重要。生成AI在不同复杂应用中的使用的最大障碍之一就是幻觉。Meta AI研究发布的最新论文《链式验证减少大型语言模型中的幻觉》介绍了一种简单的技术,可以直接减少文本生成时的幻觉。

本文将探讨幻觉问题,并介绍论文中提到的CoVe概念,以及如何使用LLMs、LangChain框架和LangChain表达语言(LCEL)来实现它以创建自定义链。

学习目标

  • 了解LLMs中的幻觉问题。
  • 了解缓解幻觉的链式验证(CoVe)机制。
  • 了解CoVe的优点和缺点。
  • 学习使用LangChain来实现CoVe,并理解LangChain表达语言。

本文是作为数据科学博文马拉松的一部分发表的。

LLMs中的幻觉问题是什么?

让我们首先尝试了解LLM中的幻觉问题。使用自回归生成方法,LLM模型根据之前的上下文来预测下一个单词。对于频繁主题,模型已经看过足够多的示例,可以自信地为正确的标记分配高概率。然而,由于模型没有接受过关于异常或陌生主题的训练,它可能会以高置信度提供不准确的标记。这导致了看似合理但错误的幻觉信息。

下面是Open AI的ChatGPT中出现幻觉的一个例子,我询问了一本由印度作者于2020年出版的书籍《Small Things的经济学》,但模型却带着充分的自信回答错误,并将其与另一位诺贝尔奖获得者阿比吉特·巴纳吉的书《Poor Economics》混淆。

链式验证(CoVe)技术

CoVe机制结合了提示和一致性检查,为LLMs创建了一个自我验证系统。下面是论文中列出的主要步骤。我们将逐一详细了解每个步骤。

链式过程概述

  1. 生成基线响应:给定查询,使用LLM生成响应。
  2. 计划验证:给定查询和基线响应,生成一系列验证问题的列表,这些问题可以帮助自我分析是否存在原始响应中的任何错误。
  3. 执行验证:依次回答每个验证问题,因此检查答案与原始响应之间的一致性或错误。
  4. 生成最终经过验证的响应:根据发现的不一致性(如果有的话),生成一个经过修订的响应,并纳入验证结果。

使用详细示例理解链式过程

生成初始响应

我们首先将查询传递给LLM,不进行任何特殊提示,生成一个初始响应。这作为CoVe过程的起点。由于像这样的基准反应经常容易产生幻觉,CoVe技术试图在后续阶段发现并纠正这些错误。

示例 – “墨西哥-美国战争的主要原因是什么?”

机器人回答 – 墨西哥-美国战争是1846年至1848年美国和墨西哥之间的一场武装冲突。它紧随1845年美国吞并德克萨斯的行动,在这次行动中,德克萨斯土地被认为是美国的一部分,尽管其在1835年的德克萨斯革命中事实上脱离。

计划验证

根据查询和基准答案创建验证问题,以便我们可以检查基准回答的事实主张。为了实施这一点,我们可以根据查询和基准回答模板一系列验证问题。验证问题可以灵活设置,不一定与原始文本完全匹配。

示例 – 墨西哥-美国战争何时开始和结束?美国什么时候吞并了德克萨斯州?德克萨斯州何时脱离墨西哥?

执行验证

一旦我们规划了验证问题,我们就可以逐个回答这些问题。本文讨论了执行验证的4种不同方法:

1. 合并模式- 在这种模式下,验证问题的规划与执行在同一个提示中完成。问题及其答案在同一个LLM提示中提供。这种方法通常不推荐,因为验证响应可能会产生幻觉。

2. 2步骤- 规划和执行分为两个步骤,在两个单独的LLM提示中完成。首先,我们生成验证问题,然后回答这些问题。

3. 分解- 在这种方法中,每个验证问题都独立回答,而不是在同一个大的响应中,且不包含基准原始响应。它可以避免不同验证问题之间的混淆,也可以处理更多的问题。

4. 分解+修订- 在这种方法中添加了一个额外的步骤。在回答每个验证问题后,CoVe机制将检查答案是否与原始基准响应匹配。这是在使用额外提示的单独步骤中进行的。

外部工具或自用LLM: 我们需要一个工具来验证我们的响应并给出验证答案。这可以使用LLM本身或外部工具来执行。如果我们想要更高的准确性,我们可以使用外部工具,如互联网搜索引擎、任何参考文档或根据我们的用例使用任何网站。

最终验证响应

在这最后一步,生成改进和验证后的响应。使用少量提示,并包含基准响应和验证问题答案的所有上下文。如果使用了“分解+修订”方法,则还提供了交叉检查不一致性的输出。

CoVe技术的限制

尽管验证链似乎是一种简单但有效的技术,但它仍然有一些限制:

  1. 无法完全消除幻觉: 无法保证从响应中完全消除幻觉,因此可能会产生误导信息。
  2. 计算密集: 生成和执行验证问题以及响应生成可能增加计算开销和成本。因此,它可能会减慢过程或增加计算成本。
  3. 模型特定限制: 这种CoVe方法的成功在很大程度上取决于模型的能力以及其识别和纠正错误的能力。

CoVe的LangChain实现

算法的基本概述

在这里,我们将为CoVe的4个步骤中的每个步骤使用4个不同的提示模板,每个步骤的输出作为下一个步骤的输入。同时,我们采用了分解执行验证问题的方法。我们使用外部互联网搜索工具代理来为我们的验证问题生成答案。

步骤1:安装和加载库

!pip install langchain duckduckgo-search

步骤2:创建和初始化LLM实例

在这里,我使用LangChain中的Google Palm LLM,因为它是免费提供的。可以使用此 链接 生成用于Google Palm的API密钥,并使用您的Google账户登录。

from langchain import PromptTemplatefrom langchain.llms import GooglePalmfrom langchain.schema.output_parser import StrOutputParserfrom langchain.schema.runnable import RunnablePassthrough, RunnableLambdaAPI_KEY='生成的API密钥'llm=GooglePalm(google_api_key=API_KEY)llm.temperature=0.4llm.model_name = 'models/text-bison-001'llm.max_output_tokens=2048

第三步:生成初始基准回答

我们将创建一个提示模板,以生成初始基准回答,并使用此模板创建基准回答LLM链。

LLM链将使用LangChain表达语言来组合链条。这里我们将提示模板和LLM模型以及最后的输出解析器链在一起。

BASELINE_PROMPT = """回答下面的问题,要求提供简明的事实回答,不需要额外的细节。问题:{query}回答:"""# 生成初始回答的链baseline_response_prompt_template = PromptTemplate.from_template(BASELINE_PROMPT)baseline_response_chain = baseline_response_prompt_template | llm | StrOutputParser()

第四步:生成用于验证问题的问题模板

现在,我们将构建一个验证问题模板,该模板将帮助在下一步中生成验证问题。

VERIFICATION_QUESTION_TEMPLATE = """您的任务是基于下面提供的问题,创建一个验证问题。示例问题:谁写了书《小物之神》?示例验证问题:书《小物之神》是由[writer]写的吗?如果不是,那么是谁写的《小物之神》?解释:在上面的示例中,验证问题仅仅关注了ANSWER_ENTITY(作者的姓名)和QUESTION_ENTITY(书名)。同样,您需要关注实际问题中的ANSWER_ENTITY和QUESTION_ENTITY,并使用基准回答中的实体值来生成验证问题。实际问题:{query}最终验证问题:"""# 生成用于验证答案的问题模板的链verification_question_template_prompt_template = PromptTemplate.from_template(VERIFICATION_QUESTION_TEMPLATE)verification_question_template_chain = verification_question_template_prompt_template | llm | StrOutputParser()

第五步:生成验证问题

现在,我们将使用上面定义的验证问题模板来生成验证问题:

VERIFICATION_QUESTION_PROMPT= """您的任务是基于下面的问题、验证问题模板和基准回答创建一系列的验证问题。示例问题:谁写了书《小物之神》?示例验证问题模板:书《小物之神》是由[writer]写的吗?如果不是,那么是谁写的《小物之神》?示例基准回答:Jhumpa Lahiri示例验证问题:1. 《小物之神》是由Jhumpa Lahiri写的吗?如果不是,那么是谁写的《小物之神》?解释:在上面的示例中,验证问题仅仅关注了ANSWER_ENTITY(作者的姓名)和QUESTION_ENTITY(书名),并根据模板和基准回答中的实体值进行替换。同样,您需要关注实际问题中的ANSWER_ENTITY和QUESTION_ENTITY,并使用基准回答中的实体值来生成验证问题。实际问题:{query}基准回答:{base_response}验证问题模板:{verification_question_template}最终验证问题:"""# 生成验证问题的链verification_question_generation_prompt_template = PromptTemplate.from_template(VERIFICATION_QUESTION_PROMPT)verification_question_generation_chain = verification_question_generation_prompt_template | llm | StrOutputParser()

第六步:执行验证问题

这里我们将使用外部搜索工具代理来执行验证问题。该代理使用了LangChain的Agent和Tools模块以及DuckDuckGo搜索模块。

注意-搜索代理存在时间限制,请谨慎使用,因为多次请求可能导致错误,因为请求之间存在时间限制

from langchain.agents import ConversationalChatAgent, AgentExecutorfrom langchain.tools import DuckDuckGoSearchResults# 创建搜索代理search = DuckDuckGoSearchResults()tools = [search]custom_system_message = "Assistant assumes no knowledge & relies on internet search to answer user's queries."max_agent_iterations = 5max_execution_time = 10chat_agent = ConversationalChatAgent.from_llm_and_tools(    llm=llm, tools=tools, system_message=custom_system_message)search_executor = AgentExecutor.from_agent_and_tools(    agent=chat_agent,    tools=tools,    return_intermediate_steps=True,    handle_parsing_errors=True,    max_iterations=max_agent_iterations,    max_execution_time=max_execution_time)# 执行验证问题的链verification_chain = RunnablePassthrough.assign(    split_questions=lambda x: x['verification_questions'].split("\n"), # 每个验证问题都逐一传递 factored approach) | RunnablePassthrough.assign(    answers = (lambda x: [{"input": q,"chat_history": []} for q in x['split_questions']])| search_executor.map() # 每个问题都独立执行搜索) | (lambda x: "\n".join(["问题:{} 答案:{}\n".format(question, answer['output']) for question, answer in zip(x['split_questions'], x['answers'])]))# 创建最终的精炼回答

步骤7:生成最终的精细化回答

现在我们将生成最终的精细化回答,其中我们定义提示模板和llm链。

FINAL_ANSWER_PROMPT= """给定以下 `原始查询` 和 `基线答案`,分析 `验证问题和答案`,最终提供精细化回答。原始查询:{query}基线答案:{base_response}验证问题和答案对:{verification_answers}最终的精细化回答:"""# 生成最终回答的链final_answer_prompt_template = PromptTemplate.from_template(FINAL_ANSWER_PROMPT)final_answer_chain = final_answer_prompt_template | llm | StrOutputParser()

步骤8:将所有链连接在一起

现在我们将之前定义的所有链连接在一起,以便它们可以连续运行。

chain = RunnablePassthrough.assign(    base_response=baseline_response_chain) |  RunnablePassthrough.assign(    verification_question_template=verification_question_template_chain) | RunnablePassthrough.assign(    verification_questions=verification_question_generation_chain) | RunnablePassthrough.assign(    verification_answers=verification_chain) | RunnablePassthrough.assign(    final_answer=final_answer_chain)response = chain.invoke({"query": "谁写了书《小事经济学》?"})print(response)

#response的输出{'query': "谁写了书《小事经济学》?", 'base_response': 'Sanjay Jain', 'verification_question_template': '书籍 [小事经济学] 是由 [作者] 写的吗?如果不是,那么是谁写的 [小事经济学] ?', 'verification_questions': '1. 《小事经济学》是由 Sanjay Jain 写的吗?如果不是,那么是由谁写的 [小事经济学] ?', 'verification_answers': '问题:1. 《小事经济学》是由 Sanjay Jain 写的吗?如果不是,那么是由谁写的 [小事经济学] ?答案:《小事经济学》是由 Sudipta Sarangi 写的 \n', 'final_answer': 'Sudipta Sarangi'}

输出图片:

结论

本研究提出的验证链(CoVe)技术是一种旨在构建大型语言模型、更加批判性地思考其回答并在必要时进行修正的策略。这是因为该方法将验证过程划分为更小、更可管理的查询。实验证明,禁止模型审查其先前的回答有助于避免重复错误或“幻觉”。仅要求模型仔细检查其答案就能显著提高结果。给予CoVe更多能力,比如允许它从外部来源获取信息,可能是提高其效果的一种方式。

要点

  • 验证链过程是一种有用的工具,可以结合不同的技术来验证回答的不同部分。
  • 除了许多优点外,验证链过程也存在一定的局限性,可以通过使用不同的工具和机制来减轻这些局限性。
  • 我们可以利用LangChain包来实现这个CoVe过程。

常见问题

本文中显示的媒体不是Analytics Vidhya拥有的,而是根据作者的判断使用的。

Leave a Reply

Your email address will not be published. Required fields are marked *