项目名称:NVIDIA AI-AGENT夏季训练营--RAG智能对话机器人
报告日期:2024年8月18日
项目负责人:ZZY
项目概述:
这个项目利用语言模型解析图片,将图片信息保存为文本,并使用嵌入模型将信息保存到FAISS向量数据库中,再调用语言模型分别在基于数据库和不基于数据库这两种情况下回答相同的问题,对比回答的结果,从而实现并评价这个小型的多模态RAG系统的能力。
在实现多模态RAG的同时,也对比了不同的prompt设计对RAG效果的影响。
技术方案与实施步骤
-
模型选择:
图片识别大模型选择Microsoft的Phi-3-vision模型(相较于Phi-3-mini多了对图片的识别能力),用于生成回答的大模型选择phi-3-small。这两个模型非常轻量化,是构建小型RAG系统的极佳选择。通过Nvidia的NIM平台,可以很方便地通过api调用该模型。
嵌入模型则选择了ai-embed-qa-4,同样可以通过NIM平台来调用。
如果仅使用大模型,其生成的结果很有可能符合某种语言逻辑却存在事实上的错误,这属于大模型的“幻觉”。RAG(Retrieval-Augmented-Generation 检索增强生成)结合了检索技术和生成模型,通过外部知识源检索相关信息来辅助生成过程。在本次项目中,构建的小型RAG系统能从我们提供的真实数据中检索信息,从而减少生成内容与事实相悖的情况
-
数据的构建:
在本项目中,数据以文本为载体。文本在进行简单的处理后,利用预训练好的嵌入模型将文本转换为向量表示,保存在FAISS向量数据库中。在处理时,整个文本被分割成更小的段落或句子,这将有效提升RAG的检索准确率。
-
功能整合(进阶版RAG):
在多模态RAG中,外部数据不局限于文本。为了实现多模态RAG,在本项目中使用Phi-3-vision对目标图片进行解析,提取尽可能多的有效信息,以文本为中间载体将图片的信息向量化,从而将图片整合进我们的小型RAG系统,实现多模态。
实施步骤:
-
环境搭建:
首先需要安装Miniconda。
在打开的终端中按照下面的步骤执行,配置环境(如果遇到网络问题,可以尝试换清华源或者阿里源,加后缀-i http://mirrors.aliyun.com/pypi/simple --trusted-host mirrors.aliyun.com)
#创建python 3.8虚拟环境
conda create --name ai_endpoint python=3.8
##这里是3.8及以上版本就行
conda activate ai_endpoint
#安装nvidia_ai_endpoint工具
##这是langchai和NIM结合的工具库
pip install langchain-nvidia-ai-endpoints
#安装Jupyter Lab
pip install jupyterlab
#安装langchain_core
pip install langchain_core
#安装langchain
pip install langchain
#安装matplotlib
##涉及数据可视化和图表绘制
pip install matplotlib
#安装Numpy
pip install numpy
##安装faiss, 这里如果没有GPU可以安装CPU版本。实现这个小项目CPU版本足矣
pip install faiss-cpu==1.7.2
#安装OPENAI库
pip install openai
pip install gradio
pip install langchain-community
-
代码实现:
第一步:导入必要的库
from langchain_nvidia_ai_endpoints import ChatNVIDIA
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain.schema.runnable import RunnableLambda
from langchain.schema.runnable.passthrough import RunnableAssign
from langchain_core.runnables import RunnableBranch
from langchain_core.runnables import RunnablePassthrough
from langchain.chains import ConversationChain
from langchain.memory import ConversationBufferMemoryimport os
import base64
import matplotlib.pyplot as plt
import numpy as npfrom tqdm import tqdm
from pathlib import Pathfrom operator import itemgetter
from langchain.vectorstores import FAISS
from langchain.text_splitter import CharacterTextSplitter
import faiss
还得导入NVIDIA NIM的API KRY:
os.environ["NVIDIA_API_KEY"] =
第二步:利用Microsoft Phi 3 vision解析图片数据
import os
import base64
def image2b64(image_file):
# 获取当前工作目录
current_dir = os.getcwd()
# 构建完整的文件路径
full_path = os.path.join(current_dir, image_file)
with open(full_path, "rb") as f:
image_b64 = base64.b64encode(f.read()).decode()
return image_b64
# 测试代码
image_file = "economic-assistance-chart.png"
image_b64 = image2b64(image_file)
#print(image_b64)
from PIL import Image
display(Image.open("economic-assistance-chart.png"))
(这是用到的图片)
(↑↑↑这里的代码没截全,主要看下prompt和输出的内容,后文会有代码的补充↑↑↑)
第三步:初始化嵌入模型
from langchain_nvidia_ai_endpoints import NVIDIAEmbeddings
embedder = NVIDIAEmbeddings(model="ai-embed-qa-4")
第四步:获取文本数据
import os
from tqdm import tqdm
from pathlib import Path
# Here we read in the text data and prepare them into vectorstore
ps = os.listdir("./zh_data/")
data = []
sources = []
for p in ps:
if p.endswith('.txt'):
path2file="./zh_data/"+p
with open(path2file,encoding="utf-8") as f:
lines=f.readlines()
for line in lines:
if len(line)>=1:
data.append(line)
sources.append(path2file)
documents=[d for d in data if d != '\n']
len(data), len(documents), data[0]
第五步:将文本数据处理到FAISS vectorstore并将其保存到磁盘
# 只需要执行一次,后面可以重读已经保存的向量存储
text_splitter = CharacterTextSplitter(chunk_size=400, separator=" ")
docs = []
metadatas = []
for i, d in enumerate(documents):
splits = text_splitter.split_text(d)
#print(len(splits))
docs.extend(splits)
metadatas.extend([{"source": sources[i]}] * len(splits))
store = FAISS.from_texts(docs, embedder , metadatas=metadatas)
store.save_local('./zh_data/nv_embedding')
(第二次执行可只执行以下语句:)
## Load the vectorestore back.
#store = FAISS.load_local("./zh_data/nv_embedding", #embedder,allow_dangerous_deserialization=True)
第六步:提出问题并基于Phi-3-small-128k-instruct模型进行RAG检索,并与不使用RAG的情况作为对比
不使用RAG:
使用RAG
可以看出在使用RAG的情况下,大模型虽然限于性能还是不能完美的回答问题,但在基于真实数据的情况下,也不会产生幻觉以致于编造答案。
但我们仍可以做一些尝试,因为图片包含的信息其实是足以解答问题的。
第七步:提升RAG的表现
尝试修改prompt,得到长文本形式的图片信息,而不是markdown形式的表格。
得到新文本后,重复第四步、第五步,将新文本嵌入向量数据库,作为RAG的外源知识。
再重复第六步,观察大模型生成内容的变化。
这次大模型的回答正确了。
第八步:通过Gradio实现网页端的UI交互
# 定义一个函数,用于处理用户的输入
def get_answer(question):
result = chain.invoke(question)
return result
# 创建 Gradio 接口
interface = gr.Interface(
fn=get_answer, # 回调函数
inputs=gr.Textbox(label="Enter your question here"), # 文本输入
outputs=gr.Textbox(label="Answer"), # 文本输出
title="Document-based Q&A System", # 标题
description="Ask questions about the documents.", # 描述
allow_flagging="never", # 不允许标记
)
# 启动 Gradio 应用程序
interface.launch(debug=True, share=False, show_api=False, server_port=5000, server_name="0.0.0.0")
项目成果与展示:
-
应用场景展示:
RAG对话机器人的应用场景非常广泛,具有能够从外部数据源检索相关信息并结合语言生成模型来提供更加准确和有用的回答的能力。可以用于包括但不限于以下场景:
客户服务:企业可以构建个性化的客户问答助手,提高客户满意度。这类机器人可以从公司的常见问题解答(FAQ)、产品手册等文档中检索信息,提供针对性的答案。
教育辅导:为学生提供互动式的知识查询平台,增强自主学习能力。例如,学生可以通过提问得到教材中特定概念的解释或者练习题的解答。
内部知识库:大型组织可以借此构建内部知识检索系统,促进信息共享。员工可以通过对话的形式获取公司政策、技术文档等内部资源
-
功能演示:
通过Gradio实现网页端的UI交互
不使用RAG:
使用RAG:
问题与解决方案:
-
问题的发现及分析:
项目过程中遇到的一个有意思的小问题:在图片解析阶段不同的prompt会直接影响RAG的效果。猜测是在嵌入时跟文档切分和嵌入的方式有关。大模型实际上具备理解Markdown格式的表格的能力,但在本项目中,Markdown格式的表格存储在在txt中,而切分文本又是以换行符为分隔,每一行被转换为一个嵌入向量。如果失去第一行的表头,后面的几行markdown其实就是国家名和两个百分数,没有实际意义,在嵌入时就会损失信息,进而影响RAG的能力。而修改prompt后,从图片提取的文件为文本,每一段被转换为一个嵌入向量,自然就能保留更多的有效信息。
-
解决措施:
上面那个问题是通过输出图片提取信息的结果发现的。针对问题以及猜测可能的原因,调整prompt。原先的prompt要求模型根据图片生成对应的markdown表格,不利于嵌入后的检索。因此调整prompt,使得模型生成长文本形式的信息,从而提高了RAG在嵌入后检索的准确率。经过测试,phi-3-vision会根据不同prompt生成不同的输出。因此在完成其他类似的项目时,也要根据项目的要求,设计合适的prompt。
项目总结与展望:
-
项目评估:
这次项目整体上展示了多模态RAG实现的大致pipeline。虽然RAG的规模比较小,但有了pipeline之后,如果需要更强的能力,只用根据实际的需求,选择功能更强大部分进行组合就行。
存在的不足在于数据太少,只选了一张图片做了示范。
-
未来方向:
未来可以选择更强大的嵌入模型,可直接对图片、音频、视频等进行嵌入,而无需以文本为载体(向量数据库Weaviate中好像有google提供的多模态嵌入模型)。
也可以一次性嵌入更多的数据,形成个人的知识库,管理个人的不同格式的资料等。
附件与参考资料
(Nvidia AI-Agent夏季RAG训练营的课件)