【学习大模型】RAG基础

简介: RAG(Retrieval-Augmented Generation)技术是为了解决大模型中的幻觉问题、实时交互、数据安全和知识动态性挑战。它结合了搜索和大模型的提示功能,使模型能基于检索到的信息生成更准确的回答。RAG通过向量数据库和向量检索,将文本转化为向量表示,然后进行相似度计算和检索,以提供上下文相关的信息。

一 背景

RAG技术全称为检索增强生成技术(Retrieval Augmented Generation),是一种向LLM提供从特定数据源检索的信息的技术。简而言之,RAG结合了搜索和LLM的提示功能,在此基础上,模型根据搜索算法提供的信息,作为上下文来回答问题。这些查询和检索到的上下文会一并被注入到发送给LLM的提示中。RAG技术的出现,主要解决了以下问题:

  1. 大模型的幻觉问题:大模型幻觉指模型输出的“事实性”内容中包含虚假、误导性信息。通过RAG可以解决可解释性、信息溯源、信息验证证等问题,一旦检索的内容和生成的内容建立的关系,可以知道LLM模型根据哪些信息得出的回答。
  2. 与真实世界实时交互问题:RAG可以帮助模型对自身知识进行动态更新,同时,帮助模型在执行指令时,实时补全空白知识。
  3. 数据问题:私有数据安全问题,RAG技术可以将私有数据作为一个外部数据库,让LLM在回答私有数据问题时候,直接从该数据库中提取信息。
  4. 知识的动态性问题:目前LLM存在偏见、幻觉、时效性等问题(由于只是按照概率空间进行输出),RAG的外部检索很好的解决了这一问题,从而保持了模型的时效性和准确性。

二 向量介绍

其中在做大模型RAG相关的应用中,我们经常需要用到向量的概念,包括向量数据库和向量检索。对于没接触过的人可能比较懵。本文介绍下文本向量化的概念,以及向量检索的原理,只是简单介绍,不做深入。

1. 向量介绍

文本向量化是将文本数据转换为数值向量的过程。它的目的是将文本信息表示为机器可以处理和分析的形式。在自然语言处理中,文本通常是由字符串组成的,例如句子、段落或文档。然而,计算机程序很难直接理解和处理文本数据。文本向量化的作用就是将这些文本转换为数值向量,使得计算机可以对其进行数学运算和分析。通过文本向量化,我们可以将文本数据用于各种任务,如文本分类、情感分析、信息检索、机器翻译等。它使得计算机能够处理和理解自然语言,并从中提取有意义的信息。

2. 文本向量生成

通义千问提供了文本向量的模型,是基于LLM底座的多语言文本统一向量模型,面向全球多个主流语种,提供高水准的向量服务,可以将文本数据快速转换为高质量的向量数据。详情可以参考官网介绍:https://help.aliyun.com/zh/dashscope/developer-reference/text-embedding-quick-start

其中我们用的比较多的是text-embedding-v1模型,我们可以通过官网代码进行快速验证调用。

import dashscope
from http import HTTPStatus
def embed_with_str():
    resp = dashscope.TextEmbedding.call(
        model=dashscope.TextEmbedding.Models.text_embedding_v1,
        input='测试')
    if resp.status_code == HTTPStatus.OK:
        print(resp)
    else:
        print(resp)
if __name__ == '__main__':
    embed_with_str()

可以测试一下这个接口,看下这个接口输出的向量长什么样。

可以看到 “测试” 这四个字对应的输出是一个 1536 维的向量,这就是 “测试” 的向量表示,和官网说明的向量维度大小一致。

3.向量相似度计算和向量检索

3.1 向量相似度

向量相似度是一种用于衡量两个向量之间相似程度的度量方法。它通常用于比较不同对象或数据的相似性,例如文本、图像、音频等。常见的向量相似度度量方法包括:

① 欧几里得距离:计算两个向量之间的欧几里得距离,距离越小表示相似度越高。

② 余弦相似度:通过计算两个向量的余弦值来衡量它们的相似性。余弦值接近 1 表示相似度高,接近 0 表示相似度低。

③ 曼哈顿距离:计算两个向量在各个维度上的绝对差值的总和,曼哈顿距离越小表示相似度越高。

Jaccard 相似系数:适用于集合或布尔向量的相似度度量,计算两个集合的交集与并集的比值。

④ 皮尔逊相关系数:衡量两个向量的线性相关程度,相关系数的值在-1 到 1 之间,-1 表示完全负相关,1 表示完全正相关。

