Advanced RAG 05:探讨基于文本内在语义信息的数据分块方法

编者按:在 RAG (Retrieval Augmented Generation) 系统中,将文本数据高效地划分成相对独立且富有语义信息的数据块(chunks)是一项较为关键的任务。基于规则的传统数据分块方法存在一些问题,因此探讨基于文本内在语义信息的数据分块方法非常必要。

在这篇文章中,作者深入探讨了三种基于文本内在语义信息的数据分块方法:基于嵌入的方法、基于模型的方法和基于大语言模型的方法。 作者对这些方法的技术原理和实际应用进行了详细分析。

本文详细论点归纳: (1) 基于嵌入的方法使用句子嵌入计算相似度进行数据分块,但存在粒度较粗的问题。 (2) 基于模型的方法利用预训练模型(如 BERT)进行数据分块,但需要针对特定场景进行定制训练才能发挥最佳性能。 (3) 基于大语言模型的方法使用 LLM 生成“proposition”作为数据分块的基本单元,可以实现更细粒度的数据分块,但成本较高。这些方法各有优缺点,需要根据实际应用场景权衡选择最合适的方案。

作者 | Florian June

编译 | 岳扬

文档解析完成后,就能够获得一些结构化或半结构化数据。目前的主要任务是将这些数据分解成更小的 chunks (译者注:用于描述数据或信息被分成小块或片段的术语)再提取特征(features),然后将从数据中提取的特征(features)转换成一种能够捕捉到其语义的形式。其在 RAG System 中的位置如图 1 所示。

图 1:数据分块过程(红框标注)在 RAG System 中的位置。图片由原文作者提供。

大多数常用的数据分块方法(chunking)都是基于规则的,采用 fixed chunk size(译者注:将数据或文本按照固定的大小进行数据分块)或 overlap of adjacent chunks(译者注:让相邻的数据块具有重叠内容,确保信息不会丢失。) 等技术。对于具有多个层级结构的文档,我们可以使用 Langchain 提供的 RecursiveCharacterTextSplitter[1],这种方法允许将文档按照不同的层级进行分割。

然而,在实际应用中,由于预定义的规则(比如数据分块大小(chunk size)或重叠部分的大小(size of overlapping parts))过于死板,基于规则的数据分块方法很容易导致检索到的上下文(retrieval contexts)不完整或包含 noise(译者注:指不需要的、干扰性的信息或数据,可能会对分析或处理造成干扰或误导的数据。) 的数据块过大等问题。

因此,最优雅的数据分块方法显然是基于语义的数据分块。通过 Semantic chunking(译者注:一种根据文本中的语义信息将文本分成有意义的片段或块的过程) ,希望每个数据块所包含的信息在语义上相对独立,以便更好地进行分析和处理。

本文将探讨按照文本内在语义信息进行划分的数据分块方法,解释其原理以及这些方法在实际应用中的使用情况。我们将介绍三种方法:

  • Embedding-based(译者注:基于嵌入的数据分块方法,数据被映射到一个低维空间中,以便更好地捕捉其语义信息。)
  • Model-based(译者注:基于模型的数据分块方法,使用了预先训练好的模型来进行语义分块。)
  • LLM-based(译者注:基于大语言模型的数据分块方法,使用 LLM 捕捉文本中的语义信息。)

01 Embedding-based 方法

LlamaIndex[2] 和 Langchain[3] 都提供了基于嵌入(embedding)的 semantic chunker(译者注:能够将文本或数据按照语义信息进行分块的工具或算法。) 。他们的算法理念大致是差不多的,本文将以 LlamaIndex 为例进行解读。

请注意,需要安装最新版本的 LlamaIndex ,才能使用 LlamaIndex 中的 semantic chunker。 我之前安装的 LlamaIndex 版本号是 0.9.45,并没有包含这个算法。因此,我创建了一个新的 conda 虚拟环境,并安装了更新版本的 LlamaIndex —— 0.10.12:

pip install llama-index-core

pip install llama-index-readers-file

pip install llama-index-embeddings-openai

值得一提的是,0.10.12 版本的 LlamaIndex 可以根据需求安装所需的组件或模块,因此本文仅安装一些关键组件。

(llamaindex_010) Florian:~ Florian$ pip list | grep llama
llama-index-core              0.10.12
llama-index-embeddings-openai 0.1.6
llama-index-readers-file 0.1.5
llamaindex-py-client          0.1.13

测试代码如下所示:

from llama_index.core.node_parser import (
    SentenceSplitter,
    SemanticSplitterNodeParser,
)
from llama_index.embeddings.openai import OpenAIEmbedding
from llama_index.core import SimpleDirectoryReader


import os
os.environ["OPENAI_API_KEY"] = "YOUR_OPEN_AI_KEY"

# load documents
dir_path = "YOUR_DIR_PATH"
documents = SimpleDirectoryReader(dir_path).load_data()


