生成式 AI 与 LangCHain(一)(2)https://developer.aliyun.com/article/1511582
什么是幻觉?
生成语言模型的快速发展,如 GPT-3、Llama 和 Claude 2,引起了人们对其局限性和潜在风险的关注。一个主要关注点是幻觉,即模型生成的输出是荒谬的、不连贯的或不忠实于提供的输入。幻觉在现实世界的应用中会带来性能和安全风险,比如医疗或机器翻译。幻觉的另一个方面是,LLM 生成包含敏感个人信息的文本,比如电子邮件地址、电话号码和实际地址。这引起了重大的隐私问题,因为它表明语言模型可以从其训练语料库中记忆和恢复这些私人数据,尽管它们不存在于源输入中。
幻觉在 LLM 的背景下指的是生成的文本与预期内容不忠实或荒谬的现象。这个术语类比于心理幻觉,后者涉及感知不存在的东西。在 NLG 中,幻觉文本可能在所提供的上下文中看起来流畅和自然,但缺乏具体性或可验证性。忠实性,即生成的内容保持与源的一致性和真实性,被认为是幻觉的反义词。
内在幻觉发生在生成的输出与源内容相矛盾时,而外在幻觉涉及生成无法通过源材料验证或支持的信息。外在幻觉有时可能包括事实上正确的外部信息,但它们的不可验证性引起了来自事实安全角度的担忧。
努力解决幻觉问题正在进行中,但需要在不同任务之间有一个全面的理解,以开发有效的缓解方法。LLM 中的幻觉可能由各种因素引起:
- 编码器的不完美表示学习。
- 错误解码,包括注意到源输入的错误部分和解码策略的选择。
- 曝光偏差,即训练和推理时间之间的差异。
- 参数化知识偏差,其中预训练模型优先考虑自己的知识而不是输入,导致生成过多的信息。
幻觉缓解方法可以分为两组:数据相关方法和建模与推理方法(见“自然语言生成中幻觉的调查”,季子威等,2022 年):
数据相关方法:
构建一个忠实的数据集:从零开始构建具有清洁和忠实目标的数据集,或者在确保语义一致性的同时重写真实句子。
自动清理数据:识别和过滤现有语料库中的不相关或矛盾信息,以减少语义噪声。
信息增强:使用外部信息(如检索到的知识或合成数据)来增强输入,以提高语义理解并解决源目标差异。
建模和推理方法:
架构:修改编码器架构以增强语义解释,注意机制以优先处理源信息,解码器结构以减少幻觉并强制实施隐式或显式约束。
训练:结合规划、强化学习(RL)、多任务学习和可控生成技术,通过改善对齐、优化奖励函数和平衡忠实度与多样性来减轻幻觉。
后处理:通过生成-再精化策略来纠正输出中的幻觉,或使用后处理校正方法专门优化结果的忠实度。
幻觉的结果是,自动事实核查可以应用的地方,是错误信息的传播危险或政治目的的滥用。 错误信息,包括误导信息、欺骗性新闻和谣言,对社会构成了重大威胁,尤其是在内容的创建和传播变得更加容易的社交媒体环境中。社会所面临的威胁包括对科学、公共卫生叙事、社会极化和民主进程的不信任。新闻业和档案学已经广泛研究了这个问题,事实核查倡议也在回应中不断增长。致力于事实核查的组织为独立事实核查人员和记者提供培训和资源,从而扩大专家事实核查工作的规模。解决错误信息问题对于维护信息的完整性并通过打击其对社会的有害影响至关重要。在文献中,这种问题被称为文本蕴含,即模型预测文本对之间的方向性真实关系(即如果,通常,一个阅读 t 的人会推断 h 最有可能是真实的)。在本章中,我们将重点关注通过信息增强和后处理进行自动事实核查。事实可以从 LLMs 或使用外部工具中检索。在前一种情况下,预训练语言模型可以取代知识库和检索模块的位置,利用其广泛的知识来回答开放领域问题,并使用提示来检索特定事实。
图 4.1:三个阶段的自动事实核查流程。
我们可以区分三个阶段:
- 主张检测 - 确定需要验证的主张
- 检索 - 检索证据以查找支持或反驳主张的来源
- 主张验证 - 根据证据评估主张的真实性
从 2018 年开始,从 24 层 BERT-Large 开始,语言模型已经在诸如维基百科之类的大型知识库上进行预训练,因此能够回答来自维基百科或因为其训练集越来越包括其他来源 - 互联网、教科书、arxiv 和 Github 的知识问题。使用简单的掩蔽提示即可查询事实。例如,为了回答问题“微软的总部在哪里?”,该问题将被重写为“微软的总部在[MASK]”,然后输入到语言模型中获取答案。在这种方法中,最终激活函数引起的东西有趣的是,如果一个没有接收源文本(无条件 LLM)的 LLM 产生比接收源文本(有条件 LLM)的 LLM 更小的生成目标损失,这表明所生成的令牌具有幻觉性(Fillippova,2020)。幻象令牌占目标令牌总数的比率可以作为所生成输出中幻象程度的度量标准。在 LangChain 中,我们有一个链可用于事实检查和提示链,其中模型积极质疑陈述中涉及的假设。在这种自我检查的链中,LLMCheckerChain
,模型被提示按顺序进行多次操作,首先将假设明确表达出来,看起来像这样:
Here’s a statement: {statement}\nMake a bullet point list of the assumptions you made when producing the above statement.\n
请注意,这是一个字符串模板,其中花括号中的元素将被变量替换。接下来,这些假设将被反馈给模型,以便通过这样的提示逐个检查它们:
Here is a bullet point list of assertions: {assertions} For each assertion, determine whether it is true or false. If it is false, explain why.\n\n
最后,模型被要求做出最终判断:
In light of the above facts, how would you answer the question '{question}'
LLMCheckerChain
可以自己完成所有这些操作,正如这个例子所示:
from langchain.chains import LLMCheckerChain from langchain.llms import OpenAI llm = OpenAI(temperature=0.7) text = "What type of mammal lays the biggest eggs?" checker_chain = LLMCheckerChain.from_llm(llm, verbose=True) checker_chain.run(text)
模型可以返回不同的结果给该问题,其中的一些是错误的,而一些会被正确地标识为假的。当我尝试这个东西时,我得到了像蓝鲸、北美海狸或已灭绝的巨型恐鸟之类的结果。我认为这是正确的答案:
Monotremes, a type of mammal found in Australia and parts of New Guinea, lay the largest eggs in the mammalian world. The eggs of the American echidna (spiny anteater) can grow as large as 10 cm in length, and dunnarts (mouse-sized marsupials found in Australia) can have eggs that exceed 5 cm in length. • Monotremes can be found in Australia and New Guinea • The largest eggs in the mammalian world are laid by monotremes • The American echidna lays eggs that can grow to 10 cm in length • Dunnarts lay eggs that can exceed 5 cm in length • Monotremes can be found in Australia and New Guinea – True • The largest eggs in the mammalian world are laid by monotremes – True • The American echidna lays eggs that can grow to 10 cm in length – False, the American echidna lays eggs that are usually between 1 to 4 cm in length. • Dunnarts lay eggs that can exceed 5 cm in length – False, dunnarts lay eggs that are typically between 2 to 3 cm in length. The largest eggs in the mammalian world are laid by monotremes, which can be found in Australia and New Guinea. Monotreme eggs can grow to 10 cm in length. > Finished chain.
因此,虽然这不能保证正确的答案,但它可以阻止一些错误的结果。至于扩展检索(或 RAG),我们在本章的问题回答部分中已经看到了这种方法。事实检查方法涉及将声明分解为更小的可检查查询,这些查询可以被构造为问题回答任务。专为搜索领域数据集而设计的工具可以帮助事实检查员有效地查找证据。现成的搜索引擎如 Google 和 Bing 也可以检索涵盖主题和证据的相关内容,以准确捕捉声明的真实性。在下一节中,我们将应用这种方法来返回基于网络搜索和其他工具的结果。在下一节中,我们将实现一个链来总结文档。我们可以问任何问题以从这些文档中得到答案。
如何总结长文档?
在本节中,我们将讨论自动化长文本和研究论文摘要的过程。在当今快节奏的商业和研究环境中,跟上不断增长的信息量可能是一项令人望而生畏的任务。对于计算机科学和人工智能等领域的工程师和研究人员,保持最新发展的状态至关重要。然而,阅读和理解大量的论文可能是耗时费力的。这就是自动化发挥作用的地方。作为工程师,我们的愿望是构建和创新,通过创建管道和进程来自动化避免重复的任务。这种方法经常被误解为懒惰,其实它允许工程师集中精力解决更复杂的挑战,并更有效地利用他们的技能。在这里,我们将搭建一个自动化工具,可以快速概括长文本的内容,以更易消化的格式呈现。此工具旨在帮助研究人员跟上每天发表的论文数量,特别是在人工智能等快速发展的领域。通过自动化摘要过程,研究人员可以节省时间和精力,同时也确保他们了解其领域的最新发展。该工具将基于 LangChain,并利用大型语言模型(LLMs)以更简明和简化的方式概述论文的核心主张、含义和机制。它还可以回答有关论文的特定问题,使其成为文献综述和加速科学研究的有价值资源。作者计划进一步开发该工具,以便自动处理多个文档并针对特定研究领域进行定制。总体而言,这种方法旨在通过提供更高效、更易于访问的方式来让研究人员受益于最新研究。LangChain 支持使用 LLMs 处理文档的 Map Reduce 方法,以实现文档的高效处理和分析。当阅读大型文本并将其拆分为适合 LLM 令牌上下文长度的文档(块)时,可以将链逐个应用于每个文档,然后将输出组合成一个文档。核心主张是 Map Reduce 过程涉及两个步骤:
- 映射步骤——将 LLM 链分别应用于每个文档,将输出视为新文档,然后
- 减少步骤——所有新文档都将传递到一个独立的组合文档链中,以获取单个输出。
图中所示:
图 4.2:LangChain 中的映射减少链(来源:LangChain 文档)。
这种方法的影响是它允许对文档进行并行处理,并且能够使用 LLM 进行推理、生成或分析单个文档以及组合它们的输出。该过程的机制涉及压缩或折叠映射文档,以确保它们适合于组合文档链,这也可能涉及到利用 LLM。如果需要,压缩步骤可以递归执行。下面是加载 PDF 文档并对其进行摘要的简单示例:
from langchain.chains.summarize import load_summarize_chain from langchain import OpenAI from langchain.document_loaders import PyPDFLoader pdf_loader = PyPDFLoader(pdf_file_path) docs = pdf_loader.load_and_split() llm = OpenAI() chain = load_summarize_chain(llm, chain_type="map_reduce") chain.run(docs)
变量pdf_file_path
是一个包含 PDF 文件路径的字符串。映射和减少步骤的默认提示是这样的:
Write a concise summary of the following: {text} CONCISE SUMMARY:
我们可以为每一步指定任何提示。在为本章开发的文本摘要应用中,我们可以看到如何传递其他提示。在 LangChainHub 上,我们可以看到 qa-with sources 提示,它采用了这样一个 reduce/combine 提示:
Given the following extracted parts of a long document and a question, create a final answer with references (\"SOURCES\"). \nIf you don't know the answer, just say that you don't know. Don't try to make up an answer.\nALWAYS return a \"SOURCES\" part in your answer.\n\nQUESTION: {question}\n=========\nContent: {text}
在这个提示中,我们会提出一个具体的问题,但同样我们也可以给 LLM 一个更抽象的指令,提取假设和含义。文本将会是从映射步骤中得到的摘要。这样的指令会有助于避免幻觉。其他指令的示例可能是将文档翻译成不同的语言或以某种风格重新表述。一旦我们开始做很多调用,特别是在映射步骤中,我们会看到成本增加。我们在做很多调用,并且总共使用了很多标记。是时候给这个问题一些关注了!
标记使用情况
在使用模型时,特别是在长循环中,比如使用映射操作时,跟踪标记使用情况并了解你花费了多少钱是很重要的。对于任何严肃的生成式人工智能使用,我们需要了解不同语言模型的能力、定价选项和用例。OpenAI 提供了不同的模型,包括 GPT-4、ChatGPT 和 InstructGPT,以满足各种自然语言处理需求。GPT-4 是一个强大的语言模型,适用于解决自然语言处理中的复杂问题。它提供了基于使用的标记大小和数量的灵活定价选项。ChatGPT 模型,如 GPT-3.5-Turbo,专注于对话应用,如聊天机器人和虚拟助手。它们在生成准确和流畅的响应方面表现出色。ChatGPT 模型的定价基于使用的标记数量。InstructGPT 模型专为单轮指令遵循而设计,并针对快速和准确的响应生成进行了优化。InstructGPT 家族中的不同模型,如 Ada 和 Davinci,提供不同水平的速度和功率。Ada 是最快的模型,适用于速度至关重要的应用,而 Davinci 是最强大的模型,能够处理复杂的指令。InstructGPT 模型的定价取决于模型的能力,从像 Ada 这样的低成本选项到像 Davinci 这样的更昂贵的选项。OpenAI 的 DALL·E、Whisper 和 API 服务用于图像生成、语音转录、翻译和访问语言模型。DALL·E 是一种 AI 驱动的图像生成模型,可以无缝集成到应用程序中,用于生成和编辑新颖的图像和艺术品。OpenAI 提供了三个分辨率层次,允许用户选择他们所需的细节级别。更高的分辨率提供更复杂和更详细的图像,而较低的分辨率提供更抽象的表示。每张图像的价格根据分辨率而变化。Whisper 是一个能够将语音转录为文本并将多种语言翻译成英语的 AI 工具。它有助于捕捉对话,促进交流,并提高跨语言理解。使用 Whisper 的成本基于每分钟的费率。OpenAI 的 API 提供了对强大语言模型的访问,如 GPT-3,使开发人员能够创建高级应用程序。当注册 API 时,用户会获得一个初始的标记使用限制,表示在特定时间范围内与语言模型进行交互的标记数量。随着用户的记录和使用增加,OpenAI 可能会增加标记使用限制,为模型提供更多访问权限。如果用户需要更多标记来支持其应用程序,他们也可以请求配额增加。我们可以通过连接到 OpenAI 回调来跟踪 OpenAI 模型中的标记使用:
with get_openai_callback() as cb: response = llm_chain.predict(text=”Complete this text!”) print(f"Total Tokens: {cb.total_tokens}") print(f"Prompt Tokens: {cb.prompt_tokens}") print(f"Completion Tokens: {cb.completion_tokens}") print(f"Total Cost (USD): ${cb.total_cost}")
在这个例子中,带有 llm_chain
的那一行可以是任何 OpenAI 模型的用法。 我们应该看到成本和令牌的输出。还有其他两种获得令牌使用情况的方法。 除了 OpenAI 回调外,llm
类的 generate()
方法返回 LLMResult
类型的响应而不是字符串。 这包括令牌使用情况和完成原因,例如 (来自 LangChain 文档):
input_list = [ {"product": "socks"}, {"product": "computer"}, {"product": "shoes"} ] llm_chain.generate(input_list)
结果看起来像这样:
LLMResult(generations=[[Generation(text='\n\nSocktastic!', generation_info={'finish_reason': 'stop', 'logprobs': None})], [Generation(text='\n\nTechCore Solutions.', generation_info={'finish_reason': 'stop', 'logprobs': None})], [Generation(text='\n\nFootwear Factory.', generation_info={'finish_reason': 'stop', 'logprobs': None})]], llm_output={'token_usage': {'prompt_tokens': 36, 'total_tokens': 55, 'completion_tokens': 19}, 'model_name': 'text-davinci-003'})
最后,OpenAI API 中的聊天完成响应格式包括一个使用对象,其中包含令牌信息,例如可能看起来像这样的 (节选):
{ "model": "gpt-3.5-turbo-0613", "object": "chat.completion", "usage": { "completion_tokens": 17, "prompt_tokens": 57, "total_tokens": 74 } }
接下来,我们将看一下如何使用 OpenAI 函数和 LangChain 从文档中提取特定的信息。
从文档中提取信息
2023 年 6 月,OpenAI 宣布更新 OpenAI API,包括对函数调用的新功能,这将提供增强的功能。 开发人员现在可以向 gpt-4-0613 和 gpt-3.5-turbo-0613 模型描述功能,并让模型智能生成包含调用这些功能的参数的 JSON 对象。此功能旨在增强 GPT 模型与外部工具和 API 之间的连接,提供了一种可靠的方式来从模型中检索结构化数据。函数调用使开发人员能够创建使用外部工具或 OpenAI 插件回答问题的聊天机器人。 它还允许将自然语言查询转换为 API 调用或数据库查询,并从文本中提取结构化数据。 更新的机制涉及使用新的 API 参数,即 /v1/chat/completions
终点的 functions
。 通过名称、描述、参数和要调用的功能本身对来定义函数。开发人员可以使用 JSON Schema 向模型描述功能,并指定要调用的所需功能。在 LangChain 中,我们可以使用 OpenAI 中的这些函数调用进行信息提取或调用插件。 在信息提取中,我们可以使用 OpenAI 聊天模型中的提取链来指定实体及其属性从文本和文档中提取实体及其属性。 例如,这有助于识别文本中提到的人。 通过使用 OpenAI 函数参数并指定模式,可以确保模型输出所需的实体和属性及其适当的类型。这种方法的意义在于它允许通过定义具有所需属性和类型的模式精确提取实体。 它还能够指定哪些属性是必需的,哪些是可选的。模式的默认格式是字典,但我们还可以在 Pydantic 中定义属性及其类型,以控制和灵活提取过程。这是一个期望的用于 Curricum Vitae(简历)中信息的模式示例:
from typing import Optional from pydantic import BaseModel class Experience(BaseModel): start_date: Optional[str] end_date: Optional[str] description: Optional[str] class Study(Experience): degree: Optional[str] university: Optional[str] country: Optional[str] grade: Optional[str] class WorkExperience(Experience): company: str job_title: str class Resume(BaseModel): first_name: str last_name: str linkedin_url: Optional[str] email_address: Optional[str] nationality: Optional[str] skill: Optional[str] study: Optional[Study] work_experience: Optional[WorkExperience] hobby: Optional[str]
我们将尝试从这份简历中解析信息。利用 LangChain 中的create_extraction_chain_pydantic()
函数,我们可以将我们的模式作为输入,并且输出将是一个符合该模式的实例化对象。简而言之,我们可以尝试以下代码片段:
from langchain.chains import create_extraction_chain_pydantic from langchain.chat_models import ChatOpenAI from langchain.document_loaders import PyPDFLoader pdf_loader = PyPDFLoader(pdf_file_path) docs = pdf_loader.load_and_split() # please note that function calling is not enabled for all models! llm = ChatOpenAI(model_name="gpt-3.5-turbo-0613") chain = create_extraction_chain_pydantic(pydantic_schema=Resume, llm=llm) return chain.run(docs)
我们应该会得到这样的输出:
[Resume(first_name='John', last_name='Doe', linkedin_url='linkedin.com/in/john-doe', email_address='hello@openresume.com', nationality=None, skill='React', study=None, work_experience=WorkExperience(start_date='May 2023', end_date='Present', description='Lead a cross-functional team of 5 engineers in developing a search bar, which enables thousands of daily active users to search content across the entire platform. Create stunning home page product demo animations that drives up sign up rate by 20%. Write clean code that is modular and easy to maintain while ensuring 100% test coverage.', company='ABC Company', job_title='Software Engineer'), hobby=None)]
它离完美还差得远 - 只有一个工作经验被解析出来。但考虑到我们迄今为止所付出的少量工作,这是个不错的开始。请在 Github 上查看完整示例。我们可以添加更多功能,例如猜测个性或领导能力。OpenAI 以特定语法将这些函数调用注入系统消息中,他们的模型已经针对此进行了优化。这意味着函数计入上下文限制,并相应地计入输入标记。LangChain 本身具有将函数调用注入为提示的功能。这意味着我们可以在 LLM 应用程序中使用除 OpenAI 之外的模型提供者进行函数调用。我们现在将着眼于这一点,并将其构建成一个交互式 Web 应用程序与 Streamlit。
使用工具回答问题
LLMs 是在一般语料库数据上进行训练的,可能对需要特定领域知识的任务效果不佳。单独使用 LLMs 不能与环境交互,并访问外部数据源,然而,LangChain 提供了一个平台,用于创建访问实时信息并执行诸如天气预报、预订、建议菜谱和管理任务等任务的工具。代理和链中的工具允许开发由 LLMs 驱动的数据感知和代理性应用程序,并且为解决问题的各种方法提供了广泛的应用范围,扩展了它们的用例并使它们更加多样和强大。工具的一个重要方面是它们可以在特定领域内或处理特定的输入。例如,LLM 缺乏固有的数学能力。然而,像计算器这样的数学工具可以接受数学表达式或方程作为输入并计算结果。LLM 和这样的数学工具结合起来进行计算并提供准确答案。通常,这种检索方法和 LLMs 的组合被称为检索增强生成(RAG),并通过从外部源中检索相关数据并将其注入到背景中来解决 LLMs 的局限性。这些检索的数据作为增强提示给 LLMs 的附加信息。通过通过 RAG 用特定用例的信息根植 LLMs,改善了响应的质量和准确性。通过检索相关数据,RAG 有助于减少 LLMs 的错觉响应。例如,将 LLM 用于医疗应用程序,可以在推断期间从外部来源检索相关医学信息,如医学文献或数据库。然后,这些检索的数据可以被合并到上下文中,以增强生成的响应,并确保它们与特定领域的知识一致和准确。在这种情况下,实施 RAG 的好处是双重的。首先,即使模型的训练数据截止日期过去,它也允许将最新信息纳入响应中。这确保用户甚至对于最新事件或不断发展的主题都可以收到准确和相关的信息。其次,RAG 通过利用来自新闻文章或与特定主题相关的网站等来源的具体上下文,增强了 ChatGPT 提供更详细和上下文的答案的能力。通过检索特定上下文的信息,响应将更加准确。
RAG(检索增强生成)通过从数据源中检索信息,以补充提供给语言模型的提示,为模型提供所需的背景信息以生成准确的响应。RAG 涉及几个步骤:
- 提示: 用户向聊天机器人提供提示,描述他们对输出的期望。
- 研究:执行上下文搜索,并从各种数据源中检索相关信息。这可能涉及查询数据库,基于关键字搜索索引文档,或调用 API 从外部源检索数据。
- 更新资源:检索到的上下文被注入到原始提示中,通过提供访问事实数据,增强提示,提高准确性。
- 叙述:基于这个增强输入,LLM 生成包含事实正确信息的响应,并将其发送回聊天机器人以传递给用户。
因此,通过结合外部数据源并将相关上下文注入提示中,RAG 增强了 LLM 生成准确、最新并与特定领域或主题对齐的响应的能力。图示了通过工具和推理增强 LLM 的过程(来源:https://github.com/billxbf/ReWOO,由 Binfeng Xu 等人于 2023 年 5 月编写的论文 “Decoupling Reasoning from Observations for Efficient Augmented Language Models Resources” 的实现):
图 4.4:工具增强的语言模型范例,利用语言模型的可预见推理能力来提高系统参数和提示效率
让我们看看这个实例!LangChain 中有很多可用的工具,而且 - 如果这还不够 - 自己开发工具也不难。让我们设置一个带有几个工具的代理:
from langchain.agents import ( AgentExecutor, AgentType, initialize_agent, load_tools ) from langchain.chat_models import ChatOpenAI def load_agent() -> AgentExecutor: llm = ChatOpenAI(temperature=0, streaming=True) # DuckDuckGoSearchRun, wolfram alpha, arxiv search, wikipedia # TODO: try wolfram-alpha! tools = load_tools( tool_names=["ddg-search", "wolfram-alpha", "arxiv", "wikipedia"], llm=llm ) return initialize_agent( tools=tools, llm=llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True )
了解一个重要的细节是 AgentExecutor
是一个链,因此 - 如果我们想的话 - 我们可以将其集成到更大的链中。我们可以使用不同的语法初始化这个链,就像这样:
return MRKLChain.from_chains(llm, chains, verbose=True)
在这种语法中,我们将工具传递为链配置。MRKL 代表模块化推理、知识和语言。零射击代理是 MRKL 框架中最通用的行动代理。请注意 ChatOpenAI
构造函数中的参数 streaming
设置为 True
。这样做可以提供更好的使用体验,因为它意味着文本响应将随着其到来而更新,而不是一次性完成所有文本。目前只有 OpenAI、ChatOpenAI 和 ChatAnthropic 实现支持流式处理。所有提到的工具都有其特定的用途,这是描述的一部分,传递给语言模型。这些工具是插入到代理中的:
- DuckDuckGo - 一个注重隐私的搜索引擎;它的一个额外优势是不需要开发者注册。
- Wolfram Alpha - 一个结合了自然语言理解和数学能力的集成,用于像“2x+5 = -3x + 7?”这样的问题。
- Arxiv - 搜索学术预印出版物;这对于研究导向的问题很有用。
- Wikipedia - 有关任何有重要名声的实体的问题
请注意,要使用 Wolfram Alpha,您必须设置一个帐户,并设置 WOLFRAM_ALPHA_APPID
环境变量,其中包含您在 developer.wolframalpha.com/
创建的开发者令牌。除了 DuckDuckGo 之外,LangChain 还集成了很多其他搜索工具,可以利用 Google 或 Bing 搜索引擎,或者使用元搜索引擎。还有一个用于天气信息的 Open-Meteo 集成,但是这些信息也可以通过搜索获得。让我们将我们的 agent 设计为一个 streamlit 应用。
Streamlit是一个面向机器学习和数据科学团队的开源应用程序框架。它允许用户使用 Python 在几分钟内创建美观的网络应用程序。
让我们使用我们刚刚定义的load_agent()
函数编写代码:
import streamlit as st from langchain.callbacks import StreamlitCallbackHandler chain = load_agent() st_callback = StreamlitCallbackHandler(st.container()) if prompt := st.chat_input(): st.chat_message("user").write(prompt) with st.chat_message("assistant"): st_callback = StreamlitCallbackHandler(st.container()) response = chain.run(prompt, callbacks=[st_callback]) st.write(response)
请注意,我们在调用链中使用回调处理程序,这意味着我们将在返回结果时立即看到响应。我们可以在终端上像这样本地启动应用:
PYTHONPATH=. streamlit run question_answering/app.py
Streamlit 应用程序的部署可以在本地或服务器上进行。或者,您可以在 Streamlit Community Cloud 上或 Hugging Face Spaces 上部署此应用程序。
- 对于Streamlit Community Cloud,请执行以下操作:
- 1. 创建一个 Github 存储库
- 2. 前往 Streamlit Community Cloud,点击“New app”,然后选择新的存储库
- 3. 点击“部署!”
- 至于Hugging Face Spaces,它的工作原理如下:
- 1. 创建一个 Github 仓库
- 2. 在 https://huggingface.co/ 上创建一个 Hugging Face 账户
- 3. 前往“Spaces”,然后点击“Create new Space”。在表单中,填写一个名称,将空间类型设置为“Streamlit”,并选择新的存储库。
这是应用程序的截图:
Figure 4.5: 在 Streamlit 中的问答应用程序。
搜索结果相当不错,尽管具体取决于所使用的工具,可能仍然会出现错误结果。对于关于拥有最大蛋的哺乳动物的问题,使用 DuckDuckGo 搜索会返回一篇讨论鸟类和哺乳动物蛋的文章,并有时得出鸵鸟是拥有最大蛋的哺乳动物的结论,尽管鸭嘴兽有时也会出现。以下是正确推理的日志输出(缩写版):
> Entering new AgentExecutor chain... I'm not sure, but I think I can find the answer by searching online. Action: duckduckgo_search Action Input: "mammal that lays the biggest eggs" Observation: Posnov / Getty Images. The western long-beaked echidna ... Final Answer: The platypus is the mammal that lays the biggest eggs. > Finished chain.
你可以看到,拥有一个强大的自动化和问题解决框架,你可以将需要数百小时才能完成的工作压缩到几分钟之内。你可以尝试不同的研究问题,以了解工具的使用方式。书籍仓库中的实际实现允许您尝试不同的工具,并提供了自验证选项。检索增强生成(RAG)与 LLMs 可以通过将来自外部来源的相关数据注入上下文中,显著提高响应的准确性和质量。通过用特定用例的知识基础来奠定 LLMs 的基础,我们可以减少幻觉,并使它们在真实世界情景中更有用。RAG 比重新训练模型更具成本效益和效率。您可以在 BlockAGI 项目中看到使用 LangChain 的增强信息检索的非常高级的示例,该项目受到 BabyAGI 和 AutoGPT 的启发,网址为github.com/blockpipe/BlockAGI
。在接下来的几节中,我们将通过它们的决策制定策略来比较主要类型的代理。
推理策略
当前一代的生成模型,如 LLMs,擅长发现现实世界数据的模式,例如视觉和音频信息,以及非结构化文本,但是它们在涉及结构化知识表示和推理的任务所需的符号操作方面存在困难。推理问题对 LLMs 构成挑战,并且有不同的推理策略可以补充神经网络作为生成模型固有的模式完成能力。通过专注于在提取的信息上实现符号操作,这些混合系统可以增强语言模型的能力。模块化推理,知识和语言(MRKL)是一个结合语言模型和工具来执行推理任务的框架。在 LangChain 中,这包括三个部分:
- 工具,
- 一个
LLMChain
,以及 - 代理本身。
工具是代理可以使用的可用资源,如搜索引擎或数据库。LLMChain 负责生成文本提示并解析输出以确定下一步操作。代理类使用 LLMChain 的输出来决定采取哪些行动。我们在第二章,LangChain 简介中讨论了工具使用策略。我们可以在此图表中看到观察模式的推理:
图 4.6:推理观察(来源:https://arxiv.org/abs/2305.18323;许滨峰等人,2023 年 5 月)。
依赖观察的推理包括基于当前知识状态或通过观察获取的证据做出判断、预测或选择。在每次迭代中,代理向语言模型(LLM)提供上下文和示例。用户的任务首先与上下文和示例结合,然后交给 LLM 启动推理。LLM 生成一个思考和一个动作,然后等待来自工具的观察。观察结果添加到提示中以启动下一次对 LLM 的调用。在 LangChain 中,这是一个行动代理(也称为零-shot 代理, ZERO_SHOT_REACT_DESCRIPTION
),这是创建代理时的默认设置。正如提到的,计划也可以在任何操作之前制定。在 LangChain 中称为计划和执行代理的策略在这里示例化:
图 4.7: 从观察中分离推理(来源:https://arxiv.org/abs/2305.18323;许彬锋等人,2023 年 5 月)。
规划器(一个 LLM)可以针对规划和工具使用进行微调,产生一个计划列表(P)并调用一个工作者(在 LangChain 中:代理)通过使用工具来收集证据(E)。P 和 E 与任务结合,然后传递给求解器(一个 LLM)得出最终答案。我们可以编写一个伪算法如下:
- 规划所有步骤(规划器)
- 对于步骤中的每一步:
- 确定完成步骤所需的适当工具
规划器和求解器可以是不同的语言模型。这样就可以使用更小、更专业的规划器和求解器模型,并针对每次调用使用更少的标记。我们可以在我们的研究应用程序中实现计划和求解,让我们做吧!首先,让我们向load_agent()
函数添加一个strategy
变量。它可以取两个值,即“plan-and-solve”或“one-shot-react”。对于“one-shot-react”,逻辑保持不变。对于“plan-and-solve”,我们将定义一个规划器和一个执行器,我们将使用它们来创建一个PlanAndExecute
代理执行器:
from typing import Literal from langchain.experimental import load_chat_planner, load_agent_executor, PlanAndExecute ReasoningStrategies = Literal["one-shot-react", "plan-and-solve"] def load_agent( tool_names: list[str], strategy: ReasoningStrategies = "one-shot-react" ) -> Chain: llm = ChatOpenAI(temperature=0, streaming=True) tools = load_tools( tool_names=tool_names, llm=llm ) if strategy == "plan-and-solve": planner = load_chat_planner(llm) executor = load_agent_executor(llm, tools, verbose=True) return PlanAndExecute(planner=planner, executor=executor, verbose=True) return initialize_agent( tools=tools, llm=llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True )
为了简洁起见,我省略了我们之前已有的导入操作。让我们定义一个通过 Streamlit 中的单选按钮设置的新变量。我们将把这个变量传递给load_agent()
函数:
strategy = st.radio( "Reasoning strategy", ("plan-and-solve", "one-shot-react", ))
你可能注意到load_agent()
接受一个字符串列表tool_names
。这也可以在用户界面(UI)中选择:
tool_names = st.multiselect( 'Which tools do you want to use?', [ "google-search", "ddg-search", "wolfram-alpha", "arxiv", "wikipedia", "python_repl", "pal-math", "llm-math" ], ["ddg-search", "wolfram-alpha", "wikipedia"])
最后,在应用程序中,代理如下加载:
agent_chain = load_agent(tool_names=tool_names, strategy=strategy)
我们可以在这里看到 UI:
图 4.8: 在我们的研究应用程序中实现计划和执行。
请查看应用程序,并查看关于“在大型语言模型的背景下什么是计划和求解代理”的不同步骤。简单地说,第一步,计划如下:
- 定义大型语言模型:大型语言模型是经过大量文本数据训练的人工智能模型,可以根据其接收到的输入生成类似人类的文本。
- 在大型语言模型的背景下理解计划的概念:在大型语言模型的背景下,计划是指模型生成的解决问题或回答问题的结构化大纲或一组步骤。
- 在大型语言模型的背景下理解求解代理的概念:求解代理是大型语言模型的组成部分,负责生成解决问题或回答问题的计划。
- 认识到计划和求解代理在大型语言模型中的重要性:计划和求解代理有助于组织模型的思维过程,并为解决问题或回答问题的任务提供了结构化方法。
- 给出以上所采取的步骤后,请回答用户的原始问题:在大型语言模型的背景下,计划是由求解代理生成的结构化大纲或一组步骤,用于解决问题或回答问题。求解代理是大型语言模型的组成部分,负责生成这些计划。
因此,第一步是执行 LLMs 的查找:
Action: { "action": "Wikipedia", "action_input": "large language models" }
我们没有讨论这个问题的另一个方面,即在这些步骤中使用的提示策略。例如,不同的提示策略提供了解决 LLM 复杂推理问题挑战的方法。一种方法是few-shot chain-of-thought(CoT)提示,其中 LLM 通过逐步推理演示进行引导。例如,在算术推理中,可以向 LLM 展示解方程的示例演示,以帮助其理解过程。另一种策略是zero-shot-CoT提示,它消除了手动演示的需要。而是,将类似“让我们逐步思考”这样的通用提示附加到提供给 LLM 的问题陈述中。这使模型能够在没有先前明确示例的情况下生成推理步骤。在算术推理中,问题陈述可以附加这个提示并输入 LLM。Plan-and-Solve (PS)提示,涉及将复杂任务分解为较小的子任务,并根据计划逐步执行它们。例如,在解方程或涉及多个步骤的单词问题的数学推理问题中,PS 提示使 LLM 能够为接近每个子步骤制定计划,如提取相关变量和计算中间结果。为了进一步提高推理步骤和指导的质量,引入了**PS+**提示。它包括更详细的说明,如强调提取相关变量并考虑计算和常识。PS+提示确保 LLM 更好地理解问题并能够生成准确的推理步骤。例如,在算术推理中,PS+提示可以指导 LLM 识别关键变量,正确执行计算,并在推理过程中应用常识知识。这结束了我们对推理策略的讨论。所有策略都存在问题,可能表现为计算错误、缺少步骤错误和语义误解。然而,它们有助于提高生成的推理步骤的质量,增加问题解决任务的准确性,并增强 LLM 处理各种类型推理问题的能力。
总结
在本章中,我们谈到了幻觉问题,自动事实核查以及如何使 LLMs 更加可靠。工具和提示策略特别受到强调。我们首先查看并实施了提示策略,以分解和总结文档。这对于消化大型研究文章或分析非常有帮助。一旦我们开始大量调用 LLMs,这就意味着我们会产生很多成本。因此,我专门为令牌使用分配了一节。工具为 LLMs 在各个领域提供了创造性的解决方案,并开辟了新的可能性。例如,可以开发一个工具,使 LLM 能够执行高级检索搜索,查询数据库以获取特定信息,自动编写电子邮件,甚至处理电话。OpenAI API 实现了我们可以使用的功能,其中包括在文档中进行信息提取等。我们已经实现了一个非常简单的 CV 解析器版本,作为此功能的示例。但是,工具和函数调用并不是 OpenAI 的特有功能。通过 Streamlit,我们可以实现调用工具的不同代理。我们已经实现了一个应用程序,可以通过依赖外部工具(如搜索引擎或维基百科)来回答研究问题。然后,我们查看了代理使用的不同策略来做出决策。主要区别在于决策点。我们将一个计划和解决代理实施到了 Streamlit 应用程序中。我希望这能表明,在几行代码中,我们可以实现在几种情况下非常令人印象深刻的应用程序。然而,重要的是要明确,本章中开发的应用程序具有局限性。它们可以帮助您显著提高效率,但是您 - 作为人类 - 必须运用判断力并改进写作,以确保其连贯和合理。让我们看看您是否记得本章的一些关键要点!
问题
请看一下,看看你能否从记忆中得出这些问题的答案。如果对任何一个不确定,请返回本章的相应部分:
- 什么是幻觉?
- 自动事实核查是如何工作的?
- 在 LangChain 中,我们可以采取什么措施来确保输出有效?
- LangChain 中的 map-reduce 是什么?
- 我们如何统计我们使用的令牌数量(以及为什么应该这样做)?
- RAG 代表什么,使用它有什么优势?
- LangChain 中有哪些工具可用?
- 请定义计划和解决代理
- 请定义一次性代理
- 我们如何在 Streamlit 中实现文本输入字段和单选按钮?
第五章:像 ChatGPT 一样构建聊天机器人
什么是聊天机器人?
聊天机器人是一种可以与用户聊天、提供信息和支持、预订事物以及执行各种其他任务的人工智能程序。它用于重现与用户的强大互动,并可用于不同的行业和不同的目的。聊天机器人有益的原因在于它们可以自动化任务、提供即时响应,并为用户提供个性化体验。它们可以用于客户支持、潜在客户生成、销售、信息检索等。聊天机器人可以节省时间、提高效率、增强客户体验,并简化业务流程。聊天机器人通过利用自然语言处理(NLP)和机器学习算法工作。它们分析用户输入,理解其意图,并生成适当的回应。它们可以设计为与基于文本的消息平台或基于语音的应用程序配合使用。客户服务中聊天机器人的一些用例包括提供全天候支持、处理常见问题、协助产品推荐、处理订单和付款以及解决简单的客户问题。聊天机器人的更多用例包括:
- 预约安排:聊天机器人可以帮助用户安排预约、预订预约并管理其日历。
- 信息检索:聊天机器人可以为用户提供特定信息,如天气更新、新闻文章或股票价格。
- 虚拟助手:聊天机器人可以充当个人助手,帮助用户完成任务,如设置提醒、发送消息或打电话。
- 语言学习:聊天机器人可以通过提供互动对话和语言练习来协助语言学习。
- 心理健康支持:聊天机器人可以提供情感支持,提供资源,并进行心理健康对话。
- 教育:在教育环境中,正在探索虚拟助手作为虚拟导师的潜力,帮助学生学习和评估他们的知识,回答问题,并提供个性化的学习体验。
- 人力资源和招聘:聊天机器人可以协助招聘流程,通过筛选候选人、安排面试并提供有关职位空缺的信息。
- 娱乐:聊天机器人可以让用户参与互动游戏、测验和故事体验。
- 法律:聊天机器人可用于提供基本法律信息,回答常见法律问题,协助法律研究,并帮助用户导航法律程序。它们还可以协助文件准备,如起草合同或创建法律表格。
- 医学:聊天机器人可以协助检查症状,提供基本医疗建议,并提供心理健康支持。它们可以通过提供相关信息和建议,改善临床决策,帮助医疗专业人员。
这些只是一些例子,聊天机器人的用途正在不断扩展到各个行业和领域。任何领域的聊天技术都有潜力使信息更易获取,并为寻求帮助的个人提供初步支持。
什么是最先进的技术?
图灵测试,以英国计算机科学家、密码分析师和数学家艾伦·图灵命名,是人工智能(AI)中一种用于确定计算机是否具有像人类一样思考能力的调查方法。尽管关于图灵测试的相关性以及以此为基础的竞赛的有效性存在着很多争论,但这一测试仍然是讨论和研究人工智能的哲学起点。随着我们对人工智能的不断进步,以及对人类大脑功能的更深入理解和映射,图灵测试仍然是定义智能的基础,并且是关于我们对技术期望达到什么程度才能被认为是“思考机器”的辩论的基线。图灵提出,如果计算机可以在特定条件下模仿人类的反应,那么可以说计算机具有人工智能。最初的图灵测试需要三个终端,每个终端在物理上与其他两个终端分开。一个终端由计算机操作,另外两个由人类操作。在测试过程中,一个人类扮演提问者的角色,而另一个人类和计算机扮演回答者的角色。提问者在特定主题领域内使用指定的格式和上下文对回答者进行审问。经过预设的时间或问题数量后,提问者被要求决定哪个回答者是人类,哪个是计算机。自从测试形式形成以来,许多人工智能已经能够通过测试;其中最早的一个是 Joseph Weizenbaum 的 ELIZA。1966 年,他发表了一篇关于他的聊天机器人 ELIZA 的文章,“ELIZA - 一个用于研究人机自然语言交流的计算机程序。”ELIZA 是最早创建的聊天机器人之一,模拟了心理医生的角色。作为一种展示技术局限性的幽默,这个聊天机器人采用了简单的规则和模糊的开放式问题,以在对话中给人一种共情理解的印象,被认为是人工智能的一个具有讽刺意味的里程碑。然而,ELIZA 知识有限,只能在特定话题领域内进行对话。它也无法长时间保持对话或从对话中学习。图灵测试多年来一直备受批评,特别是因为历史上,问询的性质必须受限,以使计算机表现出类似于人类的智能。很多年来,只有当提问者制定查询时,计算机才会获得较高的分数,因此问题只能有“是”或“否”回答,或者与一小范围的知识领域相关。当问题是开放式的,并需要对话回答时,计算机程序能够成功欺骗提问者的可能性就小了。此外,像 ELIZA 这样的程序可以通过操纵它并不完全理解的符号来通过图灵测试。哲学家约翰·西尔尔(John Searle)认为这并不决定与人类相媲美的智能。对许多研究人员来说,计算机是否能通过图灵测试的问题已经变得无关紧要。与其专注于如何说服某人他们正在与一个人而不是一个计算机程序对话,真正的关注应该集中在如何使人机交互更直观和高效。���如,通过使用会话接口。1972 年,另一个重要的聊天机器人 PARRY 被开发出来,它扮演了一名患有精神分裂症的病人。它具有固定的个性,其回答是基于一系列假设,并且情绪反应是由用户表述的变化触发的。在 1979 年的一次实验中,PARRY 被五名精神科医生测试,他们必须确定他们所交流的患者是一个计算机程序还是真正的精神分裂病患者。结果各有不同,有些精神科医生给出了正确的诊断,而有些给出了
以下是一些聊天机器人的示例:
- ELIZA:ELIZA 是最早的聊天机器人之一,于上世纪 60 年代开发,使用模式匹配来模拟与用户的对话。
- Siri:Siri 是由苹果开发的流行的基于语音的聊天机器人。它集成在苹果设备中,可以执行任务,回答问题并提供信息。
- Alexa:Alexa 是亚马逊开发的智能个人助理。它可以响应语音命令,播放音乐,提供天气更新,并控制智能家居设备。
- Google Assistant:Google Assistant 是由谷歌开发的聊天机器人。它可以回答问题,提供建议,并根据用户命令执行任务。
- Mitsuku:Mitsuku 是一款多次赢得 Loebner 奖图灵测试的聊天机器人。它以参与自然和类似人类的对话而闻名。
这些只是一些示例,各行业和应用中还有许多其他聊天机器人可用。
使用图灵测试及其衍生物的一个关注点是它侧重于模仿和欺骗,而更有意义的测试应该强调开发者需要专注于创建有用和有趣的功能,而不仅仅是执行花招。使用基准测试和学术/专业考试提供了对 AI 系统性能的更具体评估。目前该领域研究人员的目标是为测试人工智能(AI)系统的能力提供更好的基准,特别是对于大型语言模型(LLM)如 GPT-4。他们旨在了解 LLM 的限制并确定可能失败的领域。先进的 AI 系统,包括 GPT-4,在与语言处理相关的任务上表现出色,但在简单的视觉逻辑难题上很难。LLM 可以根据统计相关性生成合理的下一个词,但可能缺乏推理或对抽象概念的理解。研究人员对 LLM 的能力有不同的看法,有些人将其成就归因于有限的推理能力。测试 LLM 并了解其能力具有实际意义。它可以帮助在医学和法律等现实领域安全有效地应用 LLM。通过确定 LLM 的优势和劣势,研究人员可以确定如何最好地利用它们。ChatGPT 的训练使其更擅长处理幻觉,与其前身相比,这意味着它生成荒谬或无关紧要的回应的可能性较小。然而,重要的是要注意,ChatGPT 仍然可以自信地提供不准确的信息,因此用户应谨慎并验证提供的信息。上下文和记忆在确保聊天机器人的对话提供准确信息和对之前互动的准确反映的响应方面起着重要作用,从而使其能够更忠实地与用户互动。我们现在将更详细地讨论这一点。
上下文和记忆
上下文和记忆是聊天机器人设计的重要方面。它们使聊天机器人能够保持对话的上下文,回应多个查询,并存储和访问长期记忆。它们是调节聊天机器人响应准确性和忠实度的重要因素。在聊天机器人中,记忆和上下文的重要性可与人与人之间对话中记忆和理解的重要性相比。没有回顾过去的交流或理解或了解更广泛背景的对话可能会杂乱无章,并导致误解,从而导致不令人满意的对话体验。上下文理解极大地影响着聊天机器人响应的准确性。它指的是聊天机器人理解整个对话以及一些相关背景的能力,而不仅仅是用户最后一条消息。了解上下文的聊天机器人能够保持对话的整体视角,使对话流程更加自然和人性化。记忆保持直接影响着聊天机器人性能的忠实度,这涉及识别和记住以前对话中的事实以供将来使用的一致性。此功能增强了用户的个性化体验。例如,如果用户说:“给我显示最便宜的航班”,然后跟着说:“那个地区的酒店怎么样?”如果没有上下文的前一条消息,聊天机器人将不知道用户指的是哪个地区。在反向场景中,一个具有上下文意识的聊天机器人会理解用户在谈论与飞行目的地相同地区的住宿。记忆缺乏导致对话中的一致性不足(忠实度不足)。例如,如果用户在一次对话中已经通过姓名确定了自己的身份,而机器人在下一次对话中忘记了这些信息,就会导致不自然和不个性化的互动。记忆和上下文对于使聊天机器人的互动更加有效、准确、亲切和令人满意至关重要。如果没有这些元素,机器人可能会显得不足、僵化并且无法与人类对话伙伴建立联系。因此,这些特征对于计算机和人类之间复杂而令人满意的互动至关重要。LLM 聊天机器人的一个新方面是它们不仅可以对意图做出响应,还可以更智能地与用户进行对话。这被称为主动对话。
有意与主动
在语言模型或聊天机器人的背景下,主动指的是系统在未明确受到用户提示的情况下启动行动或提供信息的能力。它涉及根据先前的互动或上下文线索预测用户的需求或偏好。另一方面,有意意味着聊天机器人被设计为理解并满足用户的意图或请求,并根据这些意图和期望的结果采取具体的行动或提供相关的回应。主动式聊天机器人很有用,因为它可以与客户建立联系并改善他们的体验,从而创建更好的客户旅程。这可以通过节省时间和精力来增强用户体验,还可以通过在问题出现之前快速有效地解决客户查询来提高客户满意度。主动沟通对于企业的成功至关重要,因为它提高了客户的生命周期价值(CLV)并降低了运营成本。通过主动预测客户的需求并主动提供信息,企业可以控制沟通并以有利的方式构建对话。这建立了信任、客户忠诚度,并增强了组织的声誉。此外,主动沟通通过在客户提问之前解决客户查询并减少来电支持电话,有助于提高组织的生产率。技术上,这种能力可以通过上下文、记忆和推理机制实现。这是本章的重点。在下一节中,我们将讨论现代聊天机器人的基础知识,如检索增强语言模型(RALM)以及实现它们所需的技术背景。
检索和向量
在第四章中,我们讨论了检索增强生成(RAG),其旨在通过利用外部知识和确保生成的文本准确和上下文适当来增强生成过程。在本章中,我们将进一步讨论如何结合检索和生成技术来提高生成文本的质量和相关性。特别是,我们将讨论检索增强语言模型(RALM),这是 RAG 的特定实现或应用,它指的是在生成过程中条件于一个基准语料库(即一系列书面文本)中的相关文档的语言模型。在检索中,利用语义过滤和向量存储来预过滤大型文档库中的相关信息,并将该信息纳入生成过程。这个检索包括文档的向量存储。
检索增强语言模型(RALMs)是具有检索组件以增强性能的语言模型。传统语言模型根据输入提示生成文本,但是 RALMs 通过从大量文档中检索相关信息,并将该信息用于生成更准确和上下文相关的响应而进一步发展。
RALMs 的好处包括:
- 提高性能:通过整合主动检索,LM 可以从外部来源访问相关信息,从而增强其生成准确和信息丰富响应的能力。
- 避免输入长度限制:检索增强 LM 丢弃先前检索的文档,并仅使用当前步骤检索的文档来调节下一生成。这有助于防止达到 LM 的输入长度限制,使它们能够处理更长和更复杂的查询或任务。
更详细地说,检索增强 LM 的工作机制涉及以下步骤:
- 检索:RALMs 从大型语料库中搜索相关文档或段落。LM 根据向量相似性搜索查询和当前上下文来从外部源检索相关信息。
- 调节:检索的信息用于调节 LLM 的下一生成。这意味着 LM 将检索的信息整合到其语言模型中,以生成更准确和上下文适当的响应。
- 迭代过程:检索和调节步骤是迭代执行的,每一步建立在前一步之上。这种迭代过程允许 LLM 通过整合来自外部源的相关信息逐渐提高其理解和生成能力。
检索的信息可以以不同方式使用。它可以作为语言模型的额外上下文,帮助其生成更准确和上下文相关的响应。它还可以用于提供事实信息或在生成的文本中回答特定问题。有两种主要的检索增强生成策略:
- 单次检索增强生成:这一策略涉及使用用户输入作为检索的查询,并一次生成完整的答案。检索的文档与用户输入拼接在一起,用作语言模型生成的输入。
- 主动检索增强生成:这一策略涉及在生成过程中积极决定何时以及检索什么内容。在生成的每个步骤中,根据用户输入和先前生成的输出,制定检索查询。检索的文档随后用作语言模型的输入进行生成。这种策略允许检索和生成交错进行,使模型能够根据需要动态检索信息。
在增强生成框架中,有两种前瞻性方法称为 FLARE(前瞻性增强检索生成):
- 使用检索指令的 FLARE(检索指令 FLARE):该方法在生成回答时需要时提示语言模型生成检索查询。它使用鼓励检索的指令,例如“[搜索(查询)]”,以表示需要额外的信息。
- FLARE Direct(直接 FLARE):该方法直接使用语言模型生成的内容作为搜索查询。它迭代地生成下一句以了解未来的主题,并且在存在不确定的令牌时,它会检索相关文档以重新生成下一句。
不同于传统方法只检索一次,然后用于生成,FLARE 采取迭代过程。它包括使用即将出现的句子预测作为查询来检索相关文档。如果初始生成的置信度较低,这允许系统重新生成句子。RALM 在诸如问答、对话系统和信息检索等任务中显示出很好的结果。它们可以通过利用外部知识源来提供更准确和有信息的回答。此外,通过在特定领域的文档集上对其进行培训,RALM 可以进行微调,从而增强其在专业应用中的效用。总体而言,通过融合检索,RALM 可以利用文档语料库中丰富的知识,使其在各种自然语言处理任务中更加强大和有用。RALM 利用主动检索来提升性能并克服处理复杂查询或任务的限制。LangChain 实现了构建检索系统的不同构建块的工具链。这包括数据加载器、文档转换器、嵌入模型、向量存储和检索器。它们之间的关系在此图表中显示(来源:LangChain 文档):
图 5.1:向量存储和数据加载器。
在 LangChain 中,我们首先通过数据加载器加载文档。 然后我们可以转换它们,将这些文档传递给一个向量存储作为嵌入。 然后我们可以查询向量存储或与向量存储相关联的检索器。 LangChain 中的检索器可以将加载和向量存储封装为一个步骤。 在本章中,我们将大多数跳过转换,但是你将在数据加载器、嵌入、存储机制和检索器的示例中找到解释。 由于我们正在讨论向量存储,我们需要讨论向量搜索,这是一种根据它们与查询向量的相似度搜索和检索向量(或嵌入)的技术。 它通常用于诸如推荐系统、图像和文本搜索以及异常检测等应用中。 我们将更深入地了解 RALM 背后的基础知识,现在我们将从嵌入开始。 一旦你理解了嵌入,你就能够构建从搜索引擎到聊天机器人的一切。
嵌入
嵌入是以机器可处理和理解的方式对内容进行数字表示。 这个过程的本质是将对象(比如图像或文本)转换为一个向量,尽可能地封装其语义内容,同时丢弃不相关的细节。 嵌入将一段内容,比如一个单词、句子或图像,映射到一个多维向量空间中。 两个嵌入之间的距离表示对应概念(原始内容)之间的语义相似度。
嵌入是机器学习模型生成的数据对象的表示形式。它们可以将单词或句子表示为数字向量(浮点数列表)。 至于 OpenAI 语言嵌入模型,嵌入是一个包含 1536 个浮点数的向量,表示文本。 这些数字来自于一个捕捉语义内容的复杂语言模型。
举个例子 - 比如我们有单词猫和狗 - 这些可以在一个与词汇表中的所有其他单词一起的空间中以数字方式表示。 如果空间是三维的,那么这些可以是如下形式的向量:对于猫是[0.5, 0.2, -0.1],对于狗是[0.8, -0.3, 0.6]。 这些向量编码了这些概念与其他单词的关系的信息。 粗略地说,我们期望猫和狗的概念与动物的概念更接近(更相似),而不是计算机或嵌入概念。
可以使用不同的方法创建嵌入。对于文本,一个简单的方法是词袋方法,其中每个单词由其在文本中出现的次数表示。这种方法在 scikit-learn 库中被实现为CountVectorizer
,在word2vec出现之前很受欢迎。Word2vec 大致上通过预测句子中的单词来学习嵌入,忽略了线性模型中单词顺序。嵌入的总体思想在这张图中有所展示(来源:“解释类比:向理解词嵌入迈进” by 卡尔·艾伦和蒂莫西·霍斯佩代尔, 2019; https://arxiv.org/abs/1901.09813)。
图 5.2:Word2Vec 词嵌入在 3D 空间中。我们可以用这些向量进行简单的向量运算,例如减去男人向量加上女人向量得到更接近女王的向量。
对于图像,嵌入可以来自特征提取阶段,例如边缘检测、纹理分析和颜色组成。这些特征可以在不同的窗口大小上提取,使表示既具有尺度不变性又具有平移不变性(尺度空间表示)。如今,通常情况下,卷积神经网络(CNNs)是在大型数据集(如 ImageNet)上进行预训练,以学习图像属性的良好表示。由于卷积层在输入图像上应用一系列滤波器(或卷积核)来生成特征图,概念上类似于尺度空间。当经过预训练的 CNN 在新图像上运行时,它可以输出一个嵌入向量。如今,对于大多数领域,包括文本和图像,嵌入通常来自基于变换器的模型,这些模型考虑了句子和段落中单词的上下文和顺序。根据模型架构,尤其是参数数量,这些模型可以捕捉非常复杂的关系。所有这些模型都是在大型数据集上进行训练,以建立概念及其关系。这些嵌入可以用于各种任务。通过将数据对象表示为数值向量,我们可以对它们进行数学运算,并衡量它们的相似性,或将它们用作其他机器学习模型的输入。通过计算嵌入之间的距离,我们可以执行搜索和相似性评分等任务,或对对象进行分类,例如按主题或类别。例如,我们可以通过检查产品评论的嵌入是否更接近积极或消极的概念来执行简单的情感分类器。
嵌入之间的距离度量
在向量相似性计算中使用了不同的距离度量方法,例如:
- 余弦距离是一种相似度度量,它计算向量空间中两个向量之间的夹角的余弦。它的取值范围是从 -1 到 1,其中 1 表示相同的向量,0 表示正交的向量,-1 表示完全相反的向量。
- 欧氏距离:它衡量向量空间中两个向量之间的直线距离。它的取值范围是从 0 到无穷大,其中 0 表示相同的向量,较大的值表示越不相似的向量。
- 点积:它测量两个向量的大小乘积和它们之间的夹角的余弦。它的取值范围是从 -∞ 到 ∞,其中正值表示指向相同方向的向量,0 表示正交向量,负值表示指向相反方向的向量。
在 LangChain 中,您可以通过使用 OpenAIEmbeddings
类的 embed_query()
方法来获取嵌入。以下是一个代码示例:
from langchain.embeddings.openai import OpenAIEmbeddings embeddings = OpenAIEmbeddings() text = "This is a sample query." query_result = embeddings.embed_query(text) print(query_result) print(len(query_result))
此代码将单个字符串输入传递给 embed_query
方法,并检索相应的文本嵌入。结果存储在 query_result
变量中。可以使用 len()
函数获取嵌入的长度(维度数)。我假设您已经像第三章“LangChain 入门”中建议的那样将 API 密钥设置为环境变量。您也可以使用 embed_documents()
方法获取多个文档输入的嵌入。以下是一个示例:
from langchain.embeddings.openai import OpenAIEmbeddings words = ["cat", "dog", "computer", "animal"] embeddings = OpenAIEmbeddings() doc_vectors = embeddings.embed_documents(words)
在这种情况下,embed_documents()
方法用于检索多个文本输入的嵌入。结果存储在 doc_vectors
变量中。我们本来可以检索长文档的嵌入 - 相反,我们只检索了单个单词的向量。我们还可以在这些嵌入之间进行算术运算,例如计算它们之间的距离:
from scipy.spatial.distance import pdist, squareform import pandas as pd X = np.array(doc_vectors) dists = squareform(pdist(X))
这给了我们单词之间的欧氏距离作为一个方阵。让我们来画出它们:
import pandas as pd df = pd.DataFrame( data=dists, index=words, columns=words ) df.style.background_gradient(cmap='coolwarm')
距离图应该是这样的:
图 5.3:单词 cat、dog、computer、animal 的嵌入之间的欧氏距离。
我们可以确认:猫和狗确实比起计算机更接近动物。这里可能会有很多问题,例如,狗是否比猫更像动物,或者为什么狗和猫与计算机的距离仅比与动物的距离稍远一点。尽管在某些应用中这些问题可能很重要,但让我们记住这只是一个简单的例子。在这些例子中,我们使用了 OpenAI 的嵌入——在接下来的例子中,我们将使用由 Huggingface 提供的模型的嵌入。LangChain 中有一些集成和工具可以帮助这个过程,其中一些我们将在本章后面遇到。此外,LangChain 提供了一个名为 FakeEmbeddings
的类,可以用于在不实际调用嵌入提供者的情况下测试您的管道。在本章的上下文中,我们将使用它们来检索相关信息(语义搜索)。但是,我们仍然需要讨论如何将这些嵌入集成到应用程序和更广泛的系统中,这就是向量存储发挥作用的地方。
生成式 AI 与 LangCHain(一)(4)https://developer.aliyun.com/article/1511584