Press "Enter" to skip to content

基于CPU的LangChain的检索增强生成(RAG)推理引擎

夜咖啡制作- 作者所有

使用RAG探索规模、保真度和延迟在AI应用中

虽然Retrieval Augmented Generation(RAG)已经被广泛涵盖,特别是在其应用于聊天型LLMs中,但在本文中,我们将从不同的角度来看待它,并分析它作为一个功能强大的操作工具的能力。我们还将提供一个有用的实际例子,以获得使用基于RAG的应用程序的实践经验。在本文结束时,您将对RAG有一个独特的观点-了解它在生产规模的LLM部署中的可扩展推理中的作用和潜力。

但首先,让我们复习一下推理的理解

推理是将数据转化为预测的过程。这个ML生命周期的组成部分通常由管理预处理和后处理任务的数据流管理。让我们评估一个实际的例子,考虑一个音乐流媒体服务的推荐系统,如图1所示。当用户访问流媒体平台时,应用程序界面中会呈现出一个智能策划的前10首歌曲列表。为此列表负责的推荐系统依赖于训练好的模型和强大的数据流管理,以确保高质量的结果。

图1. 简要说明一个支持“前10首推荐歌曲列表”功能的推荐系统的简单图示-图像作者

我们图中所示的黄色方框表示的预处理阶段对于确保模型的预测与用户的独特口味密切相关。从用户播放的最后250首歌曲开始,数据流处理数据并在将其传递给训练好的模型进行推理之前产生一组上下文特征。推理步骤预测出这位用户可能喜欢的东西,并产生一个传递到后处理阶段(橙色表示)的输出。在最后的步骤中,模型的前几个推荐与其他元数据(专辑封面、歌曲标题、艺术家名称以及排名)结合。然后在用户的界面上显示这些信息供用户使用。

在上面描述的工作流程中,推理步骤与应用程序拓扑中的用户非常接近。与其他AI生命周期组件如数据收集和模型优化不同,它们通常在后台运行,推理引擎则在前线,与用户界面(UI)和用户体验(UX)密切互动。

图2. AI系统的各个组件与用户体验的距离的插图-图像作者

我们可以使用上面的图示(图2)来说明AI生命周期的各个组件与用户的接近程度。虽然许多组件(例如数据收集和注释)是“幕后”的,但推理引擎作为将AI的内部过程与终端用户接触的关键桥梁而存在。它不仅仅是另一个后端机制,它是用户与应用程序的实际体验的核心部分。

鉴于推理引擎在塑造用户体验方面的关键作用,推理引擎(包括推理过程及其周边组件,如预处理/后处理、API管理和计算管理)的无故障运行是至关重要的。为了建立推理引擎质量的边界条件,我引入了“推理质量(IQ)三角形”,如图3所示。这个质量图形突出了在提高推理工作质量时需要关注的三个关键方面:

  • 延迟:如果推理引擎花费更少的时间产生响应,它减少了应用程序的开销,从而提供更好的用户体验。
  • 保真度:推理需要提供用户可以信任和有信心的答案。这包括但不限于确保响应的高准确性和减少幻觉。
  • 可扩展性:随着AI系统负载的波动,确保基础设施的可扩展性是优化成本和启用计算资源正确配置的关键。
图3. 高质量推理引擎三要素的简化模型。强调运营卓越,在保持低延迟和返回高保真度见解的同时,AI系统需要进行扩展。— 图片由作者提供

随着我们在本文中的进展,我们将参考 IQ 三角形来深入探讨这三个组成部分——延迟、保真度和规模——如何与 RAG 工作负载完美对齐。

简介:检索增强生成

检索增强生成(Retrieval Augmented Generation)简称 RAG,最初由 Piktus 等人在《Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks》(2021年)中提出,随后在各种框架和应用程序中进行了适应。RAG 属于上下文学习技术的范畴,着重于为预训练模型提供额外的知识,以增强其响应的质量。

RAG 的标志在于智能检索相关数据源的附加信息,通常使用相似度搜索等算法来从矢量数据库中检索数据。检索到的数据与用户的查询结合,丰富了提供给生成模型的输入。图4 展示了标准的 RAG 工作流程。

图4. 简单的 RAG 工作流程图 — 图片由作者提供