embed_model = OpenAIEmbedding()
splitter = SemanticSplitterNodeParser(
    buffer_size=1, breakpoint_percentile_threshold=95, embed_model=embed_model
)

nodes = splitter.get_nodes_from_documents(documents)
for node in nodes:
 print('-' * 100)
 print(node.get_content())

我了解了  splitter.get_nodes_from_documents [4] 函数的内部实现逻辑,其主要流程如图 2 所示:

图 2: splitter.get_nodes_from_documents[4] 函数的主要逻辑。图片由原文作者提供。

图 2 中提到的 “sentences” 是一个 Python 列表,其中每个成员都是包含四个键值对的字典,各键的含义如下:

  • sentence:当前句子
  • index:当前句子的序号
  • combined_sentence:用于构建滑动窗口(sliding window),包括 [index - self.buffer_size, index, index + self.buffer_size] 三个句子 (默认情况下,self.buffer_size = 1) ,能够用于计算句子间的语义相关性。将当前句子与其前后的句子合并在一起,可以减少不必要的干扰信息,从而更有效地抓取连续句子之间的关联性。
  • combined_sentence_embedding:combined_sentence 的嵌入向量

通过以上分析可以明显看出,基于嵌入向量的 semantic chunking(译者注:根据语义信息将文本或数据分成有意义的片段或块的过程) 方法本质上是基于滑动窗口(combined_sentence)计算文本相似度。 那些相邻且符合阈值的句子会被归入一个语义块。

项目路径中只有一份 BERT 论文文档[5]。以下是运行结果:

(llamaindex_010) Florian:~ Florian$ python /Users/Florian/Documents/june_pdf_loader/test_semantic_chunk.py 
...
...
----------------------------------------------------------------------------------------------------
We argue that current techniques restrict the
power of the pre-trained representations, espe-
cially for the fine-tuning approaches. The ma-
jor limitation is that standard language models are
unidirectional, and this limits the choice of archi-
tectures that can be used during pre-training. For
example, in OpenAI GPT, the authors use a left-to-
right architecture, where every token can only at-
tend to previous tokens in the self-attention layers
of the Transformer (Vaswani et al., 2017). Such re-
strictions are sub-optimal for sentence-level tasks,
and could be very harmful when applying fine-
tuning based approaches to token-level tasks such
as question answering, where it is crucial to incor-
porate context from both directions.
In this paper, we improve the fine-tuning based
approaches by proposing BERT: Bidirectional
Encoder Representations from Transformers.
BERT alleviates the previously mentioned unidi-
rectionality constraint by using a “masked lan-
guage model” (MLM) pre-training objective, in-
spired by the Cloze task (Taylor, 1953). The
masked language model randomly masks some of
the tokens from the input, and the objective is to


predict the original vocabulary id of the

maskedarXiv:1810.04805v2  [cs.CL]  24 May 2019

----------------------------------------------------------------------------------------------------
word based only on its context. Unlike left-to-
right language model pre-training, the MLM ob-
jective enables the representation to fuse the left
and the right context, which allows us to pre-
train a deep bidirectional Transformer. In addi-
tion to the masked language model, we also use
a “next sentence prediction” task that jointly pre-
trains text-pair representations. The contributions
of our paper are as follows:
• We demonstrate the importance of bidirectional
pre-training for language representations. Un-
like Radford et al. (2018), which uses unidirec-
tional language models for pre-training, BERT
uses masked language models to enable pre-
trained deep bidirectional representations. This
is also in contrast to Peters et al. 
----------------------------------------------------------------------------------------------------
...
...

测试结果表明,使用这种数据分块方法,得到的数据块粒度相对较粗。

图 2 还显示,这种方法是 page-based 的(译者注:这种方法将文本按照页面进行数据分块处理,而非其他更小的单位,比如句子或段落。),并没有直接解决跨多个页面的数据分块问题。

通常情况下,基于嵌入的数据分块方法,其性能严重依赖于嵌入模型。实际效果需要未来进一步评估。

02 Model-based 方法

2.1 Naive BERT 直接应用 BERT 模型进行数据分块

回顾一下 BERT[5] 的预训练过程(pre-training process)。其中的一个二元分类任务 —— “下一个句子预测(Next Sentence Prediction,NSP)” 是为了让模型理解两个句子之间的关系而设计的。在这种情况下,同时输入两个句子到 BERT 中,模型会预测这两个句子是否连续。

我们可以应用这一原理来设计一种直接的数据分块方法。对于一份文档,将其分割成若干句子。然后,使用滑动窗口,将相邻的句子输入到 BERT 模型中进行NSP judgement(译者注:根据BERT模型的预测结果判断两个句子是否连续),如图 3 所示:

图 3:使用 BERT 进行数据分块(Chunking)。图片由原文作者提供。

