吴恩达DeepLearning.AI课程系列 - 大模型检索增强生成(五):问题回复

科技   2024-12-12 21:01   广东  

SmartFlowAI


点击上方蓝字关注我们

作者:李剑锋

全文约 6000 字,预计阅读时间 10 分钟

1. 前言

在前两节课中,我们介绍了多种大模型的检索方法,包括基础的相似度搜索以及更高级的大语言模型辅助检索等技术。这些检索方法的核心目标,是从向量数据库中提取与问题最相关、最有价值的信息。

在掌握了这些方法之后,接下来我们将重点探讨如何将提取到的有用信息有效地传递给大模型,并使其能够基于检索到的信息生成更精准的回答。那相信大家都会好奇这到底是如何实现的,那就让带大家来看看吧!

2. 检索式问答链(RetrievalQA Chain)

首先要介绍的就是我们最常见也最常使用的方法——检索式问答链(RetrievalQA Chain)。这是一种结合检索和生成的问答方法,旨在通过检索相关信息并利用大语言模型(LLM)生成精准答案。整个流程分为提问、检索相关文档、组合提示并生成答案几个主要阶段,是检索增强生成(Retrieval-Augmented Generation, RAG)方法的典型实现。

我们可以从下图看出其具体的流程解析:

  • 提问和查询 用户提出问题后,系统会将问题转化为向量形式,并将其作为查询发送至向量存储(Vector Store)中。向量存储中包含了大量经过预处理的文档向量,可支持快速的相似度匹配。
  • 检索相关信息 向量存储会根据问题的向量表示,返回多个最相关的文档片段(Relevant Splits)。这些文档片段通常包含了问题的答案或解答所需的关键背景信息。这也是我们前几节课探讨的重点内容。
  • 提示词整合 系统将检索到的文档片段与用户的原始问题组合成提示(Prompt)。同时,系统会生成必要的背景信息(System Prompt),并保留用户的问题(Human Question),将所有信息整合成完整的上下文输入。
  • 生成答案 系统将整合后的提示发送给大语言模型(LLM),模型会基于提供的上下文和问题生成精准的答案。由于答案建立在检索到的相关信息基础上,因此能够更准确地回应用户的问题。

我们其实本节课实战部分最重点要探讨的就是第三点——提示词整合,我们需要利用Langchain将提示词按照我们想要的格式进行组合后,传给大语言模型生成我们想要的回复。这个方案相比于最传统的将问题输入给大语言模型,其优势在于:

  • 增强答案准确性:通过结合检索结果与大语言模型的生成能力,大大提高了答案的相关性和准确性。
  • 支持实时更新知识库:检索过程依赖于向量存储中的数据,这些数据可以根据需要实时更新,确保答案反映最新的知识。
  • 减轻模型记忆负担:将知识库中的信息作为输入上下文,减少了对模型内部参数存储知识的依赖。

因此RetrievalQA Chain 可广泛应用于问答系统、智能客服和搜索增强任务等领域。在这些场景中,该方法不仅能快速检索相关信息,还能结合领域知识,生成高质量的答案,是解决复杂问答任务的有效工具。

3. 其他方法

除了RetrievalQA Chain以外,如下图所示,还有其他的一些方法,比如map_reduce、Map_reank以及refine等。

3.1. Map_reduce

Map_reduce 方法将文档分成多个小块(chunks),每个块单独传递给语言模型(LLM)生成独立答案。然后,所有生成的答案会被合并成最终答案,合并过程(reduce)可能包括总结、投票等操作。

可以想象你有一本厚厚的书需要总结。这种方法会先把书分成多个小部分(称为“块”),然后每个部分都交给不同的助手,让他们各自生成一个答案。最后,你再把这些答案收集起来,用一种方式把它们合并成一个最终的答案(比如找出其中的主要观点,或者取平均结论)。

所以其流程大致如下:

  1. 分块处理:将文档拆成多个小部分。
  2. 独立回答:每个部分都交给大语言模型,分别生成独立的答案。
  3. 答案归约:把所有小答案综合起来,生成一个整体的答案。

