RAG From Scratch: Part 3 (Retrieval) by LangChain
检索(Retrieval)
一、核心概念
1. RAG系统与检索环节
- RAG(检索增强生成):通过“检索相关文档+生成答案”的组合,提升生成内容的准确性和相关性,核心流程为:索引文档 → 检索相关文档 → 基于文档生成答案。
- 检索的目标:从索引的文档中,找出与用户问题语义最相关的文档或文档片段,为后续生成环节提供依据。
2. 关键术语
- 嵌入(Embedding):将文本转换为固定长度的数值向量(如1536维),向量的数值反映文本的语义信息。
- 向量空间:文档和问题通过嵌入模型映射到高维空间(如3D、1536D),空间中距离越近的向量,语义相似性越高。
- 相似性搜索:在向量空间中,通过计算向量距离(如余弦相似度),查找与问题向量最接近的文档向量。
二、检索流程解析
1. 文档处理全流程
- 文档加载与拆分:
- 输入原始文档(如PDF、文本),使用分割器(Splitter)将长文档拆分为适合嵌入模型处理的小块(如512-8000 Token)。
- 示例:将一本书拆分为多个章节段落,每个段落作为一个独立的“文档拆分”。
- 嵌入与索引构建:
- 对每个文档拆分生成嵌入向量,存储到向量存储(如Chroma、FAISS)中,形成可检索的“索引”。
- 问题嵌入与检索:
- 将用户问题转换为嵌入向量,在向量空间中执行相似性搜索,返回与问题最相关的Top-K个文档拆分。
- 将用户问题转换为嵌入向量,在向量空间中执行相似性搜索,返回与问题最相关的Top-K个文档拆分。
2. 向量空间的语义映射原理
- 低维示例(3D空间):
- 每个文档对应3D空间中的一个点,位置由语义决定(如“猫”和“狗”的向量点靠近,“汽车”的点远离)。
- 问题嵌入后成为空间中的另一个点,通过“本地邻域搜索”查找附近的文档点(即语义相似的文档)。
- 高维应用:
- 实际场景中使用高维向量(如1536D),通过数学度量(如余弦相似度)计算向量间的“距离”,距离越近,语义越相关。
三、技术原理:相似性搜索的核心逻辑
1. 核心思想
- 语义相关性 = 向量空间距离:利用嵌入模型将文本语义转化为向量数值,通过向量距离量化语义相似性。
2. 关键方法
- k最近邻搜索(k-NN):
- 参数 K 表示检索时返回的最相关文档数量(如K=1表示只返回最相似的1个文档)。
- 示例:在3D空间中,以问题向量为中心,查找距离最近的K个文档向量。
- 实现工具:
- 向量存储库:FAISS(高效处理大规模向量)、Chroma(轻量级,适合原型开发)。
- 嵌入模型:OpenAI Embeddings、Hugging Face Embeddings(支持多语言)。
四、实践要点
1. 工具与组件集成
- 文档加载器:支持多种格式(如PDFLoader、TextLoader),适配不同数据源。
- 分割器:递归字符分割器(按字符数拆分)、NLTK分割器(按句子拆分),需根据文档结构选择。
- 向量存储选择:小规模数据用Chroma(内存存储),大规模数据用FAISS(支持GPU加速)。
2. 参数K的作用
- K值影响:
- K过小(如K=1):可能遗漏相关文档,导致信息不足;
- K过大:返回无关文档,增加生成环节的噪声。
- 调优建议:根据问题复杂度和文档相关性,通过实验确定最优K值(如K=3-5)。
3. 监控与调试
- 使用 LangSmith 工具跟踪检索结果:
- 查看检索到的文档是否与问题相关;
- 分析嵌入向量的分布和相似性计算是否正确。
五、代码演示要点:检索核心步骤
- 环境设置与依赖
- 文档加载与拆分
- 构建索引与检索器
- 执行检索
六、总结
- 检索的核心价值:通过向量空间的语义匹配,将用户问题与文档建立关联,是RAG系统“准确性”的重要保障。
- 后续环节:检索得到的文档将作为生成模型的输入,结合prompt工程生成最终答案(详见系列视频“生成”部分)。
- 实践关键:合理选择嵌入模型、分割策略和向量存储,通过参数调优(如K值)平衡检索精度与效率。
七、视频原文
第二页:这是我们系列视频从头开始制作RAG的第三部,从最基本的组件开始构建RAG的很多动机,所以今天我们将讨论检索,在最后两个简短的视频中,我概述了索引,并概述了此流程,从索引我们的文档开始,检索与我们的问题相关的文档,然后根据检索到的文档生成答案,因此我们看到索引过程基本上使用文档易于检索,
第三页:他经过一个流程,基本上看起来像您获取我们的文档,将他们以某种方式拆分成可以轻松嵌入的较小块,这些嵌入是这些文档的数字表示,易于检索,他们存储在索引中,当给定一个嵌入的问题时,索引会执行相似性搜索并返回与问题相关的拆分,
第四页: 现在如果我们深入挖掘,我们可以这样思考,如果我们获取一个文档,并将其嵌入,让我们想象一下嵌入只有三个维度,所以你知道每个文档都会被投影到这个3D空间中的某个点,现在的重点是空间中的位置由该文档中的语义含义或内容决定,因此,空间中相似位置的文档包含相似的语义信息,这个简单的想法实际上是许多搜索和检索方法的基石,你会在现代向量存储中看到这些方法,因此,具体来说我们将文档,嵌入到这个玩具3D空间中,我们将问题做同样的事情,然后我们可以进行搜索,比如本地邻域搜索,你可以在这个3D空间中围绕我们的问题思考,看看附近有哪些文档, 然后检索这些附近的邻居,因为他们可能与我们的问题具有相似的语义
第五页:这就是这里真正发生的事情,所以我们再次拿出我们的文档,将他们拆分,嵌入他们现在他们存储于这个高维空间中,我们已经将问题嵌入到同一个空间中,我们只需要围绕问题搜索附近的文档,并抓取接近的文档,我们可以选择一些数字,假设我们想要一个、两个、三个或n个文档,它们靠近这个嵌入空间中的问题,有很多有趣的方法可以非常有效地实现这一点,我在这里链接了一个,
第六页:我们有很多非常好的集成来实现这个总体思路,有很多不同的嵌入模型,很多不同的索引,很多文档加载器,还有很多分割器,它们可以重新组会,以测试执行这种索引或检索的不同方式,所以现在我将展现一些代码演练
第七页:在这里 我们定义了我们之前已经演练过了,这是我们的笔记本,我们安装了一些安装包,通过LMsmith设置了一些环境变量,我们之前展示了这个,这只是一个概述,展示了如何以端到端的方式运行rag,在上一个简短的演讲中,我们讨论了indexing,我要做的很简单,就是加载我们的文档,现在我有了我们的文档,我要重新分割它们,我们之前看到了如何构建索引,这里我们来做同样的事情,但在幻灯片中我们实际上展示了在3D空间中搜索的概念,在构建检索器时需要考虑的一个参数时K,所以K会告诉您在执行检索过程时要获取的附近邻居的数量,我们讨论了在3D空间中我想要一个附近的邻居还是两个或三个,所以在这里我们可以指定k等于1,例如,现在我们正在构建索引,所以我们将每个分割嵌入存储,它现在我问了一个问题,什么是任务分解,这与博客文章有关,我将运行获取相关文档,所以我运行他,现在我得到了多少个文档,根据K=1,我得到了一个,所以这个检索文档,应该与我的问题相关,现在我们可以去LangSmith,我们可以打开它,我们可以看看我们的检索器retriever。我们可以看到这是我们的问题,这是我们得到的一个文档,好的,这很有意义,这个文档特别涉及任务分解,它列出了许多可用于实现这一点的不同方法,这些方法都很有意义,并且在实践中展示了如何非常轻松地实现这种NE KNN或k最近邻搜索,只需要使用几行代码,接下来我们将讨论生成。