:::info
💡 在自然语言处理领域,大型语言模型(LLM)如GPT-3、BERT等已经取得了显著的进展,它们能够生 成连贯、自然的文本,回答问题,并执行其他复杂的语言任务。然而,这些模型存在一些固有的局限 性, 如“模型幻觉问题”、“时效性问题”和“数据安全问题”。为了克服这些限制,检索增强生成 (RAG)技术应运而生。
:::
什么是检索增强生成?
检索增强生成(RAG)是指对大型语言模型输出进行优化,使其能够在生成响应之前引用训练数据来源之外的权威知识库。大型语言模型(LLM)用海量数据进行训练,使用数十亿个参数为回答问题、翻译语言和完成句子等任务生成原始输出。在 LLM 本就强大的功能基础上,RAG 将其扩展为能访问特定领域或组织的内部知识库,所有这些都无需重新训练模型。这是一种经济高效地改进 LLM 输出的方法,让它在各种情境下都能保持相关性、准确性和实用性。
为什么需要RAG?
LLM 是一项关键的人工智能(AI)技术,为智能聊天机器人和其他自然语言处理(NLP)应用程序提供支持。目标是通过交叉引用权威知识来源,创建能够在各种环境中回答用户问题的机器人。不幸的是,LLM 技术的本质在 LLM 响应中引入了不可预测性。此外,LLM 训练数据是静态的,并引入了其所掌握知识的截止日期。
LLM 面临的已知挑战包括:
- 在没有答案的情况下提供虚假信息。
- 当用户需要特定的当前响应时,提供过时或通用的信息。
- 从非权威来源创建响应。
- 由于术语混淆,不同的培训来源使用相同的术语来谈论不同的事情,因此会产生不准确的响应。
您可以将大型语言模型看作是一个过于热情的新员工,他拒绝随时了解时事,但总是会绝对自信地回答每一个问题。不幸的是,这种态度会对用户的信任产生负面影响,这是您不希望聊天机器人效仿的!
RAG 是解决其中一些挑战的一种方法。它会重定向 LLM,从权威的、预先确定的知识来源中检索相关信息。组织可以更好地控制生成的文本输出,并且用户可以深入了解 LLM 如何生成响应。
检索增强生成的工作原理是什么?
如果没有 RAG,LLM 会接受用户输入,并根据它所接受训练的信息或它已经知道的信息创建响应。RAG 引入了一个信息检索组件,该组件利用用户输入首先从新数据源提取信息。用户查询和相关信息都提供给 LLM。LLM 使用新知识及其训练数据来创建更好的响应。以下各部分概述了该过程。
创建外部数据
LLM 原始训练数据集之外的新数据称为外部数据。它可以来自多个数据来源,例如 API、数据库或文档存储库。数据可能以各种格式存在,例如文件、数据库记录或长篇文本。另一种称为嵌入语言模型的 AI 技术将数据转换为数字表示形式并将其存储在向量数据库中。这个过程会创建一个生成式人工智能模型可以理解的知识库。
检索相关信息
下一步是执行相关性搜索。用户查询将转换为向量表示形式,并与向量数据库匹配。例如,考虑一个可以回答组织的人力资源问题的智能聊天机器人。如果员工搜索:“我有多少年假?”,系统将检索年假政策文件以及员工个人过去的休假记录。这些特定文件将被退回,因为它们与员工输入的内容高度相关。相关性是使用数学向量计算和表示法计算和建立的。
增强 LLM 提示
接下来,RAG 模型通过在上下文中添加检索到的相关数据来增强用户输入(或提示)。此步骤使用提示工程技术与 LLM 进行有效沟通。增强提示允许大型语言模型为用户查询生成准确的答案。
更新外部数据
下一个问题可能是——如果外部数据过时了怎么办? 要维护当前信息以供检索,请异步更新文档并更新文档的嵌入表示形式。您可以通过自动化实时流程或定期批处理来执行此操作。这是数据分析中常见的挑战——可以使用不同的数据科学方法进行变更管理。
下图显示了将 RAG 与 LLM 配合使用的概念流程。
检索增强生成和语义搜索有什么区别?
语义搜索可以提高 RAG 结果,适用于想要在其 LLM 应用程序中添加大量外部知识源的组织。现代企业在各种系统中存储大量信息,例如手册、常见问题、研究报告、客户服务指南和人力资源文档存储库等。上下文检索在规模上具有挑战性,因此会降低生成输出质量。
语义搜索技术可以扫描包含不同信息的大型数据库,并更准确地检索数据。例如,他们可以回答诸如 “去年在机械维修上花了多少钱?”之类的问题,方法是将问题映射到相关文档并返回特定文本而不是搜索结果。然后,开发人员可以使用该答案为 LLM 提供更多上下文。
RAG 中的传统或关键字搜索解决方案对知识密集型任务产生的结果有限。开发人员在手动准备数据时还必须处理单词嵌入、文档分块和其他复杂问题。相比之下,语义搜索技术可以完成知识库准备的所有工作,因此开发人员不必这样做。它们还生成语义相关的段落和按相关性排序的标记词,以最大限度地提高 RAG 有效载荷的质量。
RAG 与 Fine-Tuning 相比如何?
关于 RAG 和微调之间的区别以及各自适用的场景有很多公开讨论。这两个领域的研究表明,RAG 对于整合新知识很有用,而微调可通过改进内部知识、输出格式和教授复杂的指令来提高模型性能和效率。这些方法并不相互排斥,可以在迭代过程中相互补充,旨在提高法学硕士在复杂的知识密集型和可扩展应用程序中的使用,该应用程序需要访问快速发展的知识和遵循特定格式、语气的定制响应和风格。此外,Prompting Engineering 还可以利用模型的固有功能来帮助优化结果。下图展示了RAG与其他模型优化方法相比的不同特点:
以下是调查论文中的表格,比较了 RAG 和微调模型之间的特征:
RAG实践:基于langchain实现的RAG技术
这里我是在云上环境下,跑通的https://github.com/owenliang/rag-retrieval 项目,正好顺便学下langchain。整个过程可以看成如下形式:
安装环境
安装项目依赖:
pip install langchain pypdf rapidocr-onnxruntime modelscope transformers faiss-cpu tiktoken -i https://mirrors.aliyun.com/pypi/simple/
之后推荐先安装vllm,它对于torch、nvidia cuda等都有特定要求,如果是先安装的pytorch,按照我写这篇笔记的时间来算,需要torch==2.1的版本,另外不清楚是我使用的某云平台选用镜像是否有问题,它顺带给我连torch/cuda/cudnn的Python包全更新了一遍,所以建议先vllm:
# version 1
pip install vllm
# version 2
git clone https://github.com/vllm-project/vllm.git
cd vllm
pip install -e .
# version 3
git clone https://github.com/vllm-project/vllm.git
cd vllm
python setup.py install
服务端启动
没有问题,可参照vLLM | 🦜️🔗 Langchain 文档所写demo,进行尝试,当然是建立在自家网络能通huggingface情况下,因为要去请求拉取大模型,如果连接超时的话就采取以下方式:
export VLLM_USE_MODELSCOPE=True
python -m vllm.entrypoints.openai.api_server --model 'qwen/Qwen-7B-Chat-Int4' --trust-remote-code -q gptq --dtype float16 --gpu-memory-utilization 0.6
设置路径指向modelscope仓库,然后命令行作为shell命令,使用Qwen-7B-Chat-Int4模型,以此作为服务端程序启动。
客户端启动
服务端可以通过vllm简化,这是目前比较主流的一个框架,客户端就需要自己完成代码,首先需要解析pdf生成向量库:
from langchain_community.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.embeddings import ModelScopeEmbeddings
from langchain_community.vectorstores import FAISS
# 解析PDF,切成chunk片段
pdf_loader=PyPDFLoader('LLM.pdf',extract_images=True) # 使用OCR解析pdf中图片里面的文字
chunks=pdf_loader.load_and_split(text_splitter=RecursiveCharacterTextSplitter(chunk_size=100,chunk_overlap=10))
# 加载embedding模型,用于将chunk向量化
embeddings=ModelScopeEmbeddings(model_id='iic/nlp_corom_sentence-embedding_chinese-base')
# 将chunk插入到faiss本地向量数据库
vector_db=FAISS.from_documents(chunks,embeddings)
vector_db.save_local('LLM.faiss')
print('faiss saved!')
上述代码中:
- 解析PDF,切成chunk片段:该两行代码中,第一行是使用OCR解析pdf中图片里面的文字,需要注意的是这里的文字必须是可复制的,举个例子,如果PDF中带有截图,截图上的文字是无法被底层的pypdf所解析的,另外,在我的使用过程中,发现一个bug,对于我给的某些PDF,在第二句通过load_and_split做chunk时,会发生reshape错误,数组不对等,看了下底层源码,感觉之后有时间可以尝试修修,这里Mark一下
- 加载embedding模型,用于将chunk向量化:这里所选用的模型为modelscope最主流的一个,地址为CoROM文本向量-中文-通用领域-base ,里面提供了测试demo以及在线体验地址。
- 将chunk插入到faiss本地向量数据库:Faiss是一个Facebook AI库,它可以使相似度的搜索变得容易;比如,我们有一个向量的集合,我们可以通过Faiss给他们建索引,然后用一个别的向量,我们可以用这个索引在这个集合中找到跟它相似的向量。它不只能够让我们构建向量索引、搜索向量,同样可以通过参数的调节,使搜索变快,但是可能会导致召回或准确度下降。git地址为:https://github.com/facebookresearch/faiss
运行完上述代码后,在当前目录下生成了向量库,之后就是RAG的代码:
from langchain_community.embeddings import ModelScopeEmbeddings
from langchain_community.vectorstores import FAISS
from langchain.memory import ChatMessageHistory
from langchain.prompts.chat import ChatPromptTemplate,SystemMessagePromptTemplate,HumanMessagePromptTemplate,AIMessagePromptTemplate,MessagesPlaceholder
from langchain.schema import HumanMessage,SystemMessage,AIMessage
from langchain_openai import ChatOpenAI
from langchain_core.runnables import RunnablePassthrough
from operator import itemgetter
import os
# 加载embedding模型,用于将query向量化
embeddings=ModelScopeEmbeddings(model_id='iic/nlp_corom_sentence-embedding_chinese-base')
# 加载faiss向量库,用于知识召回
vector_db=FAISS.load_local('LLM.faiss',embeddings)
retriever=vector_db.as_retriever(search_kwargs={"k":5})
# 用vllm部署openai兼容的服务端接口,然后走ChatOpenAI客户端调用
os.environ['VLLM_USE_MODELSCOPE']='True'
chat=ChatOpenAI(
model="qwen/Qwen-7B-Chat-Int4",
openai_api_key="EMPTY",
openai_api_base='http://localhost:8000/v1',
stop=['<|im_end|>']
)
# Prompt模板
system_prompt=SystemMessagePromptTemplate.from_template('You are a helpful assistant.')
user_prompt=HumanMessagePromptTemplate.from_template('''
Answer the question based only on the following context:
{context}
Question: {query}
''')
full_chat_prompt=ChatPromptTemplate.from_messages([system_prompt,MessagesPlaceholder(variable_name="chat_history"),user_prompt])
'''
<|im_start|>system
You are a helpful assistant.
<|im_end|>
...
<|im_start|>user
Answer the question based only on the following context:
{context}
Question: {query}
<|im_end|>
<|im_start|>assitant
......
<|im_end|>
'''
# Chat chain
chat_chain={
"context": itemgetter("query") | retriever,
"query": itemgetter("query"),
"chat_history":itemgetter("chat_history"),
}|full_chat_prompt|chat
# 开始对话
chat_history=[]
while True:
query=input('query:')
response=chat_chain.invoke({'query':query,'chat_history':chat_history})
chat_history.extend((HumanMessage(content=query),response))
print(response.content)
chat_history=chat_history[-20:] # 最新10轮对话
此段代码中:
- Prompt模板:
- system_prompt:系统消息提示模板,指定了系统消息“You are a helpful assistant.”。
- user_prompt:用户消息提示模板,其中包含了一个占位符{context}和{query},在实际生成对话Prompt时,这些占位符会被替换为具体的上下文和问题内容。
- full_chat_prompt:完整的对话Prompt模板,包括了系统消息、用户消息和助手消息的组合。在这个模板中,使用了MessagesPlaceholder来表示对话历史的部分,该部分在实际生成对话Prompt时会被填充为之前的对话内容。
具体测试demo可看官网:langchain_core.prompts.chat.ChatPromptTemplate — 🦜🔗 LangChain 0.1.12
- Chat chain:根据输入数据中的 “query” 和 “chat_history” 来生成相应的上下文、查询和聊天历史,然后经过定义好的处理流程进行聊天对话的处理。最后通过invoke调用,并保留最新的十轮对话,因为是一问一答形式的,即chat_history列表中是存着一对对问答集。
运行结果
首先启动服务端,客户端按照上述过程运行后,我这的云上环境好像又不支持中文输入了,于是我改成英文问,并要求返回中文,具体测试结果如下,结果还不错:
(base) root@423810881097310208-taskrole1-0:/gemini/code/tcdata# python rag.py
2024-03-16 04:10:57,825 - modelscope - INFO - PyTorch version 2.1.2 Found.
2024-03-16 04:10:57,827 - modelscope - INFO - TensorFlow version 2.13.1 Found.
2024-03-16 04:10:57,827 - modelscope - INFO - Loading ast index from /root/.cache/modelscope/ast_indexer
2024-03-16 04:10:57,857 - modelscope - INFO - Loading done! Current index file version is 1.13.1, with md5 11a650147607d3bf668dc8f598f9a2dc and a total number of 972 components indexed
2024-03-16 04:11:00,503 - modelscope - WARNING - Model revision not specified, use revision: v1.1.0
2024-03-16 04:11:00,807 - modelscope - INFO - initiate model from /root/.cache/modelscope/hub/iic/nlp_corom_sentence-embedding_chinese-base
2024-03-16 04:11:00,807 - modelscope - INFO - initiate model from location /root/.cache/modelscope/hub/iic/nlp_corom_sentence-embedding_chinese-base.
2024-03-16 04:11:00,807 - modelscope - INFO - initialize model from /root/.cache/modelscope/hub/iic/nlp_corom_sentence-embedding_chinese-base
/root/miniconda3/lib/python3.10/site-packages/torch/_utils.py:831: UserWarning: TypedStorage is deprecated. It will be removed in the future and UntypedStorage will be the only storage class. This should only matter to you if you are using storages directly. To access UntypedStorage directly, use tensor.untyped_storage() instead of tensor.storage()
return self.fget.__get__(instance, owner)()
2024-03-16 04:11:01,893 - modelscope - WARNING - No preprocessor field found in cfg.
2024-03-16 04:11:01,893 - modelscope - WARNING - No val key and type key found in preprocessor domain of configuration.json file.
2024-03-16 04:11:01,893 - modelscope - WARNING - Cannot find available config to build preprocessor at mode inference, current config: {'model_dir': '/root/.cache/modelscope/hub/iic/nlp_corom_sentence-embedding_chinese-base'}. trying to build by task and model information.
2024-03-16 04:11:02,084 - modelscope - WARNING - No preprocessor field found in cfg.
2024-03-16 04:11:02,084 - modelscope - WARNING - No val key and type key found in preprocessor domain of configuration.json file.
2024-03-16 04:11:02,084 - modelscope - WARNING - Cannot find available config to build preprocessor at mode inference, current config: {'model_dir': '/root/.cache/modelscope/hub/iic/nlp_corom_sentence-embedding_chinese-base', 'sequence_length': 128}. trying to build by task and model information.
/root/miniconda3/lib/python3.10/site-packages/langchain_core/utils/utils.py:159: UserWarning: WARNING! stop is not default parameter.
stop was transferred to model_kwargs.
Please confirm that stop is what you intended.
warnings.warn(
query:Please tell me in detail about the basic classification of large AI models, the involved categories and basic functions, etc. Please return the answer to me in Chinese.
/root/miniconda3/lib/python3.10/site-packages/transformers/modeling_utils.py:962: FutureWarning: The `device` argument is deprecated and will be removed in v5 of Transformers.
warnings.warn(
大模型的分类主要分为NLP和图像模态两大类。
NLP类大模型主要包括文本分类、情感分析等基本功能。下游应用广泛,包括但不限于搜索引擎、智能客服、文本挖掘、自动摘要、机器翻译等。NLP类大模型的应用占比相对较高,表现分析也相对较好。
图像模态类大模型主要包括图像分类、图像检索、图像生成等基本功能。下游应用主要包括但不限于图像识别、医疗诊断、自动驾驶、虚拟现实等。图像模态类大模型的应用占比相对较低,但随着技术的发展,其应用前景广阔。
以上是关于大模型的基本分类、涉及类别和基本功能的详细介绍。
query:What are the application scenarios of the large AI model? Describe them in detail, including application scenarios and uses. The answer will be returned to Chinese.
/root/miniconda3/lib/python3.10/site-packages/transformers/modeling_utils.py:962: FutureWarning: The `device` argument is deprecated and will be removed in v5 of Transformers.
warnings.warn(
大型AI模型的应用场景主要分为文本模态和图像模态两大类。
在文本模态方面,大型AI模型主要用于文本分类、情感分析等基本功能。下游应用广泛,包括搜索引擎、智能客服、文本挖掘、自动摘要、机器翻译等。这些应用在实际操作中具有广泛的应用价值,可以帮助人们快速、准确地获取所需信息。
在图像模态方面,大型AI模型主要用于图像分类、图像检索、图像生成等基本功能。下游应用主要包括但不限于图像识别、医疗诊断、自动驾驶、虚拟现实等。这些应用在实际操作中具有广泛的应用价值,可以帮助人们更好地理解和掌握图像信息。
以上是在文本模态和图像模态方面,大型AI模型的具体应用场景和使用。
query:Let’s talk about the internal iteration period of the model from 2017 to 2022. Please return it to me in Chinese.
/root/miniconda3/lib/python3.10/site-packages/transformers/modeling_utils.py:962: FutureWarning: The `device` argument is deprecated and will be removed in v5 of Transformers.
warnings.warn(
在2017年至2022年期间,模型内部迭代期发生了重大变化。从Transformer开始,逐渐发展到GPT-1/2/3,再到InstructGPT。这个时期的模型迭代期主要体现在模型内部结构和参数量上的改进,以及模型性能和效率的提升。这些模型的出现,为AI领域的研究和发展开辟了新的道路,推动了AI技术的快速发展。
reference
- Retrieval Augmented Generation
- https://www.hopsworks.ai/dictionary/retrieval-augmented-generation-llm
- 什么是 RAG?— 检索增强生成详解 — AWS
- 检索增强生成(RAG):从理论到 LangChain 实践 [译]
以上内容来源于本人语雀公开笔记,如果有些排版问题可移步:rag笔记(一):技术介绍与实践 这里是搬运至csdn