为了真正理解 RAG 的价值,让我们考虑一个实际情境:一家主要公司的财务分析员(图5)正在努力编制季度收益报告。以传统方式执行此任务将是一项耗时的工作。基于 LLM 的应用提供了显著的效率改进,但有个问题——需要最新的专有信息,而这些信息在训练基础开源模型时无法获取。通过微调可以部分解决这个问题,但业务运营速度快意味着这个过程需要不断进行微调以保持模型的时效性。

图5. 改编版的图6。该改编版以财务分析员使用 RAG 的情景为例,展示了使用专有数据构建 RAG 系统的工作流程的有效性。— 图片由作者提供

RAG 通过检索相关的实时数据来解决这些挑战,从而使模型可以动态地更新最新信息。这对底层数据库的质量施加了压力,但至少数据管理比 LLM 的错觉和神经网络外推更有经验和可预测性。作为额外的好处,这种方法可以保护组织的敏感数据。

许多应用 AI 工程师都认为应该转向混合策略,重点放在定期微调和强大的 RAG 流程上。在实践中,这种策略能够更好地与领域特定任务相匹配,并提高在数据环境快速演变的应用中模型的相关性。

在实际例子中,您可以切换 RAG 开/关来观察预训练模型的响应质量在有或无智能检索机制提供上下文的情况下的影响。请参见图10。

支持高质量推理引擎的运行中 RAG 系统

现在我们对 RAG 及其在基于 LLM 的应用中的作用有了基础的理解,我们将重点关注实施这些系统的实际和运营方面。

如约而至,让我们回顾一下IQ三角形(图3),它凸显了高质量操作推理引擎的三个关键方面。我们将使用下面所示的堆栈(图6)来分析解决这三个方面 – 可扩展性、延迟和保真度 – 的机会。该堆栈侧重于由RAG流水线组成的推理引擎和在CPU上运行的高度优化模型。

图6. 提议的推理引擎堆栈 - 图片由我的作者提供

RAG的架构优势

RAG-based应用带来了显著的架构优势。从可扩展性的角度来看,流水线的所有数据中心组件都汇聚到单个(或少数个)向量数据库(图7),这使得RAG的新鲜数据优势能够随着用户请求数量的增加/减少而良好扩展。这种统一的方法可以显著提高对特定领域任务的响应的保真度,同时极大地简化数据治理。

图7. 简单的数据流图显示RAG-based AI系统中的数据流动 - 图片由作者提供

优化模型:效率和性能

通过模型压缩和参数高效的微调技术,模型可以在计算和环境上实现更小的占用。微调可以帮助定制模型以适应特定任务,提高其预测精度(保真度),而量化等压缩方法则可以缩小模型大小,显著提高推理的延迟。这些精简且调整良好的模型更容易部署在数据中心,并在边缘开启各种创新用例的应用。

图8 - 图片由作者提供

支持RAG的CPU

对于涉及复杂逻辑的工作流程,如RAG,CPU因其普遍性和经济高效性而脱颖而出。这使得规模改进成为可能,因为几乎任何组织都可以在云端获得企业级CPU,而专用加速器却更难获得。

现代CPU还配备了低级别优化 – 例如,Intel第四代Xeon处理器中的Intel高级矩阵扩展,可以改善深度学习训练和推理阶段的内存管理和矩阵操作。它们对低精度数据类型(例如bf16和int8)的支持使它们非常适合在推理过程中实现低延迟。

此外,CPU与RAG流水线的多个组件(图9),包括向量数据库和智能搜索(例如相似性搜索),的兼容性简化了基础设施管理,使规模化部署更加简单高效。

图9. 图7的改编。展示了CPU支持RAG系统的各个部分的能力 - 图片由作者提供

在继续之前,我必须说明我与英特尔的关联以及下面使用的产品。作为英特尔的高级AI工程师,以下实际示例在英特尔开发者云(IDC)上运行。我们将使用IDC作为免费便捷的方式来访问计算资源,以获得与本文描述的概念相关的实际经验。

实践示例:在Intel开发者云上使用LangChain实现RAG

要按照下面的实践示例进行操作,请在Intel开发者云上创建一个免费账号,并导航到“培训和研讨会”页面。在通用AI基础知识部分,选择RAG with LangChain选项。按照网页上的说明启动一个JupyterLab窗口,并自动加载带有所有示例代码的笔记本。

笔记本包含详细的文档字符串和代码描述。本文将讨论高层机制,并为特定函数提供上下文。

设置依赖关系

我们首先在基础环境中安装所有所需的软件包。您可以创建自己的conda环境,但这是一种快速简便的方法。

import sysimport os!{sys.executable} -m pip install langchain==0.0.335 --no-warn-script-location > /dev/null!{sys.executable} -m pip install pygpt4all==1.1.0 --no-warn-script-location > /dev/null!{sys.executable} -m pip install gpt4all==1.0.12 --no-warn-script-location > /dev/null!{sys.executable} -m pip install transformers==4.35.1 --no-warn-script-location > /dev/null!{sys.executable} -m pip install datasets==2.14.6 --no-warn-script-location > /dev/null!{sys.executable} -m pip install tiktoken==0.4.0 --no-warn-script-location > /dev/null!{sys.executable} -m pip install chromadb==0.4.15 --no-warn-script-location > /dev/null!{sys.executable} -m pip install sentence_transformers==2.2.2 --no-warn-script-location > /dev/null

这些命令将在您的基础环境中安装所有必要的软件包。

数据和模型

我们将使用来自GPT4All项目的Falcon 7B的量化版本(gpt4all-falcon-q4_0)。您可以在“模型浏览器”部分的GPT4ALL页面上了解更多有关该模型的信息。该模型已存储在磁盘上,以简化模型访问过程。

以下逻辑从一个名为FunDialogues的Hugging Face项目下载可用的数据集。所选数据将通过嵌入模型,并在后续步骤中放入我们的向量数据库。

def download_dataset(self, dataset):        """        下载指定的数据集并将其保存到数据路径中。        参数        ----------        dataset : str            要下载的数据集的名称。        """        self.data_path = dataset + '_dialogues.txt'        if not os.path.isfile(self.data_path):            datasets = {"robot maintenance": "FunDialogues/customer-service-robot-support",                         "basketball coach": "FunDialogues/sports-basketball-coach",                         "physics professor": "FunDialogues/academia-physics-office-hours",                        "grocery cashier" : "FunDialogues/customer-service-grocery-cashier"}                        # 从hugging face下载对话            dataset = load_dataset(f"{datasets[dataset]}")            # 将数据集转换为pandas dataframe            dialogues = dataset['train']            df = pd.DataFrame(dialogues, columns=['id', 'description', 'dialogue'])            # 打印数据框的前5行            df.head()            # 仅保留对话列            dialog_df = df['dialogue']                        # 将数据保存到txt文件            dialog_df.to_csv(self.data_path, sep=' ', index=False)        else:            print('数据已存在于路径中。')

在上面的代码片段中,您可以从4个不同的合成数据集中进行选择:

  • 机器人维护:技术员和客户支持代理人在排除机械臂故障时进行的对话。
  • 篮球教练:篮球教练和球员在比赛期间的对话。
  • 物理教授:学生和物理教授在办公时间的对话。
  • 杂货店收银员:杂货店收银员与顾客之间的对话

配置模型

LangChain API中的GPT4ALL扩展负责将模型加载到内存中并建立各种参数,例如:

  • model_path: 此行指定预训练模型的文件路径。
  • n_threads: 设置要使用的线程数,可能会影响并行处理或推理速度。这对于多核系统尤其重要。
  • max_tokens: 限制输入或输出序列的标记(单词或子词)数量,确保模型输入或输出的数据长度不超过此限制。
  • repeat_penalty: 此参数可能会对模型输出中的重复内容进行惩罚。大于1.0的值可以防止模型生成重复序列。
  • n_batch: 指定用于处理数据的批处理大小。这可以提高处理速度和内存使用率。
  • top_k: 定义模型生成过程中的“top-k”采样策略。在生成文本时,模型将只考虑最有可能的前k个标记。
def load_model(self, n_threads, max_tokens, repeat_penalty, n_batch, top_k, temp):        """        使用指定的参数加载模型进行并行处理。        参数        ----------        n_threads : int            并行处理的线程数。        max_tokens : int            模型预测的最大标记数。        repeat_penalty : float            生成中重复标记的惩罚。        n_batch : int            处理的批次数。        top_k : int            采样中考虑的前k个标记数。        """        # 回调支持逐标记流式处理        callbacks = [StreamingStdOutCallbackHandler()]        # 需要将verbose传递给回调管理器        self.llm = GPT4All(model=self.model_path, callbacks=callbacks, verbose=False,                           n_threads=n_threads, n_predict=max_tokens, repeat_penalty=repeat_penalty,                            n_batch=n_batch, top_k=top_k, temp=temp)

使用ChromaDB构建向量数据库

Chroma向量数据库是我们RAG设置的重要部分,用于有效存储和管理数据。以下是构建过程:

