本文学习 LangChain 中的 数据连接(Retrieval) 模块。该模块提供文档加载、切分,向量存储、检索等操作的封装。最后,结合RAG基本流程,我们将利用LangChain实现RAG的基本流程。
0. 模块介绍
在前面文章中我们已经讲了大模型存在的缺陷:数据不实时,缺少垂直领域数据和私域数据等。解决这些缺陷的主要方法是通过检索增强生成(RAG)。首先检索外部数据,然后在执行生成步骤时将其传递给LLM。
LangChain为RAG应用程序提供了从简单到复杂的所有构建块,本文要学习的数据连接(Retrieval)模块包括与检索步骤相关的所有内容,例如数据的获取、切分、向量化、向量存储、向量检索等模块(见下图)。
1. Document loaders 文档加载模块
LangChain封装了一系列类型的文档加载模块,例如PDF、CSV、HTML、JSON、Markdown、File Directory等。下面以PDF文件夹在为例看一下用法,其它类型的文档加载的用法都类似。
1.1 加载本地文件
LangChain加载PDF文件使用的是pypdf,先安装:
python
复制代码
pip install pypdf
加载代码示例:
python
复制代码
from langchain_community.document_loaders import PyPDFLoader
loader = PyPDFLoader("D:\GitHub\LEARN_LLM\RAG\如何向 ChatGPT 提问以获得高质量答案:提示技巧工程完全指南.pdf")
pages = loader.load_and_split()
print(f"第0页:\n{pages[0]}") ## 也可通过 pages[0].page_content只获取本页内容
看下运行结果:pypdf将PDF分成了一个数组,数组中的每个元素包含本页内容、文件路径和名称以及所在页码。
1.2 加载在线PDF文件
LangChain竟然也能加载在线的PDF文件。
1.2.1 可能需要的环境配置
在开始之前,你可能需要安装以下的Python包:
python
复制代码
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple unstructured
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pdf2image
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple opencv-python
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple unstructured-inference
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pikepdf
除了Python包,还需要下载 nltk_data,这东西非常大,下载起来非常慢。所以们可以事先下好,放到固定的位置。
- 下载地址:github.com/nltk/nltk_d…
- 下载完后,将其中的packages文件夹内的全部内容拷贝到固定位置,例如上面的 C:\Users\xxx\AppData\Roaming\nltk_data
1.2.2 示例代码
python
复制代码
from langchain_community.document_loaders import OnlinePDFLoader
loader = OnlinePDFLoader("https://arxiv.org/pdf/2302.03803.pdf")
data = loader.load()
print(data)
运行结果:
2. Text Splitting 文档切分模块
LangChain提供了许多不同类型的文本切分器,具体见下表:
名称 | 分割依据 | 添加元数据 | 描述 |
---|---|---|---|
Recursive | 用户定义字符列表 | 递归地分割文本。递归地分割文本的目的是试图保持相关的文本片段相邻。这是开始分割文本的推荐方法。 | |
HTML | HTML特定字符 | ✅ | 基于HTML特定字符分割文本。特别地,这会添加关于该片段来自何处的相关信息(基于HTML)。 |
Markdown | Markdown特定字符 | ✅ | 基于Markdown特定字符分割文本。特别地,这会添加关于该片段来自何处的相关信息(基于Markdown)。 |
Code | 代码(Python、JS)特定字符 | 基于编程语言特定字符分割文本。可选择15种不同的语言。 | |
Token | 标记 | 基于标记分割文本。有几种不同的标记计量方法。 | |
Character | 用户定义字符 | 基于用户定义字符分割文本。这是比较简单的方法之一。 | |
[Experimental] Semantic Chunker | 句子 | 首先基于句子分割文本。然后,如果相邻的句子在语义上足够相似,则合并它们。 |
这里以Recursive为例展示用法。RecursiveCharacterTextSplitter是LangChain对这种文档切分方式的封装,里面的几个重点参数:
- chunk_size:每个切块的token数量
- chunk_overlap:相邻两个切块之间重复的token数量
python
复制代码
from langchain_community.document_loaders import PyPDFLoader
loader = PyPDFLoader("D:\GitHub\LEARN_LLM\RAG\如何向 ChatGPT 提问以获得高质量答案:提示技巧工程完全指南.pdf")
pages = loader.load_and_split()
print(f"第0页:\n{pages[0].page_content}")
from langchain.text_splitter import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=200,
chunk_overlap=100,
length_function=len,
add_start_index=True,
)
paragraphs = text_splitter.create_documents([pages[0].page_content])
for para in paragraphs:
print(para.page_content)
print('-------')
以上示例程序将chunk_overlap设置为100,看下运行效果,可以看到上一个chunk和下一个chunk会有一部分的信息重合,这样做的原因是尽可能地保证两个chunk之间的上下文关系:
LangChain虽然提供了文本加载和分割模块的封装,但很明显,想要用在实际的项目中,还远远不够,需要在此基础上做非常细致的策略兜底或干脆自己实现一套符合自己实际需求的文档加载和分割器。
这里提供了一个可视化展示文本如何分割的工具,感兴趣的可以看下。
工具网址:chunkviz.up.railway.app/
3. Text embedding models 文本向量化模型封装
LangChain对一些文本向量化模型的接口做了封装,例如OpenAI, Cohere, Hugging Face等。 向量化模型的封装提供了两种接口,一种针对文档的向量化embed_documents
,一种针对句子的向量化embed_query
。
示例代码:
- 文档的向量化
embed_documents
,接收的参数是字符串数组
python
复制代码
from langchain_openai import OpenAIEmbeddings
embeddings_model = OpenAIEmbeddings() ## OpenAI文本向量化模型接口的封装
embeddings = embeddings_model.embed_documents(
[
"Hi there!",
"Oh, hello!",
"What's your name?",
"My friends call me World",
"Hello World!"
]
)
len(embeddings), len(embeddings[0])
##运行结果 (5, 1536)
- 句子的向量化
embed_query
,接收的参数是字符串
python
复制代码
embedded_query = embeddings_model.embed_query("What was the name mentioned in the conversation?")
embedded_query[:5]
## 运行结果:
## [0.0053587136790156364,
## -0.0004999046213924885,
## 0.038883671164512634,
## -0.003001077566295862,
## -0.00900818221271038]
4. Vector stores 向量存储(数据库)
将文本向量化之后,下一步就是进行向量的存储。 这部分包含两块:一是向量的存储。二是向量的查询。
官方提供了三种开源、免费的可用于本地机器的向量数据库示例(chroma、FAISS、 Lance)。因为我在之前RAG的文章中用的chroma数据库,所以这里还是以这个数据库为例。
安装:
python
复制代码
pip install chromadb
创建向量数据库,灌入数据from_documents
:
python
复制代码
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
db = Chroma.from_documents(paragraphs, OpenAIEmbeddings()) ## 一行代码搞定
查询similarity_search
:
python
复制代码
query = "什么是角色提示?"
docs = db.similarity_search(query) ## 一行代码搞定
for doc in docs:
print(f"{doc.page_content}\n-------\n")
运行结果:
它也接受传入一个向量来进行向量检索similarity_search_by_vector
。以下是代码示例。在以下代码中可能体现的价值不是很大,但是在实际项目中,如果出现只知道向量值,不知道具体文字的情况,这个接口就有用了。
python
复制代码
embedding_vector = OpenAIEmbeddings().embed_query(query)
docs = db.similarity_search_by_vector(embedding_vector)
print(docs[0].page_content)
5. Retrievers 检索器
检索器是在给定非结构化查询的情况下返回相关文本的接口。它比Vector stores更通用。检索器不需要能够存储文档,只需要返回(或检索)文档即可。Vector stores可以用作检索器的主干,但也有其他类型的检索器。检索器接受字符串查询作为输入,并返回文档列表作为输出。
LangChain检索器提供的检索类型如下:
名称 | 索引类型 | 使用LLM | 何时使用 | 描述 |
---|---|---|---|---|
Vectorstore | Vectorstore | 否 | 刚开始接触,想快速简单的入门 | 这是最简单的方法,也是最容易入门的方法。它涉及为每个文本片段创建向量。 |
ParentDocument | Vectorstore + 文档存储 | 否 | 如果您的页面有许多较小的独立信息片段,最好单独索引,但最好一起检索。 | 这涉及为每个文档索引多个片段。然后找到在嵌入空间中最相似的片段,但检索整个父文档并返回(而不是单个片段)。 |
Multi Vector | Vectorstore + 文档存储 | 有时在索引过程中 | 如果您能够从文档中提取比文本本身更相关的信息。 | 这涉及为每个文档创建多个向量。每个向量可以以多种方式创建 - 例如,文本摘要和假设性问题。 |
Self Query | Vectorstore | 是 | 如果用户提出的问题更适合根据元数据而不是文本相似性来获取文档回答。 | 这使用LLM将用户输入转换为两件事:(1)语义查找的字符串,(2)与之配套的元数据过滤器。这很有用,因为通常问题是关于文档的元数据(而不是内容本身)。 |
Contextual Compression | 任意 | 有时 | 如果您发现检索到的文档包含太多不相关信息,并且干扰了LLM。 | 这在另一个检索器之上放置了后处理步骤,并仅从检索到的文档中提取最相关的信息。这可以使用嵌入或LLM完成。 |
Time-Weighted Vectorstore | Vectorstore | 否 | 如果您的文档有时间戳,并且您想检索最近的文档。 | 这根据语义相似性(与常规向量检索相同)和时间权重(查看索引文档的时间戳)检索文档。 |
Multi-Query Retriever | 任意 | 是 | 如果用户提出的问题复杂,并且需要多个独立信息片段来回应。 | 这使用LLM从原始查询生成多个查询。当原始查询需要有关多个主题的信息片段才能正确回答时,这很有用。通过生成多个查询,我们可以为每个查询获取文档。 |
Ensemble | 任意 | 否 | 如果您有多个检索方法并希望尝试将它们组合起来。 | 这从多个检索器中获取文档,然后将它们组合在一起。 |
Long-Context Reorder | 任意 | 否 | 如果您使用长上下文模型,并且注意到它没有关注检索到的文档中的中间信息。 | 这从基础检索器中获取文档,然后重新排序文档,使最相似的文档靠近开头和结尾。这很有用,因为长上下文模型有时不关注上下文窗口中间的信息。 |
表中的每个检索类型都可以单独拿出来学习和研究。本文先大体了解都有哪些类型,以Vectorstore类型的检索为例简单使用(功能与Vectorstore中的查询一样):
python
复制代码
retriever = db.as_retriever()
docs = retriever.get_relevant_documents("什么是角色提示?")
for doc in docs:
print(f"{doc.page_content}\n-------\n")
as_retriever
:生成检索器实例get_relevant_documents
获取相关文本
as_retriever可指定使用的检索类型,同时也可指定一些其它参数,例如:
- 指定一个相似度阈值为0.5,只有相似度超过这个值才会召回
python
复制代码
retriever = db.as_retriever(
search_type="similarity_score_threshold", search_kwargs={"score_threshold": 0.5}
)
- 指定检索几个文本片段:topK
python
复制代码
retriever = db.as_retriever(search_kwargs={"k": 1})
6. Indexing
这块还没用到,先看下概念和作用。总的来说,这块是帮助用户省钱的,帮助用户减少重复的文档向量化和灌入,帮助用户定制化的删除一些冲突内容或无用内容。
6.1 概念和用途
索引API允许您将任何来源的文档加载到向量存储中并保持同步。具体来说,它有助于:
- 避免将重复的内容写入向量数据库
- 避免重写未更改的内容
- 避免在未更改的内容上重新计算向量
6.2 工作原理
LangChain索引使用记录管理器(RecordManager)来跟踪文档写入向量数据库的情况。为内容编制索引时,会为每个文档计算哈希值,并将以下信息存储在记录管理器中:
- 文档哈希(页面内容和元数据的哈希)
- 写入时间
- 源id – 每个文档都应该在其元数据中包含信息,以便我们确定该文档的最终来源
6.3 Deletion modes
该模块还提供了 Deletion modes。它的应用场景是:将文档索引到向量数据库时,可能会删除数据库中的一些现有文档。在某些情况下,您可能希望删除与正在编制索引的新文档来源相同的现有文档。在其他情况下,您可能希望批量删除所有现有文档。索引API删除模式帮助你进行自定义删除。
7. 总结,用LangChain实现RAG流程
请先阅读前置文章,了解RAG基本流程和LangChain的Prompt模板:
- 【AI大模型应用开发】3. RAG初探 - 动手实现一个最简单的RAG应用
- 【AI大模型应用开发】【LangChain系列】1. 全面学习LangChain输入输出I/O模块:理论介绍+实战示例+细节注释
以上,我们学习了LangChain数据连接模块的所有内容。下面,我们将所有流程串起来,用LangChain实现一下RAG流程。
python
复制代码
## 1. 文档加载
from langchain.document_loaders import PyPDFLoader
loader = PyPDFLoader("D:\GitHub\LEARN_LLM\RAG\如何向 ChatGPT 提问以获得高质量答案:提示技巧工程完全指南.pdf")
pages = loader.load_and_split()
## 2. 文档切分
from langchain.text_splitter import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=200,
chunk_overlap=100,
length_function=len,
add_start_index=True,
)
paragraphs = []
for page in pages:
paragraphs.extend(text_splitter.create_documents([page.page_content]))
## 3. 文档向量化,向量数据库存储
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
db = Chroma.from_documents(paragraphs, OpenAIEmbeddings())
## 4. 向量检索
# query = "什么是角色提示?"
# docs = db.similarity_search(query)
# for doc in docs:
# print(f"{doc.page_content}\n-------\n")
retriever = db.as_retriever()
docs = retriever.get_relevant_documents("什么是角色提示?")
for doc in docs:
print(f"{doc.page_content}\n-------\n")
## 5. 组装Prompt模板
import os
# 加载 .env 到环境变量
from dotenv import load_dotenv, find_dotenv
_ = load_dotenv(find_dotenv())
from langchain_openai import ChatOpenAI
llm = ChatOpenAI() # 默认是gpt-3.5-turbo
prompt_template = """
你是一个问答机器人。
你的任务是根据下述给定的已知信息回答用户问题。
确保你的回复完全依据下述已知信息。不要编造答案。
如果下述已知信息不足以回答用户的问题,请直接回复"我无法回答您的问题"。
已知信息:
{info}
用户问:
{question}
请用中文回答用户问题。
"""
from langchain.prompts import PromptTemplate
template = PromptTemplate.from_template(prompt_template)
prompt = template.format(info=docs[0].page_content, question='什么是角色提示?')
## 6. 调用LLM
response = llm.invoke(prompt)
print(response.content)
运行结果:
如何学习大模型 AI ?
由于新岗位的生产效率,要优于被取代岗位的生产效率,所以实际上整个社会的生产效率是提升的。
但是具体到个人,只能说是:
“最先掌握AI的人,将会比较晚掌握AI的人有竞争优势”。
这句话,放在计算机、互联网、移动互联网的开局时期,都是一样的道理。
我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。
我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。
😝有需要的小伙伴,可以点击下方链接免费领取或者V扫描下方二维码免费领取🆓
第一阶段(10天):初阶应用
该阶段让大家对大模型 AI有一个最前沿的认识,对大模型 AI 的理解超过 95% 的人,可以在相关讨论时发表高级、不跟风、又接地气的见解,别人只会和 AI 聊天,而你能调教 AI,并能用代码将大模型和业务衔接。
- 大模型 AI 能干什么?
- 大模型是怎样获得「智能」的?
- 用好 AI 的核心心法
- 大模型应用业务架构
- 大模型应用技术架构
- 代码示例:向 GPT-3.5 灌入新知识
- 提示工程的意义和核心思想
- Prompt 典型构成
- 指令调优方法论
- 思维链和思维树
- Prompt 攻击和防范
- …
第二阶段(30天):高阶应用
该阶段我们正式进入大模型 AI 进阶实战学习,学会构造私有知识库,扩展 AI 的能力。快速开发一个完整的基于 agent 对话机器人。掌握功能最强的大模型开发框架,抓住最新的技术进展,适合 Python 和 JavaScript 程序员。
- 为什么要做 RAG
- 搭建一个简单的 ChatPDF
- 检索的基础概念
- 什么是向量表示(Embeddings)
- 向量数据库与向量检索
- 基于向量检索的 RAG
- 搭建 RAG 系统的扩展知识
- 混合检索与 RAG-Fusion 简介
- 向量模型本地部署
- …
第三阶段(30天):模型训练
恭喜你,如果学到这里,你基本可以找到一份大模型 AI相关的工作,自己也能训练 GPT 了!通过微调,训练自己的垂直大模型,能独立训练开源多模态大模型,掌握更多技术方案。
到此为止,大概2个月的时间。你已经成为了一名“AI小子”。那么你还想往下探索吗?
- 为什么要做 RAG
- 什么是模型
- 什么是模型训练
- 求解器 & 损失函数简介
- 小实验2:手写一个简单的神经网络并训练它
- 什么是训练/预训练/微调/轻量化微调
- Transformer结构简介
- 轻量化微调
- 实验数据集的构建
- …
第四阶段(20天):商业闭环
对全球大模型从性能、吞吐量、成本等方面有一定的认知,可以在云端和本地等多种环境下部署大模型,找到适合自己的项目/创业方向,做一名被 AI 武装的产品经理。
- 硬件选型
- 带你了解全球大模型
- 使用国产大模型服务
- 搭建 OpenAI 代理
- 热身:基于阿里云 PAI 部署 Stable Diffusion
- 在本地计算机运行大模型
- 大模型的私有化部署
- 基于 vLLM 部署大模型
- 案例:如何优雅地在阿里云私有部署开源大模型
- 部署一套开源 LLM 项目
- 内容安全
- 互联网信息服务算法备案
- …
学习是一个过程,只要学习就会有挑战。天道酬勤,你越努力,就会成为越优秀的自己。
如果你能在15天内完成所有的任务,那你堪称天才。然而,如果你能完成 60-70% 的内容,你就已经开始具备成为一名大模型 AI 的正确特征了。
这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费
】
😝有需要的小伙伴,可以点击下方链接免费领取或者V扫描下方二维码免费领取🆓