综述
当下大模型存在上下文长度的限制,对于某些场景下,过长的文本无法一次输入进大模型进行理解。具体来说,在知识库场景下,RAG召回的内容往往决定了输出的效果。在RAG的子任务中,chunking就是一项非常重要的预处理任务,会极大地影响包括预问题生成、召回语义匹配等流程任务。
基于段落的滑动窗口,旨在构造语义尽可能相似的滑动上下文窗口,实现的逻辑参照了滑动窗口的实现方式,结合语义学和逻辑学提出。该方法提出是基于当下大模型的上下文长度存在限制,然而如果单纯使用一些通用的方式会存在丢失上下文或者语义切割不连贯的问题,故此提出该方案,笔者已在多个场景下验证得到良好的使用效果。
方案对比
先用一些业界上比较常见的方案来进行对比,详细的内容可见笔者的论文
基于token长度切割
最大的问题即会造成语义上的丢失。
比如一个完整的语句有100个token,如果粗暴地按token切割,很可能会对完整的语义形成割裂,比如:
今天的天气非常不错,适合出去玩。
上面是一个完整的句子,假使我按照5个字来切(这里简单地先按照字,可自行类比token的概念),那么就会切成:
今天的天气
非常不错,
适合出去玩。
我们可以简单地发现语义上已经不连贯了,那么你拿着这个输入丢给大模型,你能指望会有好的答复吗。
这事可能有人会说,如果我把token数量拉大,是否能解决这个问题。在一定的概率上,你能够规避一些问题,但是我举一个极端的情况,我下面会有一段带有特定名词的数据:
作为多模态大模型,Gemini可以同时识别和理解文本、图像、音频、视频和代码五种信息,且对信息的理解非常精准。有别于传统大模型对英伟达硬件及生态的依赖,Gemini的训练来自于谷歌的TPUv4和v5e的硬件支持。
假如我定义的长度,刚好切到了一个特定的名词
作为多模态大模型,Gemini可以同时识别和理解文本、图像、音频、视频和代码五种信息,且对信息的理解非常精准。有别于传统大模型对英伟达硬件及生态的依赖,Gemini的训练来自于谷歌的
TPUv4和v5e的硬件支持。
这个时候,如果我的问题是Gemini的训练需要什么硬件
,那么回归大概率是回归到第一段,且假设这段又刚刚好达到了大模型的输入token限制长度,那大模型再怎么牛逼都答复不了你了。
基于句子切割
这个方案也是一个流行的方案,
这样做的好处在于快且方便,保留了句子的语义。且针对实际的场景,我们会对句子进行上下文的扩展,这里是有一个发展演变的路程的:
句子切割
其实现在于通过一些策略,以句子为最小颗粒度进行划分。如:
除了 Mistral Large 之外,Mistral 还发布了一个新的优化模型 Mistral Small,针对延迟和成本进行了优化。Mistral Small 的性能优于 Mixtral 8x7B,并且延迟较低,这使其成为 Mistral AI 的开放权重的模型和旗舰模型之间的中间解决方案。Mistral Small 受益于与 Mistral Large 在 RAG 启用和函数调用方面相同的创新。
将会被划分为
除了 Mistral Large 之外,Mistral 还发布了一个新的优化模型 Mistral Small,针对延迟和成本进行了优化。
Mistral Small 的性能优于 Mixtral 8x7B,并且延迟较低,这使其成为 Mistral AI 的开放权重的模型和旗舰模型之间的中间解决方案。
Mistral Small 受益于与 Mistral Large 在 RAG 启用和函数调用方面相同的创新。
按token限制进行扩展
我们进行了大量的行业调研,包括熟知的那几家大厂以及那几家领域钻研很深的公司的产品,发现其实他们最多用的就是该方法
。
在笔者看来,这个方法最大的好处就是非常之节省资源,比如某b场,可以利用一些句子结束标识符的策略光速对全文进行切割,然后按照预设的单个chunk的max_token进行扩展,当然还是有一些巧思在里面的,比如他们在相邻的两个chunk里,有一部分的内容是重复的,这样能延展语义的连续。
但是我们在实际的场景落地中会发现一些问题,如检索冗余
、过度增强
,比如我关心的实际上是《话题A》,但是因为扩展机制把另一个话题下的《话题B》、《话题C》都给扩展进来了,如果这A、B、C三个话题完全不一样还好,但是实际上来说因为语义连贯性的问题,上下文的话题相关度还是很高的,就会造成好心办坏事
。
就会:帮助大模型理解 → 影响大模型理解。
按语义进行扩展
后来笔者想到了另一种扩展方式,即我以召回句子为中心,将语义相似度高的前后句子扩展进来是否可行。
实操下来发现,这样确实可以有效帮助大模型进行内容的理解。但是随着我们测试的进行,另一个问题又出现了,即如果在同一个段落中,A句子和C句子间夹杂了一个无关性的表达B,那么本来应该将ABC合并的就因为B中断了,但是又因为B只是承上启下的一句话,但实际上还是在同一个段落中,那么这一块内容就丢失了,简单点理解就是:
我有一篇文章
A段
B段
C段
D段
我先按照句子进行拆解,得到
A1句 A2句
B1句 B2句 B3句
C1句 C2句 C3句 C4句
D1句 D2句 D3句
假使我定位到了C3句
,然后进行扩展,但是C2句
只是一段承上启下的话,那么我本来应该通过滑动窗口滑动出B3句 C1句 C2句 C3句 C4句 D1句 D2句
。但是实际上因为C2句
的语义相关性和C3句
不高,最后效果只滑动出了C3句 C4句 D1句 D2句
,就造成了相关上下文内容的丢失。
段落切割
这也就是我们最后选择的方案,逻辑可以参考下面的实现逻辑。受限于篇幅,更详细的可见论文中的详细实现路径。
实现逻辑
-
文本内容分割:
将文本内容按照文章段落进行最小颗粒度的分割,生成一个段落或句子的列表,称为 segment_list。 -
向量化转换:
对 segment_list 中的每个段落或句子进行向量化转换,将其转换为向量表示,形成一个大小相等的向量列表 vector_list。 -
设立相关度阈值:
设立一个相关度阈值,用于判断两个向量之间的相似度是否达到匹配的标准。 -
动态滑动窗口匹配:
开启动态滑动窗口匹配过程,对 vector_list 进行遍历,以遍历到的节点为中心,向左右两侧扩展匹配。
对于当前索引位置 x,分别对其左侧和右侧的向量进行匹配。
如果匹配度大于设定的相关度阈值,则将匹配的向量合并到当前节点,并继续向相邻节点进行匹配。
继续对 x-2、x+2、x-3、x+3 等进行类似的匹配操作,直到匹配度小于阈值,即不满足匹配条件为止,停止扩展匹配。 -
停止条件:
停止条件是匹配度小于相关度阈值时停止扩展匹配,表明当前节点的左右扩展匹配已经达到边界。
理论依据
上下文相关性
语义滑动窗口考虑了文档中相邻段落之间的语义关联性。语言的含义在很大程度上是由上下文决定的,因此分析相邻段落的语义关系有助于更准确地理解文档的语义结构。
语义连贯性
语义滑动窗口允许我们在整个文档中移动,以捕捉段落之间的语义连贯性。语义连贯性是文档理解的重要因素,因为它能够帮助我们理解文档中的主题、论点和逻辑关系。
主题和语境分析
通过分析相邻段落的语义相似性,我们可以识别出文档中的不同主题和语境。这有助于我们更好地理解文档的内容结构,从而更精确地提取文档中的关键信息。
语义关系的捕捉
语义滑动窗口可以帮助我们捕捉文档中不同段落之间的语义关系,如因果关系、对比关系等。这有助于我们理解文档中的逻辑结构和信息传递方式。
文本相似性分析
语义滑动窗口可以用于分析文档中不同部分之间的相似性。这有助于我们识别出文档中重复或相关的内容,从而提取出更加全面和准确的信息。