从这里我们也可以看出,与RetrievalQA Chain相比,其会先检索一部分相关内容,再将问题和内容结合后生成答案。而 Map_reduce 是针对所有文档的每一部分单独生成答案,并通过合并提高效率,特别适合处理大量数据时的并行化任务。

3.2. Refine

Refine 方法从第一个文档块中生成一个初始答案,然后对后续文档块逐一处理。每个块会对已有的答案进行补充或修正,直到所有块处理完成,最终得到经过优化和完善的答案。

通俗点说,假设你在写一篇论文,每次只写一段,然后再逐步修改和补充前面已经写好的内容。这种方法会先生成一个初始答案,然后再逐个查看文档的其余部分,看看是否需要修改答案,或者在答案上加上新的细节,直到答案足够完善。

所以其流程大致如下:

  1. 初步回答:从第一个文档块中生成一个初始答案。
  2. 逐步完善:对于每一个后续文档块,逐一更新初始答案,使答案更加精确和全面。
  3. 最终结果:生成一个经过多轮完善的高质量答案。

所以和RetrievalQA Chain相比,RetrievalQA Chain是一次性用检索到的文档生成答案,没有后续的反复改进。而 Refine 方法会逐步地完善答案,更注重精细化和准确性,适合需要反复推敲和改进的任务。

3.3. Map_rerank

Map_rerank 方法将文档分成多个块,每个块都单独生成答案,并给这些答案打分。打分依据可以是相关性、质量等指标,最后选择得分最高的答案作为最终输出。这就像一个比赛。每个文档块都生成一个答案,这些答案好比参赛者。然后,每个答案会被打分,分数越高代表越接近问题的需求。最后,选择分数最高的那个答案作为最终结果。

那具体的流程就是:

  1. 分块处理:文档被分成多个部分。
  2. 单独生成答案:每个部分生成一个答案。
  3. 打分排序:对每个答案进行打分(比如根据其相关性、完整性)。
  4. 选择最佳答案:选择分数最高的那个答案。

所以从上面就可以看出,RetrievalQA Chain 偏向于综合所有相关文档的信息,而 Map_rerank 更注重对生成答案的筛选,通过打分和排序选出一个最优答案,而不是结合所有内容。

3.4. 小结

针对以上面提到的四种方法,我们可以参考以下策略来进行选择。

  • 如果需要处理大量文档并快速生成答案,建议选择 Map_reduce。它通过将文档分块并行处理,不仅节省时间,还能确保每个部分得到独立且完整的分析,特别适合大规模数据处理场景。
  • 当对答案质量和细节有较高要求时,可以使用 Refine。它会在处理每个新文档块时逐步优化和补充答案,从而确保上下文连贯性并生成更精准的答案。
  • 如果目标是找出最佳答案而非整合所有信息,可以采用 Map_rerank。通过对各个答案进行评分和排序,能够快速筛选出最相关、质量最高的结果。
  • RetrievalQA Chain 则专注于将问题与检索到的相关信息结合,直接交由大语言模型生成答案。这种方法流程简单直接,虽然灵活性和针对性略低,但由于其效率高度依赖于检索内容的质量,非常适合处理通用问答场景。

4. 实战练习

那在今天的练习里,我们主要针对的就是我们最常使用的RetrievalQA Chain。那同样的,我们也要准备好以下的python库并对其进行安装。

langchain                0.3.0
langchain-community      0.3.0
pypdf                    5.0.0
openai                   1.47.0
beautifulsoup4           4.12.3
chromadb                 0.5.15PyJWT                   2.10.1

为了满足本次任务的需求,我们不仅需要通过嵌入模型获取信息,还需调用大型语言模型(LLM)生成相应的回复。由于官方课程所使用的模型均为OpenAI系列的模型,这些模型都在国内的环境下无法使用。因此我选择了智谱AI推出的免费模型 GLM-4-Flash 进行演示。

GLM-4-Flash是智谱AI推出的首个免费大模型API,旨在让AI技术惠及更广泛的用户群体。该模型在语义理解、数学计算、推理、代码生成和知识应用等多个领域表现出色,支持高达1500的并发请求,能够满足大规模应用的需求。