如果 BERT 模型对这两个句子的预测得分低于预设的阈值,则表明两个句子之间的语义关系较弱。预测结果可以作为 text segmentation point (译者注:根据预测结果确定的可以分割文本的位置,即划分两个句子的位置。),如图 3 中句子 2 和句子 3 之间的虚线所示。

这种方法的优点是可以直接使用,无需进一步训练或微调。

然而,这种方法在确定文本分割点(text segmentation point)时只考虑了紧随在当前句子之后的句子,忽略了更多距离较远段落的信息。此外,这种方法的预测效率相对较低。

2.2 基于 Cross Segment Attention 的数据分块技术

《Text Segmentation by Cross Segment Attention》[6]这篇论文提出了三种基于 Cross Segment Attention 的文本分割模型,如图 4 所示:

图 4:在 cross-segment BERT 模型(a)中,我们向模型输入潜在文本分割点附近的局部上下文:左边 k 个tokens,右边 k 个tokens。在 BERT+Bi-LSTM 模型(b)中,我们首先使用 BERT 模型对每个句子进行编码,然后将句子表征输入到 Bi-LSTM 中。在 hierarchical BERT 模型(c)中,我们首先使用 BERT 对每个句子进行编码,然后将输出的句子表征输入另一个基于 transformer 的模型。Source: Text Segmentation by Cross Segment Attention.[6]

图 4(a)中的 cross-segment BERT 模型,将文本分割定义为逐句分类任务(译者注:sentence-by-sentence classification task,逐句判断其是否是文本分割点)。潜在文本分割点附近的局部上下文(两侧的 k 个 tokens)被输入到模型中。与 [CLS] 相对应的隐藏状态(hidden state)被传递给 softmax 分类器,由其决定是否在潜在断句处进行分割。

这篇论文还介绍了另外两个模型。其中一个使用 BERT 模型获取每个句子的向量表征。将连续的多个句子转换为向量表征输入到 Bi-LSTM 模型(图4(b))或另一个 BERT 模型(图4(c))中,以预测每个句子是否是文本分割点的位置。

如图5所示,当时这三种模型都取得了不错的效果:

图 5:在 text segmentation 和 discourse segmentation 任务测试集的评估结果。Source: Text Segmentation by Cross Segment Attention.[6]

然而,迄今为止,大家仅了解这篇论文中公开的训练方法[7],还未发现公开且可用、可供推理的开源模型。

2.3 基于 SeqModel 的数据分块技术

Cross-Segment 模型将每个句子单独地转换为向量表示,而不考虑句子之间的关联或上下文信息。在《Sequence Model with Self-Adaptive Sliding Window for Efficient Spoken Document Segmentation》[8]论文中提出了进一步的改进方案 —— SeqModel。

SeqModel[9] 采用 BERT 同时对多个句子进行编码,将句子转换为向量表征之前,首先考虑句子所处的上下文,并尝试捕捉其中的语义依赖关系。然后会对每个句子进行分析,并尝试确定该句子是否是文本段落中的一个分割点。 此外,该模型还利用自适应滑动窗口(self-adaptive sliding window) 方法提高推理速度,而不会影响准确性。SeqModel 的示意图如图 6 所示。

图 6:SeqModel 架构和用于推理的自适应滑动窗口方法。

Source: Sequence Model with Self-Adaptive Sliding Window for Efficient Spoken Document Segmentation[8]

SeqModel 可通过 ModelScope[10] 框架使用。代码如下:

from modelscope.outputs import OutputKeys
from modelscope.pipelines import pipeline
from modelscope.utils.constant import Tasks

p = pipeline(
    task = Tasks.document_segmentation,
    model = 'damo/nlp_bert_document-segmentation_english-base'
)

print('-' * 100)

result = p(documents='We demonstrate the importance of bidirectional pre-training for language representations. Unlike Radford et al. (2018), which uses unidirectional language models for pre-training, BERT uses masked language models to enable pretrained deep bidirectional representations. This is also in contrast to Peters et al. (2018a), which uses a shallow concatenation of independently trained left-to-right and right-to-left LMs. • We show that pre-trained representations reduce the need for many heavily-engineered taskspecific architectures. BERT is the first finetuning based representation model that achieves state-of-the-art performance on a large suite of sentence-level and token-level tasks, outperforming many task-specific architectures. Today is a good day')

print(result[OutputKeys.TEXT])

在测试数据的末尾添加了一句 “Today is a good day(今天是个好日子)”,但文本分割处理后的 result 变量中并没有以任何方式将 "Today is a good day(今天是个好日子) "分割开。

