from langchain.document_loaders import WebBaseLoader
from langchain.indexes import VectorstoreIndexCreator
loader = WebBaseLoader("http://www.paulgraham.com/greatwork.html")
index = VectorstoreIndexCreator().from_loaders([loader])
index.query("What should I work on?")
生成的结果:The work you choose should ideally have three qualities: it should be something you have a natural aptitude for, something you have a deep interest in…
第一次运行这个感觉就像纯粹的魔法。这到底是怎么回事?答案是一个称为检索增强生成或简称RAG的过程。这是一个非常简单的概念,尽管在其实现的细节上也有令人难以置信的深度。
这篇文章将提供RAG的高级概述。我们将从正在发生的事情的大局工作流开始,然后放大所有单独的部分。最后,你应该对这三行神奇的代码是如何工作的,以及创建这些问答机器人所涉及的所有原则有了深刻的理解。
如果你是一个试图构建这样的机器人的开发人员,你将了解你可以调整哪些内容以及如何调整它们。如果你是一个希望在你的数据集上使用人工智能工具的非开发人员,你将获得有助于你充分利用它们的知识。如果你只是一个好奇的头脑,你有望了解一些颠覆我们生活的技术。让我们开始吧。
什么是检索增强生成?
检索增强生成是将用户输入到大型语言模型(LLM)(如ChatGPT)中的信息与你从其他地方检索到的附加信息相补充的过程。LLM可以使用这些信息来增强它生成的响应。
下图显示了它在实践中是如何工作的:
它以用户的问题开始。例如“我如何做<某事>?”
首先发生的是检索步骤。这是一个过程,它接受用户的问题,并从可能回答它的知识库中搜索最相关的内容。检索步骤是RAG中最重要、最复杂的部分。但是现在,想象一个黑匣子,它知道如何提取与用户查询相关的最佳相关信息块。
我们就不能给LLM整个知识库吗?
你可能想知道为什么我们要费心检索,而不仅仅是将整个知识库发送到LLM。一个原因是模型一次接收的信息是有限的。第二个原因是成本——发送大量文本变得非常昂贵。最后,有证据表明,发送少量相关信息会带来更好的答案。
一旦我们从知识库中获得相关信息,我们就把它和用户的问题一起发送到大型语言模型(LLM)。LLM——最常见的ChatGPT——然后“读取”提供的信息并回答问题。这是增强生成步骤。很简单,对吧?
逆向工程:给LLM额外的知识来回答问题
我们将从最后一步开始:答案生成。也就是说,假设我们已经从知识库中提取了我们认为答案的相关信息。我们如何使用它来生成答案?
这个过程可能感觉像魔法,但在幕后它只是一个语言模型。我们将以ChatGPT为例。这一切都归结为提示和消息。
使用系统提示给出LLM自定义说明
第一个组件是系统提示,系统提示给语言模型以整体指导,对于ChatGPT,系统提示类似于“You are a helpful assistant.”。
在这种情况下,我们希望它做一些更具体的事情。而且,由于它是一个语言模型,我们可以告诉它我们想要它做什么。下面是一个示例简短的系统提示,它为LLM提供了更详细的说明:
You are a Knowledge Bot. You will be given the extracted parts of a knowledge base (labeled with DOCUMENT) and a question. Answer the question using information from the knowledge base.
我们基本上是在说,“嘿,人工智能,我们会给你一些东西来阅读。阅读它,然后回答我们的问题,ok?谢谢。”而且,因为人工智能非常擅长遵循我们的指示,所以就是这么简单。
为LLM提供专业知识来源
接下来我们需要给人工智能它的阅读材料。再说一遍——最新的人工智能真的很擅长解决问题。但是,我们可以通过一些结构和格式来帮助它。
以下是可用于将文档传递给LLM的示例格式:
------------ DOCUMENT 1 -------------
This document describes the blah blah blah...
------------ DOCUMENT 2 -------------
This document is another example of using x, y and z...
------------ DOCUMENT 3 -------------
[more documents here...]
这个格式是必须的吗?可能不是,但是尽可能明确地表达是件好事。你也可以使用机器可读的格式,比如JSON或YAML。但是,在更高级的用例中,一些一致的格式变得很重要,例如,如果你想让LLM引用它的来源。
一旦我们格式化了文档,我们就将其作为普通聊天消息发送给LLM。记住,在系统提示中,我们告诉它我们要给它一些文件,这就是我们在这里所做的一切。
把一切放在一起然后问问题
一旦我们有了系统提示和“文档”消息,我们只需将用户的问题与它们一起发送给LLM。以下是使用OpenAI的API实现的调用逻辑:
openai_response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[
{
"role": "system",
"content": get_system_prompt(), # the system prompt as per above
},
{
"role": "system",
"content": get_sources_prompt(), # the formatted documents as per above
},
{
"role": "user",
"content": user_question, # the question we want to answer
},
],
)
就是这样!一个自定义系统提示,两条消息,你有特定于上下文的答案!
这是一个简单的用例,可以扩展和改进。我们还没有做的一件事是告诉人工智能,如果它在源代码中找不到答案,该怎么做。我们可以将这些指令添加到系统提示中——通常要么告诉它拒绝回答,要么使用它的常识性知识,这取决于你的机器人想要的行为。你也可以让LLM引用它用来回答问题的特定来源。我们将在以后的文章中讨论这些策略,但现在,这是答案生成的基础。
简单的部分已经过去了,是时候回到我们跳过的黑匣子了…
检索步骤:从知识库中获取正确的信息
上面我们假设我们有正确的知识片段可以发送到LLM。但是我们实际上如何从用户的问题中获得这些呢?这是检索步骤,它是任何“与你的数据聊天”系统中基础设施的核心部分。
检索的核心是一种搜索操作——我们希望根据用户的输入查找最相关的信息。就像搜索一样,有两个主要部分:
-
索引:将你的知识库变成可以搜索/查询的内容。
-
查询:根据搜索词提取最相关的知识。
值得注意的是,任何搜索过程都可以用于检索。任何接受用户输入并返回一些结果的东西都可以工作。因此,例如,你可以尝试找到与用户问题匹配的文本并将其发送到LLM,或者你可以在谷歌上搜索问题并将顶部结果发送出去——顺便说一句,这大概是必应聊天机器人的工作方式。
也就是说,今天大多数RAG系统都依赖于一种叫做语义搜索的东西,它使用了人工智能的另一个核心技术:嵌入。那么…嵌入了什么?
什么是嵌入?它们与知识检索有什么关系?
LLM很奇怪。它们最奇怪的一点是没有人真正知道它们是如何理解语言的。嵌入是这个故事的一大部分。
如果你问一个人他是如何理解一个单词,他可能会摸索着说一些模糊的和自我理解的东西,比如“因为我知道它们的意思”。在我们大脑的深处有一个复杂的结构,知道“child”和“kid”基本上是一样的,“红色”和“绿色”都是颜色,“高兴”、“快乐”和“兴高采烈”代表着不同程度的相同情绪。我们无法解释这是如何工作的,我们只是知道它们。
语言模型对语言有类似的复杂理解,只是,因为它们是计算机,所以它不在它们的大脑中,而是由数字组成的。在LLM的世界里,任何人类语言都可以用数字向量(列表)来表示。数字向量就是嵌入。
A critical piece of LLM technology is a _translator_that goes from human _word-language_to AI number-language. We’ll call this translator an “embedding machine”, though under the hood it’s just an API call. Human language goes in, AI numbers come out.
LLM技术的一个关键部分是一个从人类文本语言到人工智能数字语言的翻译器。我们称这个翻译器为“嵌入模型”,尽管在幕后它只是一个应用编程接口调用。人类语言进去了,人工智能数字出来了。
这些数字意味着什么?没有人知道!它们只对人工智能“有意义”。但是,我们所知道的是,相似的单词最终会有相似的数字集。因为在幕后,人工智能使用这些数字来“阅读”和“说话”。所以这些数字在人工智能语言中融入了某种神奇的理解——即使我们不理解它。嵌入模型是我们的翻译。
现在,因为我们有了这些神奇的人工智能数字,我们可以绘制它们。上面例子的简化图可能看起来像这样——坐标轴只是人类/人工智能语言的一些抽象表示:
一旦我们对它们进行了绘制,我们可以看到在这个假设的语言空间中,两个点彼此越接近,它们就越相似。“你好,你好吗?”和“嘿,最近怎么样?”实际上是在彼此上面。“早上好,”另一个问候,离这些不远。“我喜欢纸杯蛋糕”和其他的完全不同。
自然,你不能在二维图上表示整个人类语言,但理论是一样的。在实践中,嵌入有更多的坐标(OpenAI使用的当前模型为1536个)。但是你仍然可以做基本的数学计算来确定两个嵌入——因此是两个文本——彼此有多接近。
这些嵌入和确定“接近性”是语义搜索背后的核心原则,它为检索步骤提供动力。
使用嵌入找到最恰当的知识
一旦我们了解了嵌入搜索的工作原理,我们就可以构建检索步骤的高级图。
在索引方面,首先我们必须将知识库分解为文本块。这个过程本身就是一个最优化问题,我们接下来会介绍它,但是现在假设我们知道怎么做。
完成后,我们将每个知识片段通过嵌入模型(实际上是一个OpenAI或类似的机器)并返回该文本的嵌入表示。然后我们将片段与嵌入一起保存在向量数据库中,向量数据库是一个针对数字向量进行优化的数据库。
现在我们有一个数据库,其中存储了我们所有的嵌入内容。从概念上讲,你可以将其视为我们整个知识库在我们的“语言”图上的图表:
一旦我们有了这个图,在查询端,我们会做一个类似的过程。首先,我们得到用户输入的嵌入:
然后我们在同一个向量空间中绘制它并找到最近的片段(在本例中为1和2):
神奇的嵌入模型认为这些是与被问到的问题最相关的答案,所以这些是我们拉出来发送给LLM的片段!
实际上,这个“最接近的点是什么”问题是通过查询我们的向量数据库来完成的。所以实际过程看起来更像这样:
查询本身涉及一些半复杂的数学——通常使用称为余弦距离的东西,尽管还有其他计算方法。数学是一个你可以进入的完整空间,但是对于这篇文章的目的来说超出了范围。
index.query(“What should I work on?”)
索引知识库
好了,我们现在了解了如何使用嵌入来查找知识库中最相关的位,将所有内容传递给LLM,并获得我们的增强答案。我们将介绍的最后一步是从知识库创建初始索引。换句话说,这张图片中的“知识拆分机”:
也许令人惊讶的是,索引你的知识库通常是整件事中最难也是最重要的部分。不幸的是,这与其说是科学,不如说是艺术,而且涉及到很多试错。
从大局来看,索引过程可以归结为两个高级步骤。
-
加载:从通常存储的知识库中获取内容。
-
拆分:将知识拆分为片段大小的块,这些块适用于嵌入搜索。
技术细节
从技术上讲,“加载器”和“拆分器”之间的边界有点模糊。你可以想象一个组件同时完成所有工作,或者将加载阶段分解为多个子组件。
也就是说,“加载器”和“拆分器”是在LangChain中完成的,它们在底层概念之上提供了一个有用的抽象。
让我们以我自己的用例为例。我想构建一个聊天机器人来回答关于我的Saas样板产品和SaaS Pegasus的问题。我想添加到知识库的第一件事是找到文档站点——包含了我所需要的所有内容知识。加载器是进入我的文档的基础设施,找出可用的页面,然后下拉每个页面。加载器完成后,它将输出单个_文档_——站点上的每个页面一个。
在加载器内部发生了很多事情!我们需要抓取所有页面,抓取每个页面的内容,然后将超文本标记语言格式化为可用文本。其他东西的加载器——例如PDF或Google Drive——有不同的部分。还有并行化、错误处理等要弄清楚。同样,这是一个几乎无限复杂的主题,但为了写这篇文章,我们将主要使用现有集成的库。所以现在,我们再次假设我们有一个神奇的盒子,一个“知识库”进去,一个单独的“文档”出来。
内置加载器是LangChain最有用的部件之一。它们提供了一长串的内置加载器,可用于从Microsoft Word文档到整个Notion站点的任何内容中提取内容。LangChain的接口与上面描述的完全相同。一个“知识库”进去,一个“文档”列表出来。
从加载程序中出来,我们将有一个与文档站点中的每个页面相对应的文档集合。此外,理想情况下,此时额外的标记已被删除,只保留底层结构和文本。
现在,我们可以将整个网页传递给我们的嵌入模型,并将其生成我们的知识片段。但是,每个页面可能涵盖很多领域!而且,页面中的内容越多,该页面的嵌入就越“不具体”。这意味着我们的“贴近性”搜索算法可能不太有效。
更有可能的是,用户问题的主题与页面中的某段文本相匹配。这就是拆分进入画面的地方。通过拆分,我们将任何单个文档拆分为一定大小、可嵌入的块,更适合搜索。
再一次,拆分文档有一门完整的艺术,包括平均制作多大的片段(太大,它们不能很好地匹配查询,太小,它们没有足够的有用上下文来生成答案),如何拆分东西(通常是标题,如果你有的话),等等。但是——一些合理的默认值足以开始处理和完善你的数据。
在LangChain中,拆分器属于一个更大的类别,叫做“document transformers”。除了提供各种拆分文档的策略,它们还具有删除冗余内容、翻译、添加元数据等工具。我们在这里只关注拆分器,因为它们代表了绝大多数文档转换。
一旦我们有了文档片段,我们将它们保存到向量数据库中,如上所述,我们终于完成了!这是索引知识库的全貌。
在LangChain中,整个索引过程都封装在这两行代码中。首先,我们初始化我们的网站加载器并告诉它我们要使用什么内容:
loader = WebBaseLoader(“http://www.paulgraham.com/greatwork.html”)
然后我们从加载器构建整个索引并将其保存到向量数据库:
index = VectorstoreIndexCreator().from_loaders([loader])
加载、拆分、嵌入和保存都在幕后进行。
回顾整个过程
最后,我们回顾下整个RAG流程。它看起来是这样的:
首先,我们索引知识库。我们获取知识并使用加载器将其转化为单个文档,然后使用拆分器将其转化为一定大小的块或片段。一旦我们有了这些,我们就把它们传递给嵌入模型,嵌入模型将它们转化为可用于语义搜索的向量。我们将这些嵌入和它们的文本片段一起保存在向量数据库中。
接下来是检索。它从问题开始,然后通过同一嵌入模型发送并传递到我们的向量数据库中,以确定最接近的匹配片段,我们将使用这些片段来回答问题。
最后,增强答案生成。我们获取知识片段,将它们与自定义系统提示和我们的问题一起格式化,最后,得到特定于上下文的答案。希望你现在对检索增强生成的工作原理有了基本的了解。
那么,如何系统的去学习大模型LLM?
我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。
作为一名热心肠的互联网老兵,我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。
但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。
所有资料 ⚡️ ,朋友们如果有需要全套 《LLM大模型入门+进阶学习资源包》,扫码获取~
篇幅有限,部分资料如下:
👉LLM大模型学习指南+路线汇总👈
💥大模型入门要点,扫盲必看!
💥既然要系统的学习大模型,那么学习路线是必不可少的,这份路线能帮助你快速梳理知识,形成自己的体系。
路线图很大就不一一展示了 (文末领取)
👉大模型入门实战训练👈
💥光学理论是没用的,要学会跟着一起做,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。
👉国内企业大模型落地应用案例👈
💥两本《中国大模型落地应用案例集》 收录了近两年151个优秀的大模型落地应用案例,这些案例覆盖了金融、医疗、教育、交通、制造等众多领域,无论是对于大模型技术的研究者,还是对于希望了解大模型技术在实际业务中如何应用的业内人士,都具有很高的参考价值。 (文末领取)
👉GitHub海量高星开源项目👈
💥收集整理了海量的开源项目,地址、代码、文档等等全都下载共享给大家一起学习!
👉LLM大模型学习视频👈
💥观看零基础学习书籍和视频,看书籍和视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。 (文末领取)
👉640份大模型行业报告(持续更新)👈
💥包含640份报告的合集,涵盖了AI大模型的理论研究、技术实现、行业应用等多个方面。无论您是科研人员、工程师,还是对AI大模型感兴趣的爱好者,这套报告合集都将为您提供宝贵的信息和启示。
👉获取方式:
这份完整版的大模型 LLM 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费
】
😝有需要的小伙伴,可以Vx扫描下方二维码免费领取🆓