使用GLM-4-Flash非常简便,用户只需登录智谱AI官网,完成注册后即可获取API Key,开始调用该模型的强大功能。这为开发者和企业提供了一个高效、低成本的解决方案,助力各类智能应用的快速开发和部署。

那为了能够实现智谱AI的调用,我们首先就需要在之前创建的环境下在终端输入以下指令安装PyJWT这个库。

pip install pyjwt -i https://pypi.org/simple

安装完成并在官网获取到API key后我们可以开始尝试调用一下智谱AI。

# 导入所需的模块
from langchain_community.chat_models import ChatZhipuAI
from langchain_core.messages import AIMessage, HumanMessage, SystemMessage

# 初始化 ChatZhipuAI 对象,指定模型名称、温度参数和 API 密钥
chat = ChatZhipuAI(
    model="glm-4-flash",  # 使用的模型名称
    temperature=0.5,      # 生成文本的随机性,范围为0到1
    api_key="你的api_key"  # 智谱AI提供的API密钥
)

# 定义消息列表,包含 AI 消息、系统消息和人类消息
messages = [
    AIMessage(content="Hi."),  # AI 生成的消息
    SystemMessage(content="Your role is a poet."),  # 系统设定的角色信息
    HumanMessage(content="Write a short poem about AI in four lines."),  # 人类发送的消息
]

# 调用 chat 对象的 invoke 方法,传入消息列表,获取 AI 的回复
response = chat.invoke(messages)

# 输出 AI 生成的内容
print(response.content)  # 显示 AI 生成的关于人工智能的四行短诗

假如我们在终端可以看到以下内容大致就可以认定是成功调用LLM获取回复了。

Whispers weave through circuits, dreams in code,
Silent sages, in silicon they reside,
A symphony of zeros, ones, and bytes,
The age of artificial, where minds collide.

然后我们可以把之前用过的向量数据库相关的代码也放入,具体可以参考之前的课程内容。完整的代码如下所示。

from langchain_community.chat_models import ChatZhipuAI
from langchain_core.messages import AIMessage, HumanMessage, SystemMessage
from langchain_community.embeddings import BaichuanTextEmbeddings
from langchain_chroma import Chroma  # 从 langchain_chroma 中引用 Chroma 类
from langchain_community.document_loaders import WebBaseLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter

# 文件导入
loader = WebBaseLoader("https://zh.d2l.ai/")
docs = loader.load()

# 文本切分
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size = 1500,
    chunk_overlap = 150
)
splits = text_splitter.split_documents(docs)

persist_directory = r'D:\langchain'

# 初始化嵌入
embeddings = BaichuanTextEmbeddings(baichuan_api_key="sk-83842453061e34d80b392edba11f62fe")

# 使用 embedding_function 参数初始化 Chroma
vectordb = Chroma(
    persist_directory=persist_directory,
    embedding_function=embeddings
)

chat = ChatZhipuAI(
    model="glm-4-flash",
    temperature=0.5,
    api_key =  "8cb13630530001eac693610d7b3a97dc.JkcrYlDKXAbtvv4x"
)

messages = [
    AIMessage(content="Hi."),
    SystemMessage(content="Your role is a poet."),
    HumanMessage(content="Write a short poem about AI in four lines."),
]

response = chat.invoke(messages)
print(response.content)  # Displays the AI-generated poem

下面我们就需要开始调用Langchain中所提供的RetrievalQA chain进行测试使用。

from langchain_community.chat_models import ChatZhipuAI
from langchain_core.messages import AIMessage, HumanMessage, SystemMessage
from langchain_community.embeddings import BaichuanTextEmbeddings
from langchain_chroma import Chroma  # 从 langchain_chroma 中引用 Chroma 类
from langchain_community.document_loaders import WebBaseLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter

# 文件导入
loader = WebBaseLoader("https://zh.d2l.ai/")
docs = loader.load()

# 文本切分
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size = 1500,
    chunk_overlap = 150
)
splits = text_splitter.split_documents(docs)