(modelscope) Florian:~ Florian$ python /Users/Florian/Documents/june_pdf_loader/test_seqmodel.py 
2024-02-24 17:09:36,288 - modelscope - INFO - PyTorch version 2.2.1 Found.
2024-02-24 17:09:36,288 - modelscope - INFO - Loading ast index from /Users/Florian/.cache/modelscope/ast_indexer
...
...
----------------------------------------------------------------------------------------------------
...
... 
We demonstrate the importance of bidirectional pre-training for language representations.Unlike Radford et al.(2018), which uses unidirectional language models for pre-training, BERT uses masked language models to enable pretrained deep bidirectional representations.This is also in contrast to Peters et al.(2018a), which uses a shallow concatenation of independently trained left-to-right and right-to-left LMs.• We show that pre-trained representations reduce the need for many heavily-engineered taskspecific architectures.BERT is the first finetuning based representation model that achieves state-of-the-art performance on a large suite of sentence-level and token-level tasks, outperforming many task-specific architectures.Today is a good day

总体而言,基于模型的 semantic chunking(译者注:将文本分成具有语义相关性的片段。) 方法仍有很大的改进空间。

我建议的一种改进方法是创建专门针对某个项目或任务定制的训练数据,以便进行领域微,这样可以提高模型的性能。此外,优化模型架构也是一个改进点。

只要我们能够找到适合特定业务场景的优秀模型,就可以继续使用基于模型的方法来解决相关问题。

03 LLM-based 方法

这篇题为《 Dense X Retrieval: What Retrieval Granularity Should We Use? 》的论文介绍了一种新的检索单位,称为 proposition 。proposition 被定义为文本中的 atomic expressions(译者注:不能进一步分解的单个语义元素,可用于构成更大的语义单位)  ,用于检索和表达文本中的独特事实或特定概念,能够以简明扼要的方式表达,使用自然语言完整地呈现一个独立的概念或事实,不需要额外的信息来解释。

那么,我们如何获取所谓的 proposition 呢?本文通过构建提示词并与 LLM 交互来获取。

LlamaIndex 和 Langchain 都实现了相关算法,下面将使用 LlamaIndex 进行演示。

LlamaIndex 的实现思路是使用论文中提供的提示词来生成 proposition :

PROPOSITIONS_PROMPT = PromptTemplate(
    """Decompose the "Content" into clear and simple propositions, ensuring they are interpretable out of
context.
1. Split compound sentence into simple sentences. Maintain the original phrasing from the input
whenever possible.
2. For any named entity that is accompanied by additional descriptive information, separate this
information into its own distinct proposition.
3. Decontextualize the proposition by adding necessary modifier to nouns or entire sentences
and replacing pronouns (e.g., "it", "he", "she", "they", "this", "that") with the full name of the
entities they refer to.
4. Present the results as a list of strings, formatted in JSON.

Input: Title: ¯Eostre. Section: Theories and interpretations, Connection to Easter Hares. Content:
The earliest evidence for the Easter Hare (Osterhase) was recorded in south-west Germany in
1678 by the professor of medicine Georg Franck von Franckenau, but it remained unknown in
other parts of Germany until the 18th century. Scholar Richard Sermon writes that "hares were
frequently seen in gardens in spring, and thus may have served as a convenient explanation for the
origin of the colored eggs hidden there for children. Alternatively, there is a European tradition
that hares laid eggs, since a hare’s scratch or form and a lapwing’s nest look very similar, and
both occur on grassland and are first seen in the spring. In the nineteenth century the influence
of Easter cards, toys, and books was to make the Easter Hare/Rabbit popular throughout Europe.
German immigrants then exported the custom to Britain and America where it evolved into the
Easter Bunny."
Output: [ "The earliest evidence for the Easter Hare was recorded in south-west Germany in
1678 by Georg Franck von Franckenau.", "Georg Franck von Franckenau was a professor of
medicine.", "The evidence for the Easter Hare remained unknown in other parts of Germany until
the 18th century.", "Richard Sermon was a scholar.", "Richard Sermon writes a hypothesis about
the possible explanation for the connection between hares and the tradition during Easter", "Hares
were frequently seen in gardens in spring.", "Hares may have served as a convenient explanation
for the origin of the colored eggs hidden in gardens for children.", "There is a European tradition
that hares laid eggs.", "A hare’s scratch or form and a lapwing’s nest look very similar.", "Both
hares and lapwing’s nests occur on grassland and are first seen in the spring.", "In the nineteenth
century the influence of Easter cards, toys, and books was to make the Easter Hare/Rabbit popular
throughout Europe.", "German immigrants exported the custom of the Easter Hare/Rabbit to
Britain and America.", "The custom of the Easter Hare/Rabbit evolved into the Easter Bunny in
Britain and America." ]

Input: {node_text}
Output:"""
)

在前面的章节“ 01 Embedding-based 方法 ”中,我们已经安装了 LlamaIndex 0.10.12 的关键组件。但如果我们想要使用  DenseXRetrievalPack ,还需要运行pip install llama-index-llms-openai 安装。安装完成后,当前的 LlamaIndex 相关组件如下所示:

(llamaindex_010) Florian:~ Florian$ pip list | grep llama
llama-index-core                    0.10.12
llama-index-embeddings-openai       0.1.6
llama-index-llms-openai             0.1.6
llama-index-readers-file            0.1.5
llamaindex-py-client                0.1.13