选择合适的向量相似度度量方法取决于数据的特点和具体的应用场景。不同的度量方法可能对不同类型的向量数据表现出不同的效果。在实际应用中,通常需要根据数据的分布、特征和需求来选择最适合的相似度度量方法。

3.2 向量检索

向量检索是一种在数据库中查找最相似向量的技术。对于诸如图片、视频、音频等非结构化数据,传统数据库方式无法进行处理。目前,通用的技术是把非结构化数据通过一系列 embedding 模型将它变成向量化表示,然后将它们存储到数据库或者特定格式里。在搜索过程中,通过相同的一个模型把查询项转化成对应的向量,并进行一个近似度的匹配就可以实现对非结构化数据的查询。

3.3 两者差别

在对向量使用过程中,经常需要使用到向量检索和向量相似度计算。

向量检索主要关注在大规模数据集中快速找到与给定向量最相似的向量。它通常涉及建立索引结构、使用高效的搜索算法来查找最接近的向量。向量检索的目的是快速定位相似的向量,而不一定计算出具体的相似度值。

相似度计算则更侧重于定量地衡量两个向量之间的相似程度。它可以使用各种相似度度量方法,如欧几里得距离、余弦相似度等,来计算两个向量的相似性得分。相似度计算给出了一个具体的数值,表示两个向量的相似程度。

简单来说,向量检索是找到最相似的向量,而相似度计算是定量评估相似程度。在实际应用中,常常先进行向量检索,找到候选的相似向量,然后再对这些候选向量进行相似度计算,以确定它们的具体相似程度。

例如,在一个图像检索系统中,向量检索可以帮助快速找到与查询图像最相似的一批候选图像,然后可以对这些候选图像进行进一步的相似度计算,以确定它们与查询图像的相似度得分,并按照得分排序展示给用户。

三 RAG常见架构

常见的RAG应用主要包括以下步骤。

1.索引(Indexing)

加载(Load):首先,我们需要加载我们的数据,这里主要使用 DocumentLoaders 完成。

拆分(Split):文本拆分器将大型文档拆分成较小的块。这对于索引数据和将其传递给模型都很有用,因为大的块更难搜索,而且不适合模型的有限上下文窗口。

存储(Store):我们需要有地方存储和索引我们的拆分后的文档,以便以后可以对其进行搜索。这通常使用 VectorStore 和 Embeddings 模型来完成。

2.检索和生成(Retrieval and generation)

检索(Retrieve):借助词向量的相似度找到与用户提出的问题最匹配的内容。

生成(Generate):直接向语言模型提供增强知识来指导其产出更符合语境的回答。

四 demo学习

1. demo代码

# Load, chunk and index the contents of the blog.
loader = WebBaseLoader(
    web_paths=("https://lilianweng.github.io/posts/2023-06-23-agent/",),
    bs_kwargs=dict(
        parse_only=bs4.SoupStrainer(
            class_=("post-content", "post-title", "post-header")
        )
    ),
)
docs = loader.load()
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
splits = text_splitter.split_documents(docs)
vectorstore = Chroma.from_documents(documents=splits, embedding=OpenAIEmbeddings())
# Retrieve and generate using the relevant snippets of the blog.
retriever = vectorstore.as_retriever()
prompt = hub.pull("rlm/rag-prompt")
llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)
def format_docs(docs):
    return "\n\n".join(doc.page_content for doc in docs)
rag_chain = (
        {"context": retriever | format_docs, "question": RunnablePassthrough()}
        | prompt
        | llm
        | StrOutputParser()
)
print("开始打印prompt")
prompt = rag_chain.get_prompts()[0].pretty_print()
print("结束打印prompt")
response = rag_chain.invoke("What is Task Decomposition?")
print("返回结果:" + response)

通过官网提供的示例代码可以学习RAG的基本流程,这段代码的目的是从指定的网页中提取相关信息,将其分割成块,使用向量表示,并通过检索链与提示和语言模型进行交互,最后打印响应。

运行后结果如下:

接下来我们一步步学习这段代码。

2. indexing:loading

# Only keep post title, headers, and content from the full HTML.
bs4_strainer = bs4.SoupStrainer(class_=("post-title", "post-header", "post-content"))
loader = WebBaseLoader(
    web_paths=("https://lilianweng.github.io/posts/2023-06-23-agent/",),
    bs_kwargs={"parse_only": bs4_strainer},
)
docs = loader.load()