persist_directory = r'D:\langchain'

# 初始化嵌入
embeddings = BaichuanTextEmbeddings(baichuan_api_key="sk-83842453061e34d80b392edba11f62fe")

# 使用 embedding_function 参数初始化 Chroma
vectordb = Chroma(
    persist_directory=persist_directory,
    embedding_function=embeddings
)

chat = ChatZhipuAI(
    model="glm-4-flash",
    temperature=0.5,
    api_key =  "8cb13630530001eac693610d7b3a97dc.JkcrYlDKXAbtvv4x"
)

from langchain.chains import RetrievalQA
question = "这本书最重要的主题是?"
qa_chain = RetrievalQA.from_chain_type(
    chat, # 大模型的选用
    retriever=vectordb.as_retriever() # 向量数据库的调用
)
result = qa_chain({"query": question}) # 输入问题
print(result["result"]) # 获取回复

假如结果出现以下内容则意味着我们其实成功的进行了调用。正常来说大模型应该不知道说这本书是什么,但是结合了数据库的内容后,大语言模型清楚的知道我们所说的书就是这本由李沐大神所写的《动手学深度学习》。

这本书最重要的主题是**深度学习**,特别是其概念、模型、算法和实现。它全面介绍了深度学习的各个方面,包括:

* **深度学习基础**:介绍深度学习的起源、发展、成功案例和特点,以及机器学习中的关键组件。
* **预备知识**:涵盖数据操作、数据预处理、线性代数、微积分、自动微分、概率等基础知识,为学习深度学习打下坚实基础。
* **线性神经网络**:从线性回归和softmax回归开始,逐步介绍多层感知机、模型选择、过拟合和欠拟合、权重衰减、暂退法等概念。
* **深度学习计算**:介绍深度学习中的层和块、参数管理、延迟初始化、自定义层、读写文件和GPU等计算相关内容。
* **卷积神经网络**:从卷积操作和图像卷积开始,逐步介绍填充、步幅、多输入多输出通道、汇聚层和LeNet等概念。
* **现代卷积神经网络**:介绍深度卷积神经网络(AlexNet)、VGG、NiN、GoogLeNet、批量规范化、残差网络(ResNet)和DenseNet等现代卷积神经网络。
* **循环神经网络**:介绍序列模型、文本预处理、语言模型、循环神经网络、时间反向传播等概念。
* **现代循环神经网络**:介绍门控循环单元(GRU)、长短期记忆网络(LSTM)、深度循环神经网络、双向循环神经网络、机器翻译、编码器-解码器架构、序列到序列学习和束搜索等概念。
* **注意力机制**:介绍注意力提示、注意力汇聚、注意力评分函数、Bahdanau注意力、多头注意力、自注意力和位置编码、Transformer等注意力机制。

总而言之,《动手学深度学习》旨在帮助读者全面了解深度学习,并掌握其核心概念和技能。

除了直接这样调用以外,其实我们还可以设置一些提示词模型使其更加的清晰明了。那在Langchain中我们可以这样来进行调用:

  1. 首先我们导入并创建一个template模版
from langchain.prompts import PromptTemplate

# Build prompt
template = """请使用以下上下文信息回答最后的问题。
如果您不知道答案,就直接说您不知道,不要试图编造答案。
回答最多使用三句话。请尽可能简洁地回答。最后一定要说“谢谢提问!”。
上下文:{context}
问题:{question}
有帮助的回答:"""

QA_CHAIN_PROMPT = PromptTemplate.from_template(template)
  1. 更新qa_chain里的内容并调用。
# Run chain
qa_chain = RetrievalQA.from_chain_type(
    chat,
    retriever=vectordb.as_retriever(),
    return_source_documents=True# 这个可有可无,只是返回找到的源文件内容
    chain_type_kwargs={"prompt": QA_CHAIN_PROMPT}
)
  1. 提供问题并调用
question = "这本书最重要的主题是?"
result = qa_chain({"query": question})
print(result["result"])
  1. 显示找到的最相关的向量数据库片段(可不加入)