在 LlamaIndex 中,DenseXRetrievalPack 是一个需要单独下载的软件包。这里直接在测试代码中下载。测试代码如下:

from llama_index.core.readers import SimpleDirectoryReader
from llama_index.core.llama_pack import download_llama_pack

import os
os.environ["OPENAI_API_KEY"] = "YOUR_OPENAI_KEY"

# Download and install dependencies
DenseXRetrievalPack = download_llama_pack(
    "DenseXRetrievalPack", "./dense_pack"
)

# If you have already downloaded DenseXRetrievalPack, you can import it directly.
# from llama_index.packs.dense_x_retrieval import DenseXRetrievalPack

# Load documents
dir_path = "YOUR_DIR_PATH"
documents = SimpleDirectoryReader(dir_path).load_data()

# Use LLM to extract propositions from every document/node
dense_pack = DenseXRetrievalPack(documents)

response = dense_pack.run("YOUR_QUERY")

这段测试代码主要使用 DenseXRetrievalPack 类的构造函数。因此,有必要分析 DenseXRetrievalPack 类的源代码[11]。

class DenseXRetrievalPack(BaseLlamaPack):
    def __init__(
        self,
        documents: List[Document],
        proposition_llm: Optional[LLM] = None,
        query_llm: Optional[LLM] = None,
        embed_model: Optional[BaseEmbedding] = None,
        text_splitter: TextSplitter = SentenceSplitter(),
        similarity_top_k: int = 4,
    ) -> None:
        """Init params."""
        self._proposition_llm = proposition_llm or OpenAI(
            model="gpt-3.5-turbo",
            temperature=0.1,
            max_tokens=750,
        )

        embed_model = embed_model or OpenAIEmbedding(embed_batch_size=128)

        nodes = text_splitter.get_nodes_from_documents(documents)
        sub_nodes = self._gen_propositions(nodes)

        all_nodes = nodes + sub_nodes
        all_nodes_dict = {n.node_id: n for n in all_nodes}

        service_context = ServiceContext.from_defaults(
            llm=query_llm or OpenAI(),
            embed_model=embed_model,
            num_output=self._proposition_llm.metadata.num_output,
        )

        self.vector_index = VectorStoreIndex(
            all_nodes, service_context=service_context, show_progress=True
        )

        self.retriever = RecursiveRetriever(
            "vector",
            retriever_dict={
                "vector": self.vector_index.as_retriever(
                    similarity_top_k=similarity_top_k
                )
            },
            node_dict=all_nodes_dict,
        )

        self.query_engine = RetrieverQueryEngine.from_args(
            self.retriever, service_context=service_context
        )

如代码所示,该构造函数的思路是首先使用 text_splitter 将文档划分为 nodes(译者注:将文档按照其最初的格式分割而成的最小单元。) ,然后调用 self._gen_propositions 生成 propositions 来获取相应的 sub_nodes(译者注:根据 nodes 生成的 propositions 所对应的文档片段或子集) 。然后,使用 nodes + sub_nodes 构建VectorStoreIndex,并通过 RecursiveRetriever 进行检索。递归检索器(recursive retriever)可以通过处理文档的小数据块(small chunks)来找到所需信息,就像我们可以直接去书籍中的某个小节或段落查找一样。但是,如果在这些小数据块(small chunks)中找不到完整的信息,递归检索器(recursive retriever)会将相关的大数据块(larger chunks)传递到生成阶段(generation stage)进一步处理,就像我们在书中某个小节或段落查找资料时,如果需要更多信息,就会翻到相关的章节或整本书一样。

项目路径中只有一份 BERT 论文文档。通过调试,我们发现 sub_nodes[].text 的内容并非原始文本,里面的内容已经被改写过了:

> /Users/Florian/anaconda3/envs/llamaindex_010/lib/python3.11/site-packages/llama_index/packs/dense_x_retrieval/base.py(91)__init__()
     90 
---> 91         all_nodes = nodes + sub_nodes
     92         all_nodes_dict = {n.node_id: n for n in all_nodes}