这段代码是在使用 BeautifulSoup4 库来过滤和加载特定类别的 HTML 元素。

  1. bs4.SoupStrainer :这是一个 BeautifulSoup4 的过滤器对象,它根据指定的类名来筛选 HTML 元素。在这里,通过设置 class_=("post-title", "post-header", "post-content"),只选择具有这些类名的元素。
  2. WebBaseLoader :这是一个用于从网络加载数据的加载器。通过设置 web_paths 参数为指定的 URL,加载器将从该 URL 获取数据。
  3. bs_kwargs:这是一个字典,用于传递参数给 BeautifulSoup4 的解析器。在这里,通过设置 parse_only 参数为 bs4_strainer,意味着只解析满足过滤器条件的元素。

最后,通过调用 loader.load(),可以加载并获取符合条件的文档(docs)。

通过打印我们可以看到具体的返回内容

print(len(docs[0].page_content))
print("打印文档的数据:"+docs[0].page_content[:500])

3. Indexing: Split

通过上面抓取的文档可以看到很长,已经远远超过了大模型可以接受的大小。为了解决这样的问题,需要对数据进行拆分。拆分相关代码如下:

text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=1000, chunk_overlap=200, add_start_index=True
)
splits = text_splitter.split_documents(docs)

这段代码是在设置并使用 RecursiveCharacterTextSplitter 来分割文档。

  1. RecursiveCharacterTextSplitter 是一个文本分隔器,它根据指定的参数将文档分割成块。

chunk_size=1000 表示每个块的大小为 1000 个字符。

chunk_overlap=200 表示块之间有 200 个字符的重叠。

add_start_index=True 表示是否为每个块添加起始索引。

  1. split_documents(docs) 方法用于对 docs 进行分割。它会将文档按照设置的参数分割成多个块,并返回这些块。

这样,splits 就会包含按照指定参数分割后的文档块。这些块可以用于进一步的处理或分析。例如,可以将它们逐个处理,或者将它们作为输入提供给其他模型或算法。

len(splits)

通过调试发现,在处理后,一共分成了66个分组。

4. Indexing: Store

现在我们需要为 66 个文本块建立索引,以便在运行时可以对它们进行搜索。最常见的方法是为每个分割文档的内容生成向量,并将这些嵌入插入到向量数据库(或向量存储)中,本次案例中,使用的是Chroma向量数据库,当我们想要在进行搜索时,传入一个文本搜索查询,对其进行向量化,并执行“向量相似度”搜索,以识别与我们的查询嵌入最相似的模块。代码如下:

vectorstore = Chroma.from_documents(documents=all_splits, embedding=OpenAIEmbeddings())

至此我们完成了整个RAG的Indexing阶段。

5. Retrieval

retriever = vectorstore.as_retriever(search_type="similarity", search_kwargs={"k": 6})

这行代码是在创建一个检索器(retriever),它使用了一个向量存储(vectorstore),并指定了搜索类型为"similarity"(相似度搜索),同时还设置了一些搜索关键字参数。其中search_kwargs 字典中设置了 k 参数为 6,这表示在进行相似度搜索时,返回最相似的 6 个结果。这样,通过创建这个检索器,你可以在后续的操作中使用它来进行相似度搜索,并获取与给定查询最相似的 6 个结果。这个检索器将根据你提供的向量数据和搜索参数来执行搜索操作。

6. Generate

其中关于提示词,代码里使用了hub接口里的"rlm/rag-prompt"prompt, hub是langSmith提供的一个提示词库,里面有很多已经写好的提示词可以参考使用。

可以通过官网查看对应的提示词,地址:https://smith.langchain.com/hub/rlm/rag-prompt

或者也可以通过代码pretty_print实时打印具体的提示词。

同时提示词也可以自己定制, 如下图所示:

template = """Use the following pieces of context to answer the question at the end in chinese.
If you don't know the answer, just say that you don't know, don't try to make up an answer.
Use three sentences maximum and keep the answer as concise as possible.
Always say "thanks for asking!" at the end of the answer.
{context}
Question: {question}
Helpful Answer:"""
# 自定义的prompt
custom_rag_prompt = PromptTemplate.from_template(template)
rag_chain = (
        {"context": retriever | format_docs, "question": RunnablePassthrough()}
        | custom_rag_prompt
        | llm
        | StrOutputParser()
)

比如在自定义的prompt加上使用中文打印,可以看到结果已经全是中文输出