def build_vectordb(self, chunk_size, overlap):        """        从数据集中构建向量数据库以便进行检索。        参数        ----------        chunk_size : int            用于向量化的文本块大小。        overlap : int            文本块之间的重叠大小。        """        loader = TextLoader(self.data_path)        # 文本分割器        text_splitter = RecursiveCharacterTextSplitter(chunk_size=chunk_size, chunk_overlap=overlap)        # 嵌入文档并存储到chroma数据库        self.index = VectorstoreIndexCreator(embedding= HuggingFaceEmbeddings(), text_splitter=text_splitter).from_loaders([loader])

执行检索机制

在收到用户查询后,我们使用相似性搜索在向量数据库中搜索相似的数据。一旦找到k个匹配结果,我们将检索并将它们用于将上下文添加到用户的查询中。我们使用PromptTemplate函数构建一个模板,并将用户的查询与检索到的上下文一起嵌入其中。模板填充完成后,我们继续进行推理组件。

def retrieval_mechanism(self, user_input, top_k=1, context_verbosity = False, rag_off= False):        """        根据用户的查询检索相关文档片段。        参数        ----------        user_input : str            用户的输入或查询。        top_k : int, optional            要返回的前k个结果数,默认为1。        context_verbosity : bool, optional            如果为True,将打印附加的上下文信息,默认为False。        rag_off : bool, optional            如果为True,禁用检索增强生成(RAG),默认为False。        """        self.user_input = user_input        self.context_verbosity = context_verbosity                        # 执行相似性搜索并从文档中检索上下文        results = self.index.vectorstore.similarity_search(self.user_input, k=top_k)        # 将所有上下文信息合并为一个字符串        context = "\n".join([document.page_content for document in results])        if self.context_verbosity:            print(f"检索与您的问题相关的信息...")            print(f"找到与您的问题最相似的内容:{context}")        if rag_off:            template = """问题:{question}            回答:这是回答:"""            self.prompt = PromptTemplate(template=template, input_variables=["question"])        else:                 template = """不要只是重复以下上下文,与您的知识结合使用以改进对问题的回答:{context}            问题:{question}            """            self.prompt = PromptTemplate(template=template, input_variables=["context", "question"]).partial(context=context)

使用LangChain LLMChain 实用程序基于用户传递的查询和配置的模板执行推理。结果将返回给用户。

 def inference(self):        """        根据用户的查询执行推理以生成响应。        返回        -------        str            生成的响应。        """        if self.context_verbosity:            print(f"您的查询: {self.prompt}")                    llm_chain = LLMChain(prompt=self.prompt, llm=self.llm)        print("使用gpt4all处理信息...\n")        response = llm_chain.run(self.user_input)        return  response  

交互实验

为了帮助您快速入门,本文档包含了集成的ipywidget组件。您必须运行笔记本中的所有单元格才能启用这些组件。我们鼓励您调整参数并评估对系统响应的延迟和准确性的影响。请记住,这只是一个起点和RAG能力的基本演示。

图10. 在这个例子中,我们可以快速感受到RAG的强大,清晰地看到RAG提供的额外上下文的好处,这有助于回答用户的问题—— “我的机器人无法启动,你能帮我吗?” RAG启用的输出提供了有效的建议,而没有RAG的原始模型只是提供了一个非常无用的礼貌询问。</figcaption></figure><h2 id=总结和讨论

没有人愿意与反应缓慢、不稳定且提供虚假信息的聊天机器人互动。有大量的技术堆栈组合可帮助开发人员避免构建会产生糟糕用户体验的系统。在本文中,我们从启用规模、准确性和延迟优势的堆栈角度解释了推理引擎质量对用户体验的重要性。RAG、CPU和模型优化技术的组合检查了IQ三角的所有角落(图3),与操作型基于LLM的AI聊天应用的需求相吻合。

尝试一些有趣的事情包括:

  • 编辑retrieval_mechanism方法中的提示模板,与检索到的上下文相结合,以得到更好的提示。
  • 调整各种模型和RAG特定的参数,评估对推理延迟和响应质量的影响。
  • 添加对您领域有意义的新数据集,并测试使用RAG构建AI聊天应用的可行性。
  • 这个例子的模型(gpt4all-falcon-q4_0)没有针对Xeon处理器进行优化。探索使用针对CPU平台优化的模型,并评估推理延迟的好处。

感谢阅读!不要忘记关注 我的个人资料获取更多类似的文章

Leave a Reply

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