ipdb> sub_nodes[20]
IndexNode(id_='ecf310c7-76c8-487a-99f3-f78b273e00d9', embedding=None, metadata={}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, text='Our paper demonstrates the importance of bidirectional pre-training for language representations.', start_char_idx=None, end_char_idx=None, text_template='{metadata_str}\n\n{content}', metadata_template='{key}: {value}', metadata_seperator='\n', index_id='8deca706-fe97-412c-a13f-950a19a594d1', obj=None)
ipdb> sub_nodes[21]
IndexNode(id_='4911332e-8e30-47d8-a5bc-ed7cbaa8e042', embedding=None, metadata={}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, text='Radford et al. (2018) uses unidirectional language models for pre-training.', start_char_idx=None, end_char_idx=None, text_template='{metadata_str}\n\n{content}', metadata_template='{key}: {value}', metadata_seperator='\n', index_id='8deca706-fe97-412c-a13f-950a19a594d1', obj=None)
ipdb> sub_nodes[22]
IndexNode(id_='83aa82f8-384a-4b06-92c8-d6277c4162bf', embedding=None, metadata={}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, text='BERT uses masked language models to enable pre-trained deep bidirectional representations.', start_char_idx=None, end_char_idx=None, text_template='{metadata_str}\n\n{content}', metadata_template='{key}: {value}', metadata_seperator='\n', index_id='8deca706-fe97-412c-a13f-950a19a594d1', obj=None)
ipdb> sub_nodes[23]
IndexNode(id_='2ac635c2-ccb0-4e62-88c7-bcbaef3ef38a', embedding=None, metadata={}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, text='Peters et al. (2018a) uses a shallow concatenation of independently trained left-to-right and right-to-left LMs.', start_char_idx=None, end_char_idx=None, text_template='{metadata_str}\n\n{content}', metadata_template='{key}: {value}', metadata_seperator='\n', index_id='8deca706-fe97-412c-a13f-950a19a594d1', obj=None)
ipdb> sub_nodes[24]
IndexNode(id_='e37b17cf-30dd-4114-a3c5-9921b8cf0a77', embedding=None, metadata={}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, text='Pre-trained representations reduce the need for many heavily-engineered task-specific architectures.', start_char_idx=None, end_char_idx=None, text_template='{metadata_str}\n\n{content}', metadata_template='{key}: {value}', metadata_seperator='\n', index_id='8deca706-fe97-412c-a13f-950a19a594d1', obj=None)

sub_nodes 和 nodes 之间的关系如图 7 所示,这个索引结构按照 small-to-big 的方式进行排序组织。

图 7:按照 small-to-big 的方式进行排序组织的索引结构。图片由作者提供。

这种索引结构是通过 self._gen_propositions[12] 构建的,代码如下:

 async def _aget_proposition(self, node: TextNode) -> List[TextNode]:
 """Get proposition."""
        inital_output = await self._proposition_llm.apredict(
            PROPOSITIONS_PROMPT, node_text=node.text
 )
        outputs = inital_output.split("\n")

        all_propositions = []

 for output in outputs:
 if not output.strip():
 continue
 if not output.strip().endswith("]"):
 if not output.strip().endswith('"') and not output.strip().endswith(
 ","
 ):
                    output = output + '"'
                output = output + " ]"
 if not output.strip().startswith("["):
 if not output.strip().startswith('"'):
                    output = '"' + output
                output = "[ " + output

 try:
                propositions = json.loads(output)
 except Exception:
 # fallback to yaml
 try:
                    propositions = yaml.safe_load(output)
 except Exception:
 # fallback to next output
 continue

 if not isinstance(propositions, list):
 continue

            all_propositions.extend(propositions)

 assert isinstance(all_propositions, list)
        nodes = [TextNode(text=prop) for prop in all_propositions if prop]

 return [IndexNode.from_text_node(n, node.node_id) for n in nodes]

 def _gen_propositions(self, nodes: List[TextNode]) -> List[TextNode]:
 """Get propositions."""
        sub_nodes = asyncio.run(
            run_jobs(
 [self._aget_proposition(node) for node in nodes],
                show_progress=True,
                workers=8,
 )
 )

 # Flatten list
 return [node for sub_node in sub_nodes for node in sub_node]

对每个原始节点(original node),都异步调用 self._aget_proposition,通过 PROPOSITIONS_PROMPT 获取 LLM 返回的 inital_output,然后根据 inital_output 获取 propositions 并构建 TextNode。最后,将这些 TextNode 与原始节点(original node)关联起来,即使用 [IndexNode.from_text_node(n, node.node_id) for n in nodes] 。

有一件事需要多说一句:原论文使用 LLM 生成的 propositions 作为训练数据,微调了一个文本生成模型。这个文本生成模型目前是公开可访问的[13],感兴趣的读者可以体验一下。

总体来看,这种利用 LLM 构建 propositions 的数据分块方法能够实现更精细的数据分块。 能够与原始节点(original node)构成一个 small-to-big 的索引结构,从而为 semantic chunking(译者注:将文本分成具有语义相关性的片段。) 方法提供了一种新思路。

不过,这种方法依赖于 LLM,成本相对较高

如果条件允许,可以对这种基于 LLM 的数据分块方法进行持续跟踪和监控。

04 Conclusion

本文探讨了三种 semantic chunking 方法的原理和具体实现,并做出了一些评述。semantic chunking 是一种更加优雅的方法,也是优化 RAG 的关键点之一。

如果您对 RAG 技术感兴趣,欢迎阅读本系列的其他文章!如有任何问题,请在评论区提出。

Thanks for reading!

——

Florian June

