本文结合“基于ERNIE SDK+LangChain搭建个人知识库”的代码示例,为您讲解RAG的相关概念。
概念
在2020年Facebook AI Research(FAIR)团队发表一篇名为《Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks》的论文。这篇论文首次提出了RAG概念(目前大语言模型领域的一个重要概念),并对该概念进行详细介绍和解释。
此图是FAIR团队的方法概述。结合了一个预先训练的检索器(查询编码器+文档Index),并进行端到端微调。对于查询x,作者使用最大内积搜索(MIPS)查找前K个文档zi对于最终预测y,并将z视为一个潜在变量,并在给定不同文献的seq2seq预测上进行边缘化。
RAG模型结合了语言模型和信息检索技术。具体来说,当模型需要生成文本或者回答问题时,它会先从一个庞大的文档集合中检索出相关的信息,然后利用这些检索到的信息来指导文本的生成,从而提高预测的质量和准确性。其中,“检索”、“利用”、“生成”是RAG的关键部分。
那如何才能更直观地理解这三个部分呢?
举个简单的例子:你正在写一篇关于小狗的文章,但你对小狗的知识有限。这时,你很可能会进行以下操作:
1.检索(Retrieval):
首先,你打开电脑,输入关键词为“小狗”的搜索请求,在互联网上检索了大量的关于小狗的文章、博客和信息。
2.利用(Utilization):
接下来,你会分析这些搜索结果,并提取其中的重要信息,包括狗狗的种类、行为习惯、饲养方式等等。你将这些信息整理成一个知识库,这个知识库就像一本百科全书,里面包含了各种关于小狗的知识点。
3.生成(Generation):
现在,你需要写文章。在文章的开头,通过一个问题引入:“小狗的寿命有多长?”随后,便可以使用之前检索和整理的信息来回答问题,或者生成文章的段落。这一步不仅仅是简单地复制粘贴,而是根据上下文和语法规则生成自然流畅的文本。
其实上述“你”的工作流就是“RAG”的工作流,可以将“你”当作一个RAG模型,即“检索”、“利用”、“生成”。了解了RAG的基本工作流之后,可能会思考:RAG主要在什么场景下使用呢?如果它们在这些场景中进行“检索”,“利用”和“生成”,具体的工作内容又是什么呢?
场景
RAG技术可以在以下一些常见的自然语言处理任务中发挥作用:
1.问答系统(QA Systems): RAG可以用于构建强大的问答系统,能够回答用户提出的各种问题。它能够通过检索大规模文档集合来提供准确的答案,无需针对每个问题进行特定训练。
2.文档生成和自动摘要(Document Generation and Automatic Summarization): RAG可用于自动生成文章段落、文档或自动摘要,基于检索的知识来填充文本,使得生成的内容更具信息价值。
3.智能助手和虚拟代理(Intelligent Assistants and Virtual Agents): RAG可以用于构建智能助手或虚拟代理,结合聊天记录回答用户的问题、提供信息和执行任务,无需进行特定任务微调。
4.信息检索(Information Retrieval): RAG可以改进信息检索系统,使其更准确深刻。用户可以提出更具体的查询,不再局限于关键词匹配。
5.知识图谱填充(Knowledge Graph Population): RAG可以用于填充知识图谱中的实体关系,通过检索文档来识别和添加新的知识点。
优势
以上是RAG一些常见的应用场景。明晰了RAG的应用范围后,可能会产生疑问:为什么这些场景需要使用RAG,而不是进行微调或者通过其他方法来实现呢?接下来,我们进一步了解RAG的优势。以下为RAG的具体优势:
1.外部知识的利用: RAG模型可以服务器托管网有效地利用外部知识库,它可以引用大量的信息,以提供更深入、准确且有价值的答案,这提高了生成文本的可靠性。
2.数据更新及时性: RAG模型具备检索库的更新机制,可以实现知识的即时更新,无需重新训练模型。说明RAG模型可以提供与最新信息相关的回答,高度适配要求及时性的应用。
3.回复具有解释性: 由于RAG模型的答案直接来自检索库,它的回复具有很强的可解释性,减少大模型的幻觉。用户可以核实答案的准确性,从信息来源中获取支持。
4.高度定制能力: RAG模型可以根据特定领域的知识库和prompt进行定制,使其快速具备该领域的能力。说明RAG模型广泛适用于的领域和应用,比如虚拟伴侣、虚拟宠物等应用。
5.安全和隐私管理: RAG模型可以通过限制知识库的权限来实现安全控制,确保敏感信息不被泄露,提高了数据安全性。
6.减少训练成本: RAG模型在数据上具有很强的可拓展性,可以将大量数据直接更新到知识库,以实现模型的知识更新。这一过程的实现不需要重新训练模型,更经济实惠。
对比微调
接下来,通过对比RAG与微调,帮助大家根据具体的业务需求,选择合适的策略:
- 任务特定vs通用性: 微调通常是为特定任务进行优化,而RAG是通用的,可以用于多种任务。微调对于特定任务的完成效果好,但在通用性问题上不够灵活。
- 知识引用vs学习: RAG模型通过引用知识库来生成答案,而微调是通过学习任务特定的数据生成答案。RAG的答案直接来自外部知识,更容易核实。
- 即时性vs训练: RAG模型可以实现即时的知识更新,无需重新训练,在及时性要求高的应用中占优势。微调通常需要重新训练模型,时间成本较高。
- 可解释性vs难以解释性: RAG的答案可解释性强,因为它们来自知识库。微调模型的内部学习可能难以解释。
- 定制vs通用性: RAG可以根据特定领域进行定制,而微调需要为每个任务进行特定微调,需要更多任务特定的数据。
结合上面的比较,我们可以清楚的看到RAG的优势在于通用性、知识引用、即时性和可解释性,而微调在特定任务上可能更适用,但同时需要更多的任务特定数据和训练。选择使用哪种方法,应根据具体的应用需求和任务来决定。
项目示例
那RAG具体怎么实现呢?我们用一个简单的代码示例来举例:基于ERNIE SDK和LangChain搭建个人知识库。
安装ERNIE Bot
!pipinstall--upgradeerniebot
测试embedding
importerniebot
erniebot.api_type="aistudio"
erniebot.access_token=""
response=erniebot.Embedding.create(
model="ernie-text-embedding",
input=[
"我是百度公司开发的人工智能语言模型,我的中文名是文心一言,英文名是ERNIE-Bot,可以协助您完成范围广泛的任务并提供有关各种主题的信息,比如回答问题,提供定义和解释及建议。如果您有任何问题,请随时向我提问。"])
print(response.get_result())
引入Chromadb向量数据库
!pipinstallchromadb
自定义嵌入函数
定义一个自定义的嵌入函数,用于将文本内容转换为嵌入向量。其中使用ERNIE Bot库来创建文本的嵌入,并且通过Chromadb库来管理这些嵌入向量。
importos
importerniebot
fromtypingimportDict,List,Optional
importchromadb
fromchromadb.api.typesimportDocuments,EmbeddingFunction,Embeddings
defembed_query(content):
response=erniebot.embedding.create(
model="ernie-text-embedding",
input=[content])
result=response.get_result()
print(result)
returnresult
classErnieEmbeddingFunction(EmbeddingFunction):
def__call__(self,input:Documents)->Embeddings:
embeddings=[]
fortextininput:
response=embed_query(text)
try:
embedding=response[0]
embeddings.append(embedding)
except(IndexError,TypeError,KeyError)ase:
print(f"Errorprocessingtext:{text},Error:{e}")
returnembeddings
chroma_client=chromadb.Client()
chroma_client=chromadb.PersistentClient(path="chromac")#数据保存硬盘位置可选
collection=chroma_client.create_collection(name="demo",embedding_function=ErnieEmbeddingFunction())
print(collection)
导入数据集
选用课程内容作为知识库
文档切割
使用LangChain库来处理和分割文本文档
fromlangchain.text_splitterimportCharacterTextSplitter
fromlangchain.vectorstoresimportChroma
fromlangchain.document_loadersimportTextLoader
loader=TextLoader('./AI大课逐字稿.txt',encoding='utf-8')
documents=loader.load()
text_splitter=CharacterTextSplitter(chunk_size=600,chunk_overlap=20)
docs=text_splitter.split_documents(documents)
docs
Embedding 嵌入
将分割后的文档列表转换为嵌入向量,以便进行进一步的分析和处理。
importuuid
docs_list=[]
metadatas=[]
ids=[]
foritemindocs:
docs_list.append(item.page_content)
metadatas.append({"source":"AI大课逐字稿"})
ids.append(str(uuid.uuid4()))
collection.add(
documents=docs_list,
metadatas=metadatas,
ids=ids
)
检索
query="讲师说见VC有两种错误的思维方式,分别是什么"
results=collection.query(
query_texts=[query],
n_results=2
)
content=results['documents'][0]
[]
prompt=f"""
用户问题:{query}
{content}
根据里的知识点回答用户问题
"""
response=erniebot.ChatCompletion.create(model="ernie-4.0",messages=[{"role":"user","content":prompt}])
print(response.get_result())
#讲师说见VC有两种错误的思维方式,分别是:
##1. 用过去的方式套今天的人工智能,比如比喻成OS。一旦比喻成操作系统,就得出结论全世界两套到三套,你觉得必然会被垄断、没有机会了,这种是典型的刻舟求剑。
#2. 人容易对已经成功的事委曲求全,对于创新的新生代创业者容易求全责备。特别是有些做VC容易犯这个错误,比如OpenAI做成了,已经证明了,是个傻子都能看到OpenAI做的很成功,我们容易对它顶礼膜拜,恨不得跪下。对创业者很多还不成形的想法,因为八字没有一撇,光看到了你的很多缺点,这种价值观是不对的,容易Miss掉一些有潜力的项目。
封装函数
包含了之前步骤中存储的文本嵌入向量。函数的目的是接收用户的查询,从数据库中检索相关信息,并生成一个回答。
defmain(query):
results=collection.query(
query_texts=[query],
n_results=2
)
content=results['documents'][0]
prompt=f"""
用户问题:{query}
{content}
根据里的知识点回答用户问题
"""
response=erniebot.ChatCompletion.create(model="ernie-4.0",messages=[{"role":"user","content":prompt}])
returnresponse.get_result()
query=input("请输入您要查询的问题:")
print(main(query))
显然,RAG的应用不仅仅满足于此,目前也诞生了各种RAG的高阶用法。
通过不断优化RAG,使其具有更强大的信息理解能力,理解问题更加透彻,找到与问题高度匹配的信息后,生成更为精准的答案。比如针对“讲一下金毛犬的特点”这一指令,高级RAG模型可以理解这是一个关于小狗特定品种的问题,将从知识库中提取金毛犬的细节信息,如体格、性格、历史等,以对齐问题的颗粒度,提供详细的回答。
在优化RAG的过程中,也产生了一系列相关的方法。
在信息检索和搜索引擎优化领域,通过实施一系列策略可以显著提升检索系统的性能。索引优化通过提升数据粒度、优化索引结服务器托管网构、添加元数据信息、对齐优化和混合检索等方法,可以提高检索的准确性和效率。向量表征模型的优化通过微调和动态嵌入技术,增强了模型对特定领域或问题的理解能力。检索后处理策略如重排序和Prompt压缩,进一步提升了检索结果的相关性和用户满意度。递归检索和搜索引擎优化通过递归检索和子查询等技术,实现了更复杂和精确的检索需求。最后,RAG评估通过独立评估和端到端评估方法,确保了检索系统在各个方面都能满足用户的需求。这些策略的实施,共同推动了检索技术的进步,为用户提供了更加高效和精准的信息服务。具体参考下图:
除了以上5种方法,还有其他很多高级的RAG用法,大家可以针对感兴趣的部分自行查阅相关论文,进行学习了解。
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
路径参数、查询参数,和请求体混合 首先,我们需要导入所需的库。我们将使用FastAPI、Path和Annotated来处理路由和参数,并使用BaseModel和Union来自定义数据模型。 完整示例代码 from typing import Annotated…