五 总结

综上所述,一个简单的基于大模型的RAG应用讲解完了,包含了RAG常见的模块,比如loading,spilt,store, prompt等。其实在实际业务过程中,远比我们的上面讲解的要复杂,尤其是在loading和spilt阶段,如何从企业大量复杂的数据中,清洗出合适的数据,成为衡量RAG应用的成功关键。后续我们会继续深入研究这块内容。

相关实践学习
使用CLup和iSCSI共享盘快速体验PolarDB for PostgtreSQL
在Clup云管控平台中快速体验创建与管理在iSCSI共享盘上的PolarDB for PostgtreSQL。
AnalyticDB PostgreSQL 企业智能数据中台:一站式管理数据服务资产
企业在数据仓库之上可构建丰富的数据服务用以支持数据应用及业务场景;ADB PG推出全新企业智能数据平台,用以帮助用户一站式的管理企业数据服务资产,包括创建, 管理,探索, 监控等; 助力企业在现有平台之上快速构建起数据服务资产体系
相关文章
|
11天前
|
机器学习/深度学习 Python
CatBoost高级教程:深度集成与迁移学习
CatBoost高级教程:深度集成与迁移学习【2月更文挑战第17天】
30 1
|
11天前
|
机器学习/深度学习 算法 Python
LightGBM高级教程:深度集成与迁移学习
LightGBM高级教程:深度集成与迁移学习【2月更文挑战第6天】
117 4
|
11天前
|
机器学习/深度学习 人工智能 自然语言处理
LLM 大模型学习必知必会系列(一):大模型基础知识篇
LLM 大模型学习必知必会系列(一):大模型基础知识篇
LLM 大模型学习必知必会系列(一):大模型基础知识篇
|
11天前
|
自然语言处理 物联网 API
检索增强生成(RAG)实践:基于LlamaIndex和Qwen1.5搭建智能问答系统
检索增强生成(RAG)实践:基于LlamaIndex和Qwen1.5搭建智能问答系统
检索增强生成(RAG)实践:基于LlamaIndex和Qwen1.5搭建智能问答系统
|
11天前
|
机器学习/深度学习 数据采集 算法
构建高效机器学习模型:从数据预处理到模型优化
【5月更文挑战第14天】 在机器学习项目中,模型的性能不仅取决于算法的选择,还受到数据处理和模型配置的影响。本文将探讨如何通过有效的数据预处理和细致的模型调优来提升机器学习模型的效能。我们将讨论数据清洗、特征工程、以及超参数调整等关键步骤,并通过实例展示这些技术如何实现在不同类型的数据集上。目标是为读者提供一套实用的策略,以帮助他们在面对实际问题时能够构建出更加健壮和精确的机器学习模型。
|
11天前
|
存储 人工智能 API
【AIGC】基于检索增强技术(RAG)构建大语言模型(LLM)应用程序
【5月更文挑战第7天】基于检索增强技术(RAG)构建大语言模型(LLM)应用程序实践
220 1
|
11天前
|
机器学习/深度学习 人工智能 自然语言处理
【大模型】比较和对比 LLM 架构
【5月更文挑战第6天】【大模型】比较和对比 LLM 架构
|
11天前
|
自然语言处理 API 开发工具
基于LangChain-Chatchat实现的本地知识库的问答应用-快速上手(检索增强生成(RAG)大模型)
基于LangChain-Chatchat实现的本地知识库的问答应用-快速上手(检索增强生成(RAG)大模型)
基于LangChain-Chatchat实现的本地知识库的问答应用-快速上手(检索增强生成(RAG)大模型)
|
11天前
|
机器学习/深度学习 数据采集 搜索推荐
机器学习中的特征工程:提升模型性能的关键步骤
【5月更文挑战第3天】特征工程是提升机器学习模型性能的关键,涉及从原始数据中提取、选择和创造特征。它能提高模型预测准确率,简化模型复杂度,增强泛化能力。常用技术包括特征选择(Filter、Wrapper、Embedded方法)、特征构造(组合、多项式、文本特征提取)和特征变换(标准化、归一化、离散化)。通过优化特征工程,可找到最佳特征组合,提升模型性能。
|
11天前
|
人工智能 Python
【AI大模型应用开发】【RAG评估】1. 通俗易懂:深度理解RAGAS评估方法的原理与应用
【AI大模型应用开发】【RAG评估】1. 通俗易懂:深度理解RAGAS评估方法的原理与应用
136 0