An artificial intelligence researcher, mainly write articles about Large Language Models, data structures and algorithms, and NLP.

END

参考资料

[1]https://github.com/langchain-ai/langchain/blob/v0.1.9/libs/langchain/langchain/text_splitter.py#L851C1-L851C6

[2]https://docs.llamaindex.ai/en/stable/examples/node_parsers/semantic_chunking.html

[3]https://python.langchain.com/docs/modules/data_connection/document_transformers/semantic-chunker

[4]https://github.com/run-llama/llama_index/blob/v0.10.12/llama-index-core/llama_index/core/node_parser/text/semantic_splitter.py

[5]https://arxiv.org/pdf/1810.04805.pdf

[6]https://arxiv.org/abs/2004.14535

[7]https://github.com/aakash222/text-segmentation-NLP/

[8]https://arxiv.org/pdf/2107.09278.pdf

[9]https://github.com/alibaba-damo-academy/SpokenNLP

[10]https://github.com/modelscope/modelscope/

[11]https://github.com/run-llama/llama_index/blob/v0.10.12/llama-index-packs/llama-index-packs-dense-x-retrieval/llama_index/packs/dense_x_retrieval/base.py

[12]https://github.com/run-llama/llama_index/blob/v0.10.12/llama-index-packs/llama-index-packs-dense-x-retrieval/llama_index/packs/dense_x_retrieval/base.py#L161

[13]https://github.com/chentong0/factoid-wiki

本文经原作者授权,由 Baihai IDP 编译。如需转载译文,请联系获取授权。

原文链接:

https://medium.com/towards-artificial-intelligence/advanced-rag-05-exploring-semantic-chunking-97c12af20a4d

本系列其他文章:

Advanced RAG 01:讨论未经优化的 RAG 系统存在的问题与挑战

Advanced RAG 02:揭开 PDF 文档解析的神秘面纱

Advanced RAG 03:运用 RAGAs 与 LlamaIndex 评估 RAG 应用

Advanced RAG 04:重排序(Re-ranking)技术探讨

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/587718.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

NASA数据集——VIIRS每日 L3深蓝气溶胶网格产品(AERDB_D3_VIIRS_SNPP),以 1 x 1 度

VIIRS/SNPP Deep Blue Level 3 monthly aerosol data, 1 degree x1 degree grid 简介 美国国家航空航天局(NASA)的可见红外成像辐射计套件(VIIRS)标准三级(L3)每月深蓝气溶胶产品来自苏米国家极轨伙伴关系…

PHP定时任务框架taskPHP3.0学习记录7宝塔面板手动可以执行自动无法执行问题排查及解决方案(sh脚本、删除超过特定天数的日志文件、kill -9)