print(result["source_documents"][0])

根据以上内容输入后,我们会发现其回复有了明显的不同,首先是其回复更加的简洁了,只回答了这本书最重要的主题,而没有像之前一样长篇大论。另外就是真的最后加上了一句谢谢提问。这也就意味着我们通过调整提示词的内容真的可以实现指令控制来让大模型输出我们想让其输出的内容。

这本书最重要的主题是深度学习,涵盖了从基础知识到高级应用,包括各种模型、算法和实现方法。

谢谢提问!

本节课程完整的代码如下所示:

from langchain_community.chat_models import ChatZhipuAI
from langchain_core.messages import AIMessage, HumanMessage, SystemMessage
from langchain_community.embeddings import BaichuanTextEmbeddings
from langchain_chroma import Chroma  # 从 langchain_chroma 中引用 Chroma 类
from langchain_community.document_loaders import WebBaseLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter

# 文件导入
loader = WebBaseLoader("https://zh.d2l.ai/")
docs = loader.load()

# 文本切分
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size = 1500,
    chunk_overlap = 150
)
splits = text_splitter.split_documents(docs)

persist_directory = r'D:\langchain'

# 初始化嵌入
embeddings = BaichuanTextEmbeddings(baichuan_api_key="你的api_key")

# 使用 embedding_function 参数初始化 Chroma
vectordb = Chroma(
    persist_directory=persist_directory,
    embedding_function=embeddings
)

chat = ChatZhipuAI(
    model="glm-4-flash",
    temperature=0.5,
    api_key =  "你的api_key"
)

from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate

# Build prompt
template = """请使用以下上下文信息回答最后的问题。
如果您不知道答案,就直接说您不知道,不要试图编造答案。
回答最多使用三句话。请尽可能简洁地回答。最后一定要说“谢谢提问!”。
上下文:{context}
问题:{question}
有帮助的回答:"""

QA_CHAIN_PROMPT = PromptTemplate.from_template(template)

# Run chain
qa_chain = RetrievalQA.from_chain_type(
    chat,
    retriever=vectordb.as_retriever(),
    return_source_documents=True# 这个可有可无,只是返回找到的源文件内容
    chain_type_kwargs={"prompt": QA_CHAIN_PROMPT}
)

question = "这本书最重要的主题是?"
result = qa_chain({"query": question})
print(result["result"])

5. 总结

通过本节的学习,我们从理论到实战,全面了解了检索增强生成(RAG)的多种方法,特别是 RetrievalQA Chain 在实际场景中的强大表现。同时,我们还尝试通过提示词优化,让大语言模型更贴合我们的需求输出精准答案。相信随着这些技术的不断发展,我们离更智能、更贴心的 AI 助手又近了一步。如果你也对这些技术感兴趣,不妨亲自试一试,把这些工具融入到自己的项目中。

在下一节课中,我们将学习如何通过加入 Chat History,让大模型“记住”对话历史,从而实现流畅自然的多轮对话。同时,还会探讨如何高效存储和管理对话记录,确保系统既智能又高效。想让你的 AI 更懂“人话”?下一节绝对不容错过!✨


往期 · 推荐

吴恩达DeepLearning.AI课程系列 - 大模型检索增强生成(四):向量数据库中的检索优化

吴恩达DeepLearning.AI课程系列 —— 大模型检索增强生成(三):向量数据库及嵌入

吴恩达DeepLearning.AI课程系列 —— 大模型检索增强生成(二):文档划分技术简介

吴恩达DeepLearning.AI课程系列 —— 大模型检索增强生成(一)

🌠 番外:我们期待与读者共同探讨如何在 AI 的辅助下,更好地发挥人类的潜力,以及如何培养和维持那些 AI 难以取代的核心技能。通过深入分析和实践,我们可以更清晰地认识到 AI 的辅助作用,并在 AI 时代下找到人类的独特价值和发展空间。“机智流”公众号后台聊天框回复“cc”,加入机智流大模型交流群!

一起“点赞”三连👇

机智流
共赴 AI 时代浪潮~
 最新文章