参考链接:https://www.bilibili.com/video/BV1PB6XYFET2?spm_id_from=333.788.videopod.sections&vd_source=e01172ea292c1c605b346101d7006c61、https://github.com/ZJU-LLMs/Foundations-of-LLMs/tree/main
# 一、语言模型基础
语言是概率的,并且语言的概率性与认知的概率性也存在着密不可分的关系。语言模型(LanguageModels, LMs)旨在准确预测语言符号的概率。从 ELIZA 到 GPT-4,语言模型经历了从规则模型到统计模型,再到神经网络模型的发展历程,逐步从呆板的机械式问答程序成长为具有强大泛化能力的多任务智能模型。
# 1.1 基于统计方法的语言模型
基于统计的语言模型通过直接统计语言符号在语料库中出现的频率来预测语言符号的概率。其中,n-grams 是最具代表性的统计语言模型。n-grams 语言模型基于马尔可夫假设和离散变量的极大似然估计给出语言符号的概率。
# 1.1.1 n-grams 语言模型
n-grams 语言模型中的 n-gram 指的是长度为 的词序列。n-grams 语言模型通过依次统计文本中的 n-gram 及其对应的 (n-1)-gram 在语料库中出现的相对频率来计算文本 出现的概率。计算公式如下所示:
通俗可理解为前 n 个词出现的情况下,将后一个词在语料库中出现的概率作为预测概率。详细的解释可参考 github。
为变量,当 时,称之为 unigram,其不考虑文本的上下文关系。当 时,称之为 bigrams,其对前一个词进行考虑。当 时,称之为 trigrams,其对前两个词进行考虑。当 时,称之为 4-grams,其对前三个词进行考虑…
n-grams 具备对未知文本的泛化能力。这也是其相较于传统基于规则的方法的优势。但是,这种泛化能力会随着 n 的增大而逐渐减弱。因此,在 n-grams 语言模型中,n 代表了拟合语料库的能力与对未知文本的泛化能力之间的权衡。当 n 过大时,语料库中难以找到与 n-gram 一模一样的词序列,可能出现大量 “零概率” 现象;在 n 过小时,n-gram 难以承载足够的语言信息,不足以反应语料库的特性。因此,在 n-grams 语言模型中,n 的值是影响性能的关键因素。
# 1.1.2 n-grams 的统计学原理
n-grams 语言模型是在 n 阶马尔可夫假设下,对语料库中出现的长度为 n 的词序列出现概率的极大似然估计。 详细证明见 github。
# 1.2 基于 RNN 的语言模型
n-grams 语言模型对未知序列有一定的泛化性,但也容易陷入 “零概率” 的困境。基于神经网络的语言模型不再通过显性的计算公式对语言符号的概率进行计算,而是利用语料库中的样本对神经网络模型进行训练。
循环神经网络(RecurrentNeuralNetwork,RNN)可以基于历史规律,对未来进行预测。基于 RNN 的语言模型,以词序列作为输入,基于被循环编码的上文和当前词来预测下一个词出现的概率。
# 1.2.1 循环神经网络 RNN
RNN 可以将历史状态以隐变量的形式循环叠加到当前状态上,对历史信息进行考虑。但是这样的环路结构给 RNN 的训练带来了挑战。在训练 RNN 时,涉及大量的矩阵联乘操作,容易引发梯度衰减或梯度爆炸问题。为了解决梯度消失和爆炸问题,GRU 和 LSTM 引入门控结构,取得了良好效果,成为主流的 RNN 网络架构。
# 1.2.2 基于 RNN 的语言模型
对词序列,基于 RNN 的语言模型每次根据当前词 和循环输入的隐藏状态,来预测下一个词 出现的概率,即
在 “自回归” 的范式下完成文本生成任务,第一轮,首先将第一个词输入给 RNN 语言模型,经过解码,得到一个输出词。然后,将第一轮输出的词与第一轮输入的词拼接,作为第二轮的输入,然后解码得到第二轮的输出。接着,将第二轮的输出和输入拼接,作为第三轮的输入,以此类推。在循环迭代的 “自回归” 过程中,不断生成新的词,这些词便构成了一段文本。
但上述 “自回归” 过程存在着两个问题:(1)错误级联放大,选用模型自己生成的词作为输入可能会有错误,这样的错误循环输入,将会不断的放大错误,导致模型不能很好拟合训练集;(2)串行计算效率低,因为下一个要预测的词依赖上一次的预测,每次预测之间是串行的,难以进行并行加速。为了解决上述两个问题,“Teacher Forcing” 在语言模型预训练过程中被广泛应用。在 Teacher Forcing 中,每轮都仅将输出结果与 “标准答案”(GroundTruth)进行拼接作为下一轮的输入。如例子中,第二轮循环中,用 “长颈鹿脖子” 来预测下一个词 “长”,而非选用 o1 中概率最高的词 “吃” 或者其他可能输出的词。
但是,Teacher Forcing 的训练方式将导致曝光偏差(Exposure Bias)的问题。曝光偏差是指 Teacher Forcing 训练模型的过程和模型在推理过程存在差异。Teacher Forcing 在训练中,模型将依赖于 “标准答案” 进行下一次的预测,但是在推理预测中,模型 “自回归” 的产生文本,没有 “标准答案” 可参考。所以模型在训练过程中和推理过程中存在偏差,可能推理效果较差。为解决曝光偏差的问题,Bengio 等人提出了针对 RNN 提出了 Scheduled Sampling 方法。其在 Teacher Forcing 的训练过程中循序渐进的使用一小部分模型自己生成的词代替 “标准答案”,在训练过程中对推理中无 “标准答案” 的情况进行预演。
# 1.3 基于 Transformer 的语言模型
Transformer 是一类基于注意力机制(Attention)的模块化构建的神经网络结构。
# 1.3.1 Transformer
Transformer 是由两种模块组合构建的模块化网络结构。两种模块分别为:注意力(Attention)模块和全连接前馈 Fully-connectedFeedforwad)模块。
- 注意力层(AttentionLayer)
具体内容参考 github。
- 全连接前馈层(Fully-connected Feedforwad Layer)
全连接前馈层占据了 Tranformer 近三分之二的参数,掌管着 Tranformer 模型的记忆。其可以看作是一种 Key-Value 模式的记忆存储管理模块 [7]。全连接前馈层包含两层,两层之间由 ReLU 作为激活函数。设全连接前馈层的输入为, 全连接前馈层可由下式表示:
其中, 和 分别为第一层和第二层的权重参数, 和 分别为第一层和第二层的偏置参数。其中第一层的可看作神经记忆中的 key,而第二层可看作 value。
- 层正则化(LayerNormalization)
层正则化用以加速神经网络训练过程并取得更好的泛化性能。 具体内容参考 github。
- 残差连接(ResidualConnections)
引入残差连接可以有效解决梯度消失问题。在基本的 Transformer 编码模块中包含两个残差连接。 第一个残差连接是将自注意力层的输入由一条旁路叠加到自注意力层的输出上,然后输入给层正则化。第二个残差连接是将全连接前馈层的输入由一条旁路引到全连接前馈层的输出上,然后输入给层正则化。
上述将层正则化置于残差连接之后的网络结构被称为 Post-LN Transformer。与之相对的,还有一种将层正则化置于残差连接之前的网络结构,称之为 Pre-LN Transformers。对比两者,Post-LN Transformer 应对表征坍塌(Representation Collapse)的能力更强,但处理梯度消失略弱。而 Pre-LN Transformers 可以更好的应对梯度消失,但处理表征坍塌的能力略弱。
# 1.3.2 基于 Transformer 的语言模型
相较于 RNN 模型串行的循环迭代模式,Transformer 并行输入的特性,使其容易进行并行计算。但是,Transformer 并行输入的范式也导致网络模型的规模随输入序列长度的增长而平方次增长。这为应用 Transformer 处理长序列带来挑战。
# 1.4 语言模型的采样方法
语言模型的输出为一个向量,该向量的每一维代表着词典中对应词的概率。当前,两类主流的解码方法可以总结为 (1). 概率最大化方法;(2). 随机采样方法。
# 1.4.1 概率最大化方法
设词典大小为,输入文本为,模型在 轮自回归后生成的文本为。生成文档的出现的概率可由下式进行计算。
基于概率最大化的解码方法旨在最大化,以生成出可能性最高的文本。该问题的搜索空间大小为,是 NP-Hard 问题。现有概率最大化方法通常采用启发式搜索方法。
- 贪心搜索
贪心搜索在在每轮预测中都选择概率最大的词。贪心搜索只顾 “眼前利益”,忽略了 “远期效益”。当前概率大的词有可能导致后续的词概率都很小。贪心搜索容易陷入局部最优,难以达到全局最优解。
- 波束搜索(BeamSearch)
波束搜索在每轮预测中都先保留 个可能性最高的词。模型在结束 轮的推理后可以得到一个推理结果集合,从该集合中找出使 最大的预测结果。
# 1.4.2 随机采样方法
概率最大的文本通常是最为常见的文本,其所生成的文本缺乏多样性。为了增加生成文本的多样性,随机采样的方法在预测时增加了随机性。在每轮预测时,其先选出一组可能性高的候选词,然后按照其概率分布进行随机采样,采样出的词作为本轮的预测结果。当前,主流的 Top-K 采样和 Top-P 采样方法分别通过指定候选词数量和划定候选词概率阈值的方法对候选词进行选择。在采样方法中加入 Temperature 机制可以对候选词的概率分布进行调整。
# 1.5 语言模型的评测
评测语言模型生成能力的方法可以分为两类。第一类方法不依赖具体任务,直接通过语言模型的输出来评测模型的生成能力,称之为内在评测(IntrinsicEvaluation)。第二类方法通过某些具体任务,如机器翻译、摘要生成等,来评测语言模型处理这些具体生成任务的能力,称之为外在评测(ExtrinsicEvaluation)。
# 1.5.1 内在评测
在内在评测中,测试文本通常由与预训练中所用的文本独立同分布的文本构成,不依赖于具体任务。最为常用的内部评测指标是困惑度(Perplexity)。其度量了语言模型对测试文本感到 “困惑” 的程度。具体内容参考 github。
# 1.5.2 外在评测
在外在评测中,测试文本通常包括该任务上的问题和对应的标准答案,其依赖于具体任务。 外在评测方法通常可以分为基于统计指标的评测方法和基于语言模型的评测方法两类。
- 基于统计指标的评测
基于统计指标的方法构造统计指标来评测语言模型的输出与标准答案间的契合程度,并以此作为评测语言模型生成能力的依据。BLEU(BiLingualEvaluation Understudy)和 ROUGE(Recall-Oriented Understudy for Gisting Evaluation)是应用最为广泛的两种统计指标。其中,BLEU 是精度导向的指标,而 ROUGE 是召回导向的指标。具体内容参考 github。
- 基于语言模型的评测
目前基于语言模型的评测方法主要分为两类:(1)基于上下文词嵌入(Contextual Embeddings)的评测方法;(2)基于生成模型的评测方法。典型的基于上下文词嵌入的评测方法是 BERTScore [24]。典型的基于生成模型的评测方法是 G-EVAL。与 BERTScore 相比,G-EVAL 无需人类标注的参考答案。这使其可以更好的适应到缺乏人类标注的任务中。
# 六、检索增强生成
由于训练数据的正确性、时效性和完备性可能存在不足,其难以完全覆盖用户的需求。同时,由于参数空间有限,大语言模型对训练数据的学习也难以达到完美。上述训练数据和参数学习上的不足将导致:大语言模型在面对某些问题时无法给出正确答案,甚至出现 “幻觉”,即生成看似合理实则逻辑混乱或违背事实的回答。
为了解决这些问题并进一步提升大语言模型的生成质量,可以将相关信息存储在外部数据库中,供大语言模型进行检索和调用。这种从外部数据库中检索出相关信息来辅助改善大语言模型生成质量的系统被称之为检索增强生成(Retrieval-AugmentedGeneration,RAG)。
# 6.1 检索增强生成简介
检索增强生成(RAG)旨在通过检索和整合外部知识来增强大语言模型生成文本的准确性和丰富性,其是一个集成了外部知识库、信息检索器、大语言模型等多个功能模块的系统。RAG 利用信息检索、深度学习等多种技术为大语言模型在生成过程中引入最新的、特定领域的知识,从而克服传统大语言模型的局限性,提供更加精准和可靠的生成内容。
# 6.1.1 检索增强生成的组成
RAG 通常集成了外部知识库(Corpus)、信息检索器(Retriever)、生成器(Generator,即大语言模型)等多个功能模块。其基本架构如下图所示。
具体而言,给定一个自然语言问题(Query),检索器将问题进行编码,并从知识库(如维基百科)中高效检索出与问题相关的文档。然后,将检索到的知识和原始问题一并传递给大语言模型,大语言模型根据检索到的知识和原始问题生成最终的输出。
RAG 的核心优势在于不需要对大语言模型的内部知识进行更新(即不需要重新训练),便可改善大语言模型的幻觉现象,提高生成质量。这可以有效避免内部知识更新带来的计算成本和对旧知识的灾难性遗忘(Catastrophic Forgetting)。
# 6.2 检索增强生成架构
# 6.2.1 RAG 架构分类
针对不同的业务场景,RAG 中的生成器可以选用不同的大语言模型,考虑到大语言模型的开源 / 闭源、微调成本等问题,RAG 中的大语言模型可以是参数不可感知 / 调节的 “黑盒” 模型,也可以是参数可感知和微调的 “白盒” 模型。从是否对大语言模型进行微调的角度出发,本小节将 RAG 架构分类两大类:黑盒增强架构和白盒增强架构。
黑盒增强架构可根据是否对检索器进行微调分为两类:无微调、检索器微调。白盒增强架构也可根据是否对检索器进行微调分为两类:仅微调大语言模型、检索器与大语言模型协同微调。
# 6.2.2 黑盒增强架构
- 无微调
无微调架构是所有 RAG 架构中形式最简单的。该架构中,检索器和语言模型经过分别独立的预训练后参数不再更新,直接组合使用。In-Context RALM 是该框架下的代表性方法。
- 检索器微调
虽然无微调架构在实现和部署上非常便捷,但它完全没有考虑检索器与语言模型之间潜在的协同效应,效果有待提升。为了进一步提升效果,可以采用检索器微调架构对检索器进行微调,以更好地适用于黑盒增强的环境。在检索器微调架构中,大语言模型的参数保持不变,仅用其输出指导检索器的微调。REPLUG LSR 是检索器微调框架的代表性方法。其微调检索器的过程中采用 KL 散度损失函数来训练检索器,目的是对齐检索到的文档的相关性分布与这些文档对语言模型性能提升的贡献分布。
# 6.2.3 白盒增强架构
- 仅微调语言模型
仅微调语言模型指的是检索器作为一个预先训练好的组件其参数保持不变,大语言模型根据检索器提供的上下文信息,对自身参数进行微调。RETRO 是仅
微调语言模型的代表性方法之一。
- 检索器和语言模型协同微调
在检索器和语言模型协同微调的架构中,检索器和语言模型的参数更新同步进行。这种微调的方式使得检索器能够在检索的同时学习如何更有效地支持语言模型的需求,而语言模型则可以更好地适应并利用检索到的信息,以进一步提升 RAG 的性能。
Atlas 是该架构的代表性工作,其架构如下图所示。与 REPLUGLSR 类
似,其在预训练和微调阶段使用 KL 散度损失函数来联合训练检索器和语言模型,
以确保检索器输出的文档相关性分布与文档对语言模型的贡献分布相一致。不同
之处在于,Atlas 在预训练和微调过程中,检索器和语言模型参数同步被更新,检
索器学习向语言模型提供最相关的文档,而语言模型则学习如何利用这些文档来
改善其对查询的响应。为了确保检索结果与模型最新状态保持同步,Atlas 同样需
要定期更新语料库文档的向量编码,从而维持检索的准确性。
# 6.2.4 对比与分析
# 6.3 知识检索
在 RAG 中,检索的效果(召回率、精度、多样性等)会直接影响大语言模型的生成质量。此外,检索的时间也是 RAG 总耗时的关键部分,因此检索的效率将影响用户的使用体验。优化检索过程,提升检索的效果和效率,对改善 RAG 的性能具有重要意义。
# 6.3.1 知识库构建
在 RAG 框架中,知识库构建主要涉及数据采集及预处理与知识库增强两个步骤。
- 数据采集及预处理
在构建文本型知识库的数据采集过程中,来自不同渠道的数据被整合、转换为统一的文档对象。这些文档对象不仅包含原始的文本信息,还携带有关文档的元信息(Metadata),例如文章标题,分类信息,时间信息,关键词等。元信息可以用于后续的检索和过滤。
在采集到相应的数据后,还需通过数据预处理来提升数据质量和可用性。在构建文本型知识库时,数据预处理主要包括数据清洗和文本分块两个过程。
文本分块的效果直接影响后续检索结果的质量。制定合适的分块策略至关重要,包括确定切分方法(如按句子或段落切分)、设定块大小,以及是否允许块之间有重叠。文本分块的具体实施流程通常开始于将长文本拆解为较小的语义单元,如句子或段落。随后,这些单元被逐步组合成更大的块,直到达到预设的块大小,构建出独立的文本片段。为了保持语义连贯性,通常还会在相邻的文本片段之间设置一定的重叠区域。
- 知识库增强
知识库增强是通过改进和丰富知识库的内容和结构,以提升其质量和实用性。这一过程通常涉及查询生成与标题生成等多个步骤,以此为文档建立语义 “锚点”,方便检索时准确定位到相应文本。
查询生成指的是利用大语言模型生成与文档内容紧密相关的伪查询。这些伪查询从查询的角度来表达文档的语义,可以作为相关文档的 “键”,供检索时与用户查询进行匹配。
标题生成指的是利用大语言模型为没有标题的文档生成合适的标题。这些生成的标题提供了文档的关键词和上下文信息,能来用来帮助快速理解文档内容,并在检索时更准确地定位到与用户提问相关的信息。对于那些原始文档中缺乏标题的情况,通过语言模型生成标题显得尤为重要。
# 6.3.2 查询增强
知识库涵盖的知识表达形式是有限的,但用户的提问方式却是千人千面的。这可能导致用户查询和知识库之间不能很好匹配,从而降低检索效果。为了解决此问题,可以对用户查询的语义和内容进行扩展,即查询增强,以更好的匹配知识库中的文本。
- 查询语义增强
查询语义增强旨在通过同义改写和多视角分解等方法来扩展、丰富用户查询的语义,以提高检索的准确性和全面性。
- (1) 同义改写
同义改写通过将原始查询改写成相同语义下不同的表达方式,来解决用户查
询单一的表达形式可能无法全面覆盖到知识库中多样化表达的知识。改写工作可以调用大语言模型完成。
- (2) 多视角分解
多视角分解采用分而治之的方法来处理复杂查询,将复杂查询分解为来自不同视角的子查询,以检索到查询相关的不同角度的信息。
- 查询内容增强
查询内容增强旨在通过生成与原始查询相关的背景信息和上下文,从而丰富查询内容,提高检索的准确性和全面性。生成背景文档是一种查询内容增强的方法。它指的是在原始查询的基础上,利用大语言模型生成与查询内容相关的背景文档。
# 6.3.3 检索器
给定知识库和用户查询,检索器旨在找到知识库中与用户查询相关的知识文本。检索器可分为判别式检索器和生成式检索器两类。
- 判别式检索器
判别式检索器通过判别模型对查询和文档是否相关进行打分。判别式检索器通常分为两大类:稀疏检索器和稠密检索器。稀疏检索器利用离散的、基于词频的文档编码向量进行检索,而稠密检索器则利用神经网络生成的连续的、稠密向量对文档进行检索。
- (1) 稀疏检索器
稀疏检索器(SparseRetriever)是指使用稀疏表示方法来匹配文本的模型。这类检索器通过统计文档中特定词项出现的统计特征来对文档进行编码,然后基于此编码计算查询与知识库中的文档的相似度来进行检索。典型的稀疏检索技术包括 TF-IDF 和 BM25 等,它们通过分析词项的分布和频率来评估文档与查询的相关性。
TF-IDF 基于词频(TF)和逆文档频率(IDF)来衡量词语在文档或语料库中的重要性,然后用此重要性对文本进行编码。词频(TF)表示词语在文档中的出现频率,计算公式为:
逆文档频率(IDF)衡量词语的普遍性,计算公式为:
最终,TF-IDF 值为:
TF-IDF 通过高词频和低文档频率产生高权重,倾向于过滤常见词语,保留重要词语。BM25 是一种改进的文本检索算法,它在 TF-IDF 基础上通过文档长度归一化和词项饱和度调整,更精确地评估词项重要性,优化了词频和逆文档频率的计算,并考虑了文档长度对评分的影响。虽然不涉及词项上下文,但是 BM25 在处理大规模数据时表现优异,广泛应用于搜索引擎和信息检索系统。
- (2) 稠密检索器
稠密检索器一般利用预训练语言模型对文本生成低维、密集的向量表示,通
过计算向量间的相似度进行检索。按照所使用的模型结构的不同,稠密检索器大
致可以分为两类:交叉编码类(Cross-Encoder)、双编码器类(Bi-Encoder)
交叉编码类 “端到端” 的给出查询和文档的相似度。这类模型将查询和文档拼接在一起,随后利用预训练语言模型作为编码器(例如 BERT)生成一个向量表示。接着,通过一个分类器处理这个向量,最终输出一个介于 0 和 1 之间的数值,表示输入的查询和文档之间的相似程度。其优点在于模型结构简单,能够实现查询和文档之间的深度交互。然而,由于交叉编码类模型需要进行高复杂度的交叉注意力操作,计算量大,因此不适合在大规模检索阶段使用。这种模型更适用于对少量候选文档进行更精确排序的阶段,可以显著提升检索结果的相关性。
双编码类模型采用了一种 “两步走” 的策略。第一步,查询和文档首先各自通过独立的编码器生成各自的向量表示;第二步,对这两个向量之间的相似度进行计算,以评估它们的相关性。这种方法的优势在于,它允许预先离线计算并存储所有文档的向量表示,在线检索时则可直接进行向量匹配。因此,双编码器非常适合在工业环境中部署,具有极高的匹配效率。然而,在这种分离的处理方式中,查询与文档在提取特征向量时缺乏交互。这可能会对匹配的精确度产生影响。为了缓解查询与文档在提取特征向量时缺乏交互的问题,可以在双编码器的基础上引入查询与文档的交互,通过对比学习对双编码器进行微调,以使双编码器编码的特征向量可以兼顾查询和文档,以进一步提升双编码器的效果。
- 生成式检索器
生成式检索器通过生成模型对输入查询直接生成相关文档的标识符。生成式检索器通常采用基于 Encoder-Decoder 架构的生成模型,如 T5、BART 等。生成式检索器的训练过程通常分为两个阶段。在第一阶段,模型通过序列到序列的学习方法,学习如何将查询映射到相关的文档标识符。这一阶段主要通过最大似然估计(MLE)来优化模型,确保生成的文档标识符尽可能准确。在第二阶段,通过数据增强和排名优化进一步提高检索效率和准确性。数据增强主要通过生成伪查询或使用文档片段作为查询输入,以增加训练数据的多样性和覆盖面。排名优化则涉及使用特定的损失函数,如对比损失或排名损失,来调整模型生成文档标识符的顺序和相关性,从而更好地匹配查询的需求
在生成式检索器中,DocID 的设计至关重要。其需要在语义信息的丰富性与标识符的简洁性之间取得平衡。常用的 DocID 形式分为两类:基于数字的 DocID 和基于词的 DocID。基于数字的 DocID 方法使用唯一的数字值或整数字符串来表示文档,虽然构建简单,但在处理大量文档时可能导致标识符数量激增,增加计算和存储负担。相比之下,基于词的 DocID 方法直接从文档的标题、URL 或 N-gram 中提取表示,能更自然地传达文档的语义信息。通常,标题是最佳选择,因为它提供了文档的宏观概述。但在缺乏高质量标题时,URL 或 N-gram 也可作为有效的替代方案。
尽管生成式检索器在性能上取得了一定的进步,但与稠密检索器相比,其效果仍稍逊一筹。此外,生成式检索器还面临着一系列挑战,包括如何突破模型输入长度的限制、如何有效处理大规模文档以及动态新增文档的表示学习等,这些都是亟待解决的问题。
# 6.3.4 检索效率增强
知识库中通常包含海量的文本,对知识库中文本进行逐一检索缓慢而低效。为提升检索效率,可以引入向量数据库来实现检索中的高效向量存储和查询。向量数据库的核心是设计高效的相似度索引算法。
- 相似度索引算法
在向量检索中,常用的索引技术主要分成三大类:基于空间划分的方法、基于量化方法和基于图的方法。
基于空间划分的方法将搜索空间划分为多个区域来实现索引,主要包括基于树的索引方法和基于哈希的方法两类。其中,基于树的索引方法通过一系列规则递归地划分空间,形成一种树状结构,每个叶节点代表一个较小区域,区域内的数据点彼此接近。在查询时,算法从树的根节点出发,逐步深入到合适的叶节点,最后在叶节点内部进行数据点的相似度比较,以找到最近的向量。常见的基于树的索引包括 KD 树 [4] 和 Ball 树 [13] 等。而基于哈希的方法(如局部敏感哈希(LSH)[11])通过哈希函数将向量映射到哈希表的不同桶中,使得相似向量通常位于同一桶内。
基于图的方法通过构建一个邻近图,将向量检索转化为图的遍历问题。这类方法在索引构建阶段,将数据集中的每个向量表示为图中的一个节点,并根据向量间的距离或相似性建立边的连接。不同的图索引结构主要体现在其独特的赋边策略上。索引构建的核心思想源于小世界网络模型,旨在创建一个结构,使得从任意入口点出发,能在较少步数内到达查询点的最近邻。这种结构允许在搜索时使用贪婪算法,逐步逼近目标。然而,图索引设计面临稀疏性和稠密性的权衡:较稀疏的图结构每步计算代价低,而较稠密的图则可能缩短搜索路径。基于图的代表性方法有 NSW [32]、IPNSW [37] 和 HNSW [31] 等。
基于乘积量化的方法通过将高维向量空间划分为多个子空间,并在每个子空间中进行聚类得到码本和码字,以此作为构建索引的基础 [18]。这类方法主要包括训练和查询两个阶段。在训练阶段,该方法学习如何将高维向量最优地量化为码字 ID 序列。通常这个过程涉及将原始空间划分为多个子空间,在每个子空间内进行聚类,并通过聚类中心得到码字和码本。每个子空间内的聚类中心点即为码字,所有码字的集合构成码本。每个训练样本的每个子向量可以都用相应子空间的码字来近似,这样就实现了码字 ID 序列来表示训练样本,达到了数据量化的目的。在查询阶段,系统同样将查询向量划分为子向量,并在每个子空间中找到最近的码字,得到码字 ID 序列。随后,系统计算查询向量的每个子向量到所有对应子空间的码字的距离,形成距离表。最后,系统利用这个距离表和数据库中每个向量的码字 ID 序列,快速查找并累加各个子向量的对应距离,得到查询向量与数据库向量之间的近似距离。通过对这些距离进行排序,系统最终得到最近邻结果。该方法在减少内存占用和加快距离计算速度方面表现出色,但量化过程中会不可避免地会引入一些误差。在某些需要精度更高的应用场景中,我们可以在 PQ 的基础上进一步进行精确排序,以得到精确的最近邻结果。
- 常见软件库介绍
向量数据库 | URL | GitHub Star |
---|---|---|
milvus | https://github.com/milvus-io/milvus | 28.4K |
typesense | https://github.com/typesense/typesense | 19.0K |
qdrant | https://github.com/qdrant/qdrant | 18.9K |
chroma | https://github.com/chroma-core/chroma | 13.7K |
weaviate | https://github.com/weaviate/weaviate | 10.4K |
pinecone | https://www.pinecone.io/ | × |
# 6.3.5 检索结果重排
检索器可能检索到与查询相关性不高的文档。这些文档如果直接输入给大语言模型,可能会引发生成质量的下降。为此,在将其输入给大语言模型之前,还需要对其进行进一步的精选。精选的主要途径是对检索到的文档进行重新排序,简称重排,然后从中选择出排序靠前的文档。重排方法主要分为两类:基于交叉编码的方法和基于上下文学习的方法。
-
基于交叉编码的重排方法利用交叉编码器(Cross-Encoders)来评估文档与查询之间的语义相关性。
-
基于上下文学习的重排方法是指通过设计精巧的 Prompt,使用大语言模型来执行重排任务。这种方法可以利用大语言模型优良的深层语义理解能力,从而取得了良好的表现。
# 6.4 生成增强
检索器得到相关信息后,将其传递给大语言模型以期增强模型的生成能力。利用这些信息进行生成增强是一个复杂的过程,不同的方式会显著影响 RAG 的性能。
# 6.4.1 何时增强
大语言模型在训练过程中掌握了大量知识,这些知识被称为内部知识(Self Knowledge)。判断是否需要增强的核心在于判断大语言模型是否具有内部知识。判断模型是否具有内部知识的方法可以分为两类:(1)外部观测法,通过 Prompt 直接询问模型是否具备内部知识,或应用统计方法对是否具备内部知识进行估计,这种方法无需感知模型参数;(2)内部观测法,通过检测模型内部神经元的状态信息来判断模型是否存在内部知识,这种方法需要对模型参数进行侵入式的探测。
- 外部观测法
对于大语言模型,可以通过两种问询的方式来判断大语言模型是否具备相应的内部知识:(1)Prompt 直接询问大语言模型是否含有相应的内部知识;(2)反复询问大语言模型同一个问题观察模型多次回答的一致性。此外,我们也可以通过翻看大语言模型的 “教育经历”,即通过训练数据来判断其是否具备内部知识。但是,许多大语言模型的训练数据并未公开,无法直接观测它们的训练数据。在这种情况下,可以通过设计伪训练数据统计量来拟合真实训练数据的分布,从而间接评估模型对特定知识的学习情况。
- 内部观测法
由于模型的内部知识检索主要发生在中间层的前馈网络中,因此在处理包含或不包含内部知识的不同问题时,模型的中间层会展现出不同的动态变化。基于这一特性,可以训练分类器进行判别,这种方法被称为探针。
# 6.4.2 何处增强
得益于大语言模型的上下文学习能力、注意力机制的可扩展性以及自回归生成能力,其输入端、中间层和输出端都可以进行知识融合操作。在输入端,可以将问题和检索到的外部知识拼接在 Prompt 中,然后输入给大语言模型;在中间层,可以采用交叉注意力将外部知识直接编码到模型的隐藏状态中;在输出端,可以利用外部知识对生成的文本进行后矫正。
- 在输入端增强。
在输入端增强的方法直观且易于实现。在输入端增强的方法直接将检索到的外部知识文本与用户查询拼接到 Prompt 中,然后输入给大语言模型。其是当前主流的增强方法。此方式的重点在于 Prompt 设计以及检索到的外部知识的排序。在设计 Prompt 的过程中,可以运用 CoT 等 Prompt 技巧。
- 在中间层增强
在中间层增强增强的方法利用注意力机制的灵活性,先将检索到的外部知识转换为向量表示,然后将这些向量表示通过交叉注意力融合到模型的隐藏状态中。这种方法能够更深入地影响模型的内部表示,可能有助于模型更好地理解和利用外部知识。同时,由于向量表示通常比原始文本更为紧凑,这种方法可以减少对模型输入长度的依赖。然而,这种方法需要对模型的结构进行复杂的设计和调整,无法应用于黑盒模型。
- 在输出端增强
在输出端增强的方法利用检索到的外部知识对大语言模型生成的文本进行校准,是一种后处理的方法。在此类方法中,模型首先在无外部知识的情况下生成一个初步回答,然后再利用检索到的外部知识来验证或校准这一答案。校验过程基于生成文本与检索文本的知识一致性对输出进行矫正。矫正可以通过将初步回答与检索到的信息提供给大模型,让大模型检查并调整生成的回答来完成。这种方法的优点是可以确保生成的文本与外部知识保持一致,提高答案的准确性和可靠性。然而,其效果在很大程度上依赖于检索到的外部知识的质量和相关性。若检索到的文档不准确或不相关,则会导致错误的校准结果。
# 6.4.3 多次增强
在实际应用中,用户对大语言模型的提问可能是复杂或模糊的。复杂问题往往涉及多个知识点,需要多跳(multi-hop)的理解;而模糊问题往往指代范围不明,难以一次就理解问题的含义。对于复杂问题和模糊问题,难以通过一次检索增强就确保生成正确,多次迭代检索增强在所难免。处理复杂问题时,常采用分解式增强的方案。该方案将复杂问题分解为多个子问题,子问题间进行迭代检索增强,最终得到正确答案。处理模糊问题时,常采用渐进式增强的方案。该方案将问题的不断细化,然后分别对细化的问题进行检索增强,力求给出全面的答案,以覆盖用户需要的答案。
- 分解式增强
复杂问题通常包含多个知识点。对复杂问题进行作答,需要多跳理解。因此,在复杂问题的检索增强中,我们通常无法仅通过一次检索增强就得到满意的答案。在这种情况下,模型可以将多跳问题分解为一个个子问题,然后在子问题间迭代地进行检索增强,最后得出正确结论,即分解式增强。
DEMONSTRATE–SEARCH–PREDICT(DSP) 是一种具有代表性的分解式增强框架。该框架主要包含以下三个模块:(1)DEMONSTRATE 模块,通过上下文学习的方法,将复杂问题分解为子问题;(2)SEARCH 模块,对子问题进行迭代检索增强,为最终决策提供综合的外部知识;(3)PREDICT 模块,根据 SEARCH 提供的综合外部知识生成最终回答。
- 渐进式增强
在模糊问题中,问题主体通常指代不明,容易引发歧义。在处理模糊问题时,可以对问题进行渐进式地拆解、细化,然后对细化后的问题进行检索,利用检索到的信息增强大模型。这种渐进式的增强方法可以帮助大语言模型掌握更全面的信息。
TREEOFCLARIFICATIONS(TOC) 是渐进式增强的代表性框架。该框架通过递归式检索来引导大语言模型在树状结构中探索给定模糊问题的多种澄清路径。TOC 框架首先启动第一轮检索,根据检索到的相关文档和原始问题,生成一系列具体的细化问题在此过程中,框架会根据细化问题与原问题的相关性及知识一致性进行剪枝。随后,针对每个细化问题,框架独立展开深入的检索并对问题进一步细化,最终,我们能够构建出多条完整的知识路径,每条路径的末端(叶节点)都代表了对原始问题的不同但是有效的解答。通过整合所有有效的叶节点,我们能够得出一个精确且全面的长回答
这种渐进式的检索方法能够显著改善大语言模型对模糊问题的生成效果。然而,其需要进行多轮检索并生成多个答案路径,整个系统的计算量会随着问题复杂度呈指数级增长。此外,多轮的检索与生成不仅会带来显著的时间延迟,多个推理路径还为推理带来不稳定性,容易引发错误。
# 6.4.4 降本增效
检索出的外部知识通常包含大量原始文本。将其通过 Prompt 输入给大语言模型时,会大幅度增加输入 Token 的数量,从而增加了大语言模型的推理计算成本。此问题可从去除冗余文本与复用计算结果两个角度进行解决。
- 去除冗余文本
去除冗余文本的方法通过对检索出的原始文本的词句进行过滤,从中选择出部分有益于增强生成的部分。去除冗余文本的方法主要分为三类:Token 级别的方法,子文本级别的方法以及全文本级别的方法。
- 复用计算结果
在大语言模型进行推理的自回归过程中,每个 Token 都需要用到之前 Token 注意力模块涉及的 Key 和 Value 的结果。为了避免对每个 Token 都重新计算前面的 Key 和 Value 的结果,可以将之前计算的 Key 和 Value 的结果进行缓存(即 KV-cache),在需要是直接从 KV-cache 中调用相关结果,从而避免重复计算。
综上,通过结合上述输入 Prompt 压缩与 KV-cache 机制,RAG 框架可以在保持高性能的同时,显著提升其效率。这不仅有助于在资源受限的环境中部署模型,还可以提高模型在实际应用中的响应速度。
# 6.5 实践与应用
# 6.5.1 搭建简单 RAG 系统
为了助力开发者们高效且便捷地构建 RAG 系统,当前已有诸多成熟开源框架可供选择,其中最具代表性的便是 LangChain 与 LlamaIndex。