PHP定时任务框架taskPHP3.0学习记录 PHP定时任务框架taskPHP3.0学习记录1(TaskPHP、执行任务类的实操代码实例)PHP定时任务框架taskPHP3.0学习记录2(环境要求、配置Redis、crontab执行时间语法、命令操作以及Screen全屏窗口管理器&#xff0…

AWS最近宣布Amazon Q现已全面上市

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…

Gateway结合Nacos使用!!!

一、本地结合使用 1. 引入依赖 <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> 2. bootstarp.yml配置文件 如果Nacos中配置使用yaml格式&…

Ubuntu上的screenfetch

2024年4月28日&#xff0c;周日下午 这些文本是由一个叫做 “screenfetch” 的命令生成的&#xff0c;它会显示一些系统和用户信息&#xff0c;包括操作系统、内核版本、系统运行时间、安装的软件包数量、使用的Shell、分辨率、桌面环境、窗口管理器、主题、图标主题、字体、CP…

Linux(ubuntu)—— 用户管理user 用户组group

一、用户 1.1、查看所有用户 cat /etc/passwd 1.2、新增用户 useradd 命令&#xff0c;我这里用的是2.4的命令。 然后&#xff0c;需要设置密码 passwd student 只有root用户才能用passwd命令设置其他用户的密码&#xff0c;普通用户只能够设置自己的密码 二、组 2.1查看…

分布式任务调度平台XXL-JOB

系列文章目录 文章目录 系列文章目录前言前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码吧。 Quartz作为开源作业调度中的佼佼者,是作业调度的首选。但是集群环境中Qu…

汇川AM400PLC和禾川X3E伺服EtherCAT通信

1、汇川H5UPLC和X3E伺服EtherCAT总线控制 汇川H5U PLC通过EtherCAT总线控制SV660N和X3E伺服_伺服驱动器 ethercat csdn 汇川-CSDN博客文章浏览阅读1.7w次,点赞3次,收藏37次。首先我们看下系统硬件和软件配置:汇川H5U PLC的编程软件是:AutoShop V4.6.3.0 硬件:PLC H5U-161…

什么副业兼职适合小白?1小时收益40+,1条作品只需5分钟

今天&#xff0c;我要向大家推荐一个当前非常受欢迎的项目&#xff0c;这个项目来自一位忠实的粉丝朋友的推荐。他正是依靠这个项目实现了可观的收益。 这位兄弟通过参与“词典搬砖”项目&#xff0c;已经成功变现超过一万元&#xff0c;而且这笔收入还是在睡眠中获得的&#…

踏上R语言之旅:解锁数据世界的神秘密码(五)

线性与非线性模型及R使用 文章目录 线性与非线性模型及R使用一、数据的分类与模型选择1.变量的取值类型 二、广义线性模型广义线性模型概述Logistic模型 总结 一、数据的分类与模型选择 1.变量的取值类型 因变量记为y&#xff0c;解释变量记为x1&#xff0c;x2,… 因变量y一般…

77、贪心-买卖股票的最佳时机

思路 具体会导致全局最优&#xff0c;这里就可以使用贪心算法。方式如下&#xff1a; 遍历每一位元素找出当前元素最佳卖出是收益是多少。然后依次获取最大值&#xff0c;就是全局最大值。 这里可以做一个辅助数组&#xff1a;右侧最大数组&#xff0c;求右侧最大数组就要从…

ChatGPT4.0知识问答、DALL-E生成AI图片、Code Copilot辅助编程,打开新世界的大门

目录 1、DALL-E 文字转图片 在线AI修改2、Write For Me3、Code Copilot 目前最强的AI编程大模型4、Diagrams: Show Me5、Instant Website [Multipage] 网站合成神器6、AskYourPDF Research Assistant 无限PDF7、Diagrams & Data: Research, Analyze, Visualize 精读Excel …

WPF之border标签边框控件、设置弧度、图片

border标签在WPF中承担着边框的角色又称之为边框标签&#xff0c;使用嵌套的方法去给一些标签添加边框&#xff0c;border标签包裹目标标签(border不能有多个子元素)。一般在给标签添加弧度时可以使用border。 常用属性 CornerRadius边框拐角的弧度&#xff0c;当宽高是一样的…

设计模式之监听器模式ListenerPattern(三)

一、介绍 监听器模式是一种软件设计模式&#xff0c;在对象的状态发生改变时&#xff0c;允许依赖它的其他对象获得通知。在Java中&#xff0c;可以使用接口和回调机制来实现监听器模式。 二、代码实例 1、事件Event类 package com.xu.demo.listener;// 事件类 public class…

多目标应用:MSSA多目标樽海鞘优化算法求解无人机三维路径规划(MATLAB代码)

一、无人机多目标优化模型 无人机三维路径规划是无人机在执行任务过程中的非常关键的环节&#xff0c;无人机三维路径规划的主要目的是在满足任务需求和自主飞行约束的基础上&#xff0c;计算出发点和目标点之间的最佳航路。 1.1路径成本 无人机三维路径规划的首要目标是寻找…

Maven3.9.6下载安装教程

(/≧▽≦)/~┴┴ 嗨~我叫小奥 ✨✨✨ &#x1f440;&#x1f440;&#x1f440; 个人博客&#xff1a;小奥的博客 &#x1f44d;&#x1f44d;&#x1f44d;&#xff1a;个人CSDN ⭐️⭐️⭐️&#xff1a;Github传送门 &#x1f379; 本人24应届生一枚&#xff0c;技术和水平有…

快速了解Django:核心概念解析与实践指南

title: 快速了解Django&#xff1a;核心概念解析与实践指南 date: 2024/5/1 20:31:41 updated: 2024/5/1 20:31:41 categories: 后端开发 tags: Django核心路由系统视图系统ORM管理中间件Web框架登录装饰器 第一章&#xff1a;Django简介 背景和发展历程&#xff1a; Djan…

B站广告投放开户的基本费用?

哔哩哔哩&#xff08;B站&#xff09;作为国内领先的年轻人文化社区与视频平台&#xff0c;凭借其独特的文化氛围和庞大的Z世代用户基础&#xff0c;成为品牌营销不可忽视的战场。对于意欲在这片沃土上播种品牌影响力的商家而言&#xff0c;深入理解B站广告投放的开户流程、费用…

cx_Oracle.DatabaseError: DPI-1047: Cannot locate a 64-bit Oracle Client library

pip install cx_Oracleimport cx_Oracle 再导入该模块进行数据库连接的时候报错 这个错误表明您的Python环境是64位的&#xff0c;但是您尝试使用的Oracle客户端库&#xff08;oci.dll&#xff09;是32位的 根据官方给出的文档进行查看 cx_Oracle 8 Installation — cx_Ora…

正则化手册:探索改进机器学习模型功能的实用技巧

书籍&#xff1a;The Regularization Cookbook&#xff1a;Explore practical recipes to improve the functionality of your ML models 作者&#xff1a;Vincent Vandenbussche 出版&#xff1a;Packt Publishing 书籍下载-《正则化手册&#xff1a;探索改进机器学习模型功…