虽然关于检索增强生成(Retrieval Augmented Generation,简称RAG)的介绍已经相当广泛,特别是在其应用于基于聊天的长短期记忆模型(LLM)方面,但在本文中,我们希望从不同的角度来审视它,并分析它作为强大的操作工具的能力。我们还将提供一个实用的动手示例,以便获得与基于RAG的应用相关的实践经验。
回顾一下推理的概念
推理(Inference)是将数据转化为预测的过程。这个机器学习生命周期的组成部分通常由管理预处理和后处理任务的数据管道所护理。让我们评估一个实际的例子,考虑下图所示的音乐流媒体服务的推荐系统。当用户访问流媒体平台时,一个智能编排的前10首歌曲列表会在应用程序界面中展现。负责这个列表的推荐系统依赖于一个训练有素的模型和强健的数据管道,以确保高质量的结果。
在我们的图表中,用黄色框表示的预处理阶段对于确保模型预测与用户独特口味紧密对齐至关重要。从用户播放的最后250首歌曲开始,管道处理数据并在将其传递给训练有素的模型进行推理之前生成一组上下文特征。推理步骤预测用户可能喜欢的内容,并产生一个输出,该输出传递到后处理阶段(用橙色表示)。在这最后一个步骤中,模型的顶级推荐通过额外的元数据(专辑封面、歌曲标题、艺术家姓名及其排名)得到丰富。然后这些信息显示在用户的界面上以供使用。
在上述的工作流程中,很明显推理步骤在应用程序拓扑结构中与用户的接近程度。与数据收集和模型优化等其他人工智能生命周期组件不同,它们往往在后台操作,推理引擎则在前线,与用户界面(UI)和用户体验(UX)紧密互动。
我们可以使用上图来说明人工智能生命周期各个组件与用户的接近程度。虽然很多组件如数据收集和标注处于“幕后”,但是推理引擎作为人工智能内部过程与最终用户接触的重要桥梁。它不仅仅是后端机制,它是用户与应用程序接触的核心部分。
鉴于推理引擎在塑造用户体验中的关键作用,至关重要的是推理引擎,包括推理过程及其周边组件,如预处理/后处理、API管理和计算管理,必须无缝运行。为了推理引擎的质量建立边界条件,我提出了“推理质量( IQ)三角形”,如下图所示。这个定性图表强调了在提升推理负载性能时需要关注的三个关键方面:
随着我们深入,我们将参考IQ三角,深入探讨这三个组成部分:延迟(latency)、保真度(fidelity)以及规模(scale)是如何与RAG工作负载紧密对齐的。
简介:检索增强型生成
检索增强型生成,也被称为RAG,是一种最初由Piktus等人在2021年的《用于知识密集型NLP任务的检索增强生成》中介绍的技术,并且自那以后被适配于各种框架和应用中。RAG属于上下文学习技术的范畴,这类技术专注于为预训练模型提供额外知识,以增强其响应质量。
RAG的标志特征在于,它能智能地从相关数据源检索附加信息,通常使用诸如相似性搜索的算法,从向量数据库中进行。检索到的数据与用户的查询结合在一起,丰富了提供给生成模型的输入。标准的RAG工作流程在下图中展示。
为了理解RAG真正的价值,让我们考虑一个实际的场景:一家大公司的财务分析师正努力构建季度收益报告。以传统方式执行这项任务会耗费大量时间。基于LLM的应用程序提供了显著的效率提升,但有一个问题——需要更新的、专有的信息,这在训练基础开源模型的时候是无法获取的。这个问题可以通过微调部分解决,但由于商业操作的快速节奏,这个过程需要不断的微调才能保持模型的时效性。
RAG通过检索相关的实时数据应对这些挑战,允许模型动态地用最新信息进行更新。这对底层数据库的质量提出了挑战,但至少数据管理比LLM的错觉和神经网络的外推更为经验性和可预测。作为附加福利,这种方法保护了组织数据基础设施中的敏感数据。
许多应用AI工程师同意,应该向专注于定期微调和健壮RAG管线的混合策略转变。在实践中,这种策略在与领域特定任务的对齐上得到了改进,并在数据环境迅速演变的应用中提高了模型的相关性。
支持高质量推理引擎的操作性RAG系统
现在我们已经对RAG及其在基于LLM的应用中的角色有了基本的了解,我们将专注于实施这些系统的实用和操作方面。
正如承诺的,我们将重温IQ三角形,它强调了高质量操作推理引擎的三个关键方面。我们将分析使用下面展示的技术栈,一个由RAG管线和在CPU上运行的高度优化模型组成的推理引擎,来解决这三个方面(可伸缩性、延迟和保真度)的机会。
RAG的架构优势
基于RAG的应用程序带来了显著的架构优势。从可伸缩性的角度来看,管道中所有以数据为中心的组件都集中在单个(或少数几个)向量数据库,这使得RAG的即时数据优势随着用户请求的增减能够很好地扩展。这种统一的方法可以显著提高对特定领域任务响应的准确性,同时大幅简化数据治理。
优化模型:效率与性能
通过模型压缩和参数高效微调技术,模型可以实现更小的计算和环境足迹。虽然微调可以帮助模型针对特定任务进行定制,提高其预测准确性(忠诚度),但压缩方法如量化可以减小模型体积,显著提高推理延迟。这些精简且调优的模型更容易在数据中心部署,并且使得在边缘计算环境中的人工智能应用成为可能,为各种创新用例打开了大门。
支持RAG的CPU
在涉及复杂逻辑的工作流程,如RAG,CPU因其普遍性和成本效益突显。这使得规模扩展成为可能,因为几乎任何组织都可以在云端获取到企业级CPU,不同于专业加速器,后者更难以获得。
现代CPU还配备了底层优化,以英特尔第四代至强处理器中的高级矩阵扩展为例,这些优化改进了深度学习训练和推理阶段的内存管理和矩阵运算。它们对较低精度数据类型(如bf16和int8)的支持,使它们非常适合在推理期间实现低延迟。
此外,CPU与RAG管道的多个组件,包括向量数据库和智能搜索(例如相似性搜索)的兼容性,简化了基础设施管理,使得规模化部署更加简单高效。
要跟随以下的动手实例,请创建一个免费账户在英特尔开发者云并导航至“培训和研讨会”页面。在Gen AI Essentials部分下,选择Retrieval Augmented Generation (RAG) with LangChain选项。按照网页上的指示启动一个JupyterLab窗口并自动加载带有所有示例代码的笔记本。
笔记本包括了详细的文档字符串和代码描述。本文将讨论高层次的机制,并为特定功能提供上下文。
设置依赖关系
我们开始通过安装所有所需的包到基本环境中。欢迎你创建自己的conda环境,但这是一个快速且简便的开始方式。
import sys
import 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):
"""
Downloads the specified dataset and saves it to the data path.
Parameters
----------
dataset : str
The name of the dataset to be downloaded.
"""
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"}
# Download the dialogue from hugging face
dataset = load_dataset(f"{datasets[dataset]}")
# Convert the dataset to a pandas dataframe
dialogues = dataset['train']
df = pd.DataFrame(dialogues, columns=['id', 'description', 'dialogue'])
# Print the first 5 rows of the dataframe
df.head()
# only keep the dialogue column
dialog_df = df['dialogue']
# save the data to txt file
dialog_df.to_csv(self.data_path, sep=' ', index=False)
else:
print('data already exists in path.')
在上面的代码片段中,你可以从4种不同的合成数据集中选择:
配置模型
LangChain API中的GPT4ALL扩展负责将模型加载到内存中并设置各种参数,例如:
def load_model(self, n_threads, max_tokens, repeat_penalty, n_batch, top_k, temp):
"""
Loads the model with specified parameters for parallel processing.
Parameters
----------
n_threads : int
The number of threads for parallel processing.
max_tokens : int
The maximum number of tokens for model prediction.
repeat_penalty : float
The penalty for repeated tokens in generation.
n_batch : int
The number of batches for processing.
top_k : int
The number of top k tokens to be considered in sampling.
"""
# Callbacks support token-wise streaming
callbacks = [StreamingStdOutCallbackHandler()]
# Verbose is required to pass to the callback manager
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(Rapid Annotation Generation)设置中不可或缺的一部分,在这里我们高效地存储和管理我们的数据。以下是我们构建它的方式:
def build_vectordb(self, chunk_size, overlap):
"""
Builds a vector database from the dataset for retrieval purposes.
Parameters
----------
chunk_size : int
The size of text chunks for vectorization.
overlap : int
The overlap size between chunks.
"""
loader = TextLoader(self.data_path)
# Text Splitter
text_splitter = RecursiveCharacterTextSplitter(chunk_size=chunk_size, chunk_overlap=overlap)
# Embed the document and store into chroma DB
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):
"""
Retrieves relevant document snippets based on the user's query.
Parameters
----------
user_input : str
The user's input or query.
top_k : int, optional
The number of top results to return, by default 1.
context_verbosity : bool, optional
If True, additional context information is printed, by default False.
rag_off : bool, optional
If True, disables the retrieval-augmented generation, by default False.
"""
self.user_input = user_input
self.context_verbosity = context_verbosity
# perform a similarity search and retrieve the context from our documents
results = self.index.vectorstore.similarity_search(self.user_input, k=top_k)
# join all context information into one string
context = "\n".join([document.page_content for document in results])
if self.context_verbosity:
print(f"Retrieving information related to your question...")
print(f"Found this content which is most similar to your question: {context}")
if rag_off:
template = """Question: {question}
Answer: This is the response: """
self.prompt = PromptTemplate(template=template, input_variables=["question"])
else:
template = """ Don't just repeat the following context, use it in combination with your knowledge to improve your answer to the question:{context}
Question: {question}
"""
self.prompt = PromptTemplate(template=template, input_variables=["context", "question"]).partial(context=context)
LangChain LLMChain工具会根据用户提交的查询和配置好的模板执行推理。结果将返回给用户。
def inference(self):def inference(self):
"""
Performs inference to generate a response based on the user's query.
Returns
-------
str
The generated response.
"""
if self.context_verbosity:
print(f"Your Query: {self.prompt}")
llm_chain = LLMChain(prompt=self.prompt, llm=self.llm)
print("Processing the information with gpt4all...\n")
response = llm_chain.run(self.user_input)
return response
互动实验
为了帮助你快速开始,笔记本包含了集成的ipywidget组件。你必须运行笔记本中的所有单元格以启用这些组件。
总结
没有人愿意与反应迟缓、不稳定且提供错误信息的聊天机器人交互。为了避免开发出使用户体验极差的系统,有丰富的技术栈组合可供开发者选择。在本文中,我们从实现规模化、保真度和低延迟优势的技术栈角度,解释了推理引擎质量对用户体验的重要性。RAG、CPU与模型优化技术的结合,全面覆盖了智能三角形,与基于LLM的运营AI聊天应用的需求高度一致。