了解Langchain的递归cecursivecharactertextsplitter
#python #ai #chatgpt #langchain

大型语言模型是具有广泛功能的强大工具;尽管如此,他们仍将被称为上下文窗口的独特限制努力。此上下文窗口定义了这些模型可以熟练地处理文本的边界。以gpt-3.5-turbo为例,在上下文长度为4,096个令牌,大约对应于3500个单词。

但是,当您使用超出其上下文窗口的文档向这些模型展示这些模型时会发生什么?这是一种称为“大块”的聪明策略发挥作用。分块涉及将文档分为较小,更易于管理的部分,这些部分适合在大语言模型的上下文窗口中。

Langchain为用户提供了一系列块技术可供选择。但是,在这些选择中,RecursiveCharacterTextSplitter出现了作为受欢迎和强烈推荐的方法。

快速概述

RecursiveCharacterTextSplitter拿了大文本,并根据指定的块尺寸将其拆分。它通过使用一组字符来做到这一点。提供给它的默认字符是["\n\n", "\n", " ", ""]

它需要大文本,然后试图将其拆分为第一个字符\n\n。如果第一个由\n\n拆分仍然很大,则它将移至下一个字符,即\n,并试图通过它分开。如果它仍然大于我们指定的块大小,它将移至集合中的下一个字符,直到我们获得小于指定块尺寸的分裂。

代码实现

我从事的工作

2021年2月

大学前,我在学校外工作的两件事是写作和编程。我没有写论文。我写了那时作家应该写的东西,可能仍然是:短篇小说。我的故事很糟糕。他们几乎没有任何情节,只是具有强烈感情的角色,我想象着这使他们深入。

我尝试的第一个程序是在IBM 1401上,我们的学区用于当时所谓的“数据处理”。这是九年级的,所以我当时13岁或14岁。学区的1401恰好在我们初中的地下室,而我的朋友Rich Draves和我允许使用它。就像一个迷你邦德小人的巢穴一样,所有这些外观外观的机器 - CPU,磁盘驱动器,打印机,读卡器坐在明亮的荧光灯下的高架地板上。

上面的文本摘自Paul Graham撰写的文章,标题为:What I Worked On。让我们利用RecursiveCharacterTextSplitter将其分解成小块,每个块最大尺寸为100个字符。

首先,我们从Langchain导入它:

from langchain.text_splitter import RecursiveCharacterTextSplitter

让我们加载我们希望从一个称为text的变量中创建块的文本。

text = """What I Worked On

February 2021

Before college the two main things I worked on, outside of school, were writing and programming. I didn't write essays. I wrote what beginning writers were supposed to write then, and probably still are: short stories. My stories were awful. They had hardly any plot, just characters with strong feelings, which I imagined made them deep.

The first programs I tried writing were on the IBM 1401 that our school district used for what was then called "data processing." This was in 9th grade, so I was 13 or 14. The school district's 1401 happened to be in the basement of our junior high school, and my friend Rich Draves and I got permission to use it. It was like a mini Bond villain's lair down there, with all these alien-looking machines — CPU, disk drives, printer, card reader — sitting up on a raised floor under bright fluorescent lights.
"""

接下来,我们创建一个RecursiveCharacterTextSplitter实例,使用100的chunk_sizechunk_overlap值配置为零。我们的方法涉及使用长度函数根据其特征数来测量每个块。

text_splitter = RecursiveCharacterTextSplitter(
    chunk_size = 100,
    chunk_overlap  = 0,
    length_function = len,
)

RecursiveCharacterTextSplitter提供了几种执行拆分的方法。在我们的情况下,我们将利用split_text方法。此方法需要一个代表文本的字符串输入并返回一系列字符串,每个字符串在分裂过程后代表块。

texts = text_splitter.split_text(text)
print(len(texts)) # 11
print(texts[0]) # 'What I Worked On\n\nFebruary 2021'

执行分裂后,我们的文本成功分为总共11个单独的块。

深入解释

Recursion

顾名思义,RecursiveCharacterTextSplitter采用递归作为完成文本分裂的核心机制。现在,让我们详细介绍我们早期代码如何实现这一壮举的过程。

对于我们的演练,我们将利用与代码实施过程中使用的相同的文本和参数。这涉及保罗·格雷厄姆(Paul Graham)论文的一部分,我们将考虑100个字符的大小。我们用于拆分的字符将是['\n\n', '\n', ' ', '' ]

Paul Graham's Essay

让我们从我们的初始文本开始。目前以人类可读形式呈现,我们的下一步涉及将其转换为计算机可以轻松理解的格式。

Paul Graham's Essay for computers

现在,新行已转换为\n,这正是我们需要进行分裂过程所需的。

Highlighted Paul Graham's essay

让我们选择文本。这可以比作我们的text上的split_text方法。

如前所述,RecursiveCharacterTextSplitter尝试使用预定义的字符集进行拆分。它的第一次尝试涉及\n\n字符,它是通过段落分开的手段。现在,让我们在文本中确定此字符的所有出现。

Occurrence of paragraphs in the essay

一旦我们找到了\n\n字符的所有实例,随后的步骤涉及使用此字符作为我们指定的分隔符执行拆分。

Split text by paragraph

目前,我们有四个分裂。我们的下一步涉及评估每次分割以检查它们是否符合小于我们指定的块大小的条件,该块设置为100个字符。

前两个分裂满足了这种情况,从而赢得了良好拆分的标签。由于这两个段由少于100个字符组成,因此我们可以将它们组合起来创建我们的初始块。

Initial chunk of RecursiveCharacterTextSplitter

进行第二次分开,我们发现自己处于使用\n\n角色无法实现进一步减少的情况。因此,我们进入下一个字符:\n。我们的目的是使用\n字符执行拆分,并确定我们是否可以减少拆分的大小。

此操作类似于在第二个拆分文本中调用split_text,但包含\n字符。这是递归概念发挥作用的地方。

Second chunk

使用\n字符执行拆分后,我们最终得到了两个拆分。鉴于它仅包含一个字符,因此第一个拆分符合良好的分裂。但是,第二次分开超过了我们指定的块大小。

因此,我们需要再次在此特定拆分上调用split_text方法。但是,这次我们将使用我们的角色列表中的下一个字符采用拆分,这恰好是' '字符。

RecursiveCharacterTextSplitter using spaces

最后,我们成功地降低了分裂尺寸。现在,我们继续进行每次分开以进行合并。这些合并的指导原则是,没有由此产生的合并拆分应超过我们指定的100个字符的块。

merge list

合并后,我们最终得到了四个块,每个块都遵循我们的条件,即块不应超过100个字符。

现在,让我们重新访问原始文本分割并确定尚待处理的拆分。

Final chunk in RecursiveCharacterTextSplitter

我们仍然有一个比块大小更大的拆分。我们再次重复相同的过程。

Perform split with RecursiveCharacterTextSplitter

我们使用新的行字符作为分离器启动拆分。

Perform split with RecursiveCharacterTextSplitter using spaces

我们使用空格作为分离器进行分裂。

Perform merge with RecursiveCharacterTextSplitter

接下来,我们进行合并,确保没有合并的段超过定义的块大小。

完成整个过程后,我们到达了11个单独的块。这11个块中的每一个成功地遵守了100个字符的限制。
该结果与我们以编程方式实现的目标相吻合。

Final chunks from using the RecursiveCharacterTextSplitter


,我们有。我们已经深入研究了Langchain的RecursiveCharacterTextSplitter的内部工作。对于那些感兴趣的人,您可以探索源代码here。如果您觉得这篇文章有益,请考虑对您的反应表示赞赏:ð才能: