前言
"LangChain 系列" 是一系列全面的文章和教程,探索了 LangChain 库的各种功能和特性。LangChain 是由 SoosWeb3 开发的 Python 库,为自然语言处理(NLP)任务提供了一系列强大的工具和功能。
该系列涵盖了与 NLP 相关的广泛主题,包括数据加载、文本预处理、文本分割、嵌入等等。系列中的每篇文章深入探讨了 LangChain 的特定方面,提供详细的解释、代码示例和实际应用案例。
"LangChain 系列" 的目标是帮助开发人员和数据科学家利用 LangChain 的功能来构建强大且高效的 NLP 应用程序。无论您是 NLP 的新手还是经验丰富的从业者,本系列都提供有价值的见解和指导,以提升您的 NLP 项目。
通过跟随 "LangChain 系列" 中的文章,您将全面了解如何在各种 NLP 任务中使用 LangChain,并发挥您的 NLP 项目的全部潜力。
LangChain 系列教程之 — 文本分割器
介绍
欢迎阅读这个系列的第四篇文章;到目前为止,我们已经探索了如何设置LangChain项目和加载文档;现在是时候处理我们的源文件并引入文本分割器了,这是构建基于LLM的应用程序的下一步。
查看系列的前三篇文章: •为开发LangChain设置完美的Python环境[9]。•了解LangChain项目的基本结构[10]。•学习如何使用LangChain文档加载器[11]。
什么是LangChain中的文本分割器
文本分割器是一种将大段文本拆分成较小块或片段的算法或方法。其目标是创建可单独处理的可管理的片段,这在处理大型文档或数据集时通常是必要的。
在LangChain中,根据您的用例,有不同类型的分割器;我们将最常见的分割器是RecursiveCharacterTextSplitter
,它非常适用于一般文档,例如纯文本或文本和代码的混合等。
LangChain中的文本分割器具有一些控制选项,用于管理块的大小和质量:
1.length_function:此参数确定如何计算块的长度。默认情况下,它简单地计算字符的数量,但您也可以在此处传递一个标记计数函数,它将计算块中单词或其他标记的数量,而不是字符。2.chunk_size:此参数设置块的最大大小。大小根据length_function
参数进行测量。3.chunk_overlap:此参数设置块之间的最大重叠。重叠的块意味着文本的某些部分将包含在多个块中。例如,在某些情况下,这可以有助于在块之间保持上下文或连续性。4.add_start_index:此参数是一个布尔标志,确定是否在元数据中包含每个块在原始文档中的起始位置。包含此信息可能有助于跟踪每个块在原始文档中的来源。
文本分割器的完整列表:
[12]拆分一些文档
现在,让我们继续进行第二步。在加载文档之后,我们将深入了解各种文本分割器,使用前一篇文章中介绍的PDF示例之一。
了解如何加载PDF[13]。
[14]递归字符文本分割器
RecursiveCharacterTextSplitter
常常被推荐用于处理一般文本,因为它具有较强的适应性。该文本分割器基于一个字符列表,这些字符作为文本中的分隔符或“分割点”使用。它尝试通过依次按照列表中列出的顺序拆分这些字符来创建文本块,直到生成的块达到可管理的大小为止。
默认的字符列表是 ["nn", "n", " ", ""]
。文本分割器首先尝试在每个双换行符 ("nn") 处拆分文本,这通常用于分隔文本中的段落。如果生成的块过大,它接着尝试在每个换行符 ("n") 处拆分,这通常用于分隔句子。如果块仍然过大,它最后尝试在每个空格 (" ") 处拆分,这用于分隔单词。如果块仍然过大,它会在每个字符 ("") 处拆分,尽管在大多数情况下,这种细粒度的拆分是不必要的。
这种方法的优点是它尽量保留了语义上下文,通过保持段落、句子和单词的完整性。这些文本单元往往具有强烈的语义关系,其中的单词在意义上通常密切相关,这对于许多自然语言处理任务是有益的。
在 LangChain 文档中了解更多关于
RecursiveCharacterTextSplitter
的信息:RecursiveCharacterTextSplitter[15]。
现在我们已经了解了它的工作原理,让我们尝试在实践中应用它,并继续使用 SpaceX CRS-5 Mission Press Kit[16] 的 PDF 文档进行构建。
代码语言:javascript复制按照前一篇文章的步骤到达这里[17]。
from langchain.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
# Use the PyPDFLoader to load and parse the PDF
loader = PyPDFLoader("./pdf_files/SpaceX_NASA_CRS-5_PressKit.pdf")
pages = loader.load_and_split()
print(f'Loaded {len(pages)} pages from the PDF')
text_splitter = RecursiveCharacterTextSplitter(
chunk_size = 200,
chunk_overlap = 10,
length_function = len,
add_start_index = True,
)
texts = text_splitter.split_documents(pages)
print(f'Split the pages in {len(texts)} chunks')
print(texts[0])
print(texts[1])
现在我们来看一下具体的步骤:
1.加载和解析 PDF。2.设置文本分割器:•创建一个 RecursiveCharacterTextSplitter
的实例。构造函数传递的参数包括:•chunk_size
:定义文本应该被分割成的最大块的大小。在这个例子中,设置为 400,所以每个块最多包含 400 个字符。这只是一个示例大小,我们稍后会讨论这个。•chunk_overlap
:块之间的最大重叠量。这里设置为 30,所以连续块之间最多可以有 30 个字符的重叠。•length_function
:用于计算块长度的函数。在这个例子中,使用内置的 len
函数,所以块的长度就是它的字符数。•add_start_index
:该参数决定是否在元数据中包含每个块在原始文档中的起始位置。这里设置为 True
,所以这些信息将被包含在内。3.分割文本:•然后在 text_splitter
实例上调用 split_documents
方法,将 pages
列表作为参数传递。该方法遍历 pages
列表中的每个页面,并根据初始化 text_splitter
时设置的参数将页面的文本分割成块。结果是一个块的列表,并打印出块的数量。4.打印前两个块: 最后,我们将 texts
列表中的前两个块打印到控制台。每个块表示为一个元组,其中第一个元素是块的文本(page_content
),第二个元素是一个包含有关块的元数据的字典。元数据包括块在原始文档中的起始位置,由 add_start_index
参数指定。
在这个过程中,我们打印出 PDF 加载的页数和分割器创建的块数。在这个例子中,我们有 26 页和 151 个块。
块大小和重叠
在处理文本数据时,正确设置参数非常重要。在我们的示例中,用于块大小和重叠的数字是任意选择的,但在实际情况下我们需要对它们做出决策。
首先,我们必须以一种不超过嵌入模型的标记限制的方式对文本进行分割。"嵌入"听起来可能是一个复杂的术语,但实际上,它是一种将单词、句子或整个文档转化为数值向量或'嵌入'的方法。这些向量以一种计算机可以理解的方式捕捉单词和句子的意义和关系。
我们将使用的嵌入模型是OpenAI的 text-embedding-ada-002
,它非常适用于许多类型的应用程序。该模型可以处理最多8191个标记,因此我们必须确保我们的文本块的标记数少于这个限制。
你可能想知道什么是 'token'。它与字符不同。粗略地说,一个 token 大约等于四个字符的长度。这意味着我们的模型可以处理很多字符,但我们需要小心,不要使我们的块太大,否则可能会丢失一些上下文信息。
根据我的经验,保持每个块在500到1000个字符之间是最好的。这个大小似乎在不丢失重要信息的情况下工作得很好。
至于重叠参数,它指的是我们希望在块之间重复多少文本。通常建议将其设置为块大小的10-20%。这样,我们可以确保块之间有一定的联系,而不会造成过多的重复。如果重叠太大,会导致处理过程变慢并增加成本。
因此,基于这些考虑,这是我在处理相对较长的文本时使用的配置。
代码语言:javascript复制text_splitter = RecursiveCharacterTextSplitter(
chunk_size = 500,
chunk_overlap = 50,
length_function = len,
add_start_index = True,
)
# or
text_splitter = RecursiveCharacterTextSplitter(
chunk_size = 1000,
chunk_overlap = 100,
length_function = len,
add_start_index = True,
)
[18]自定义长度函数
在这个例子中,我们使用了默认的长度计算函数 len
,它计算字符的数量,但我们也可以创建和传递更复杂的函数。例如,我们可以根据标记(token)而不是字符来计算长度。为了实现这一点,我们可以使用 HuggingFace 的 Transformers 库。让我们安装一些额外的包:
pip install transformers torch tensorflow
以下是对每个库的详细解释:
1.transformers:这是由Hugging Face开发的库,提供了通用的自然语言理解(NLU)和自然语言生成(NLG)的架构(如BERT、GPT-2、RoBERTa、XLM、DistilBert等)。它包含了约100多种语言和深度学习框架(PyTorch、TensorFlow)的数千个预训练模型。该库设计得既适合研究,又易于使用和高效。2.torch(PyTorch):PyTorch是由Facebook的AI Research实验室开发的开源机器学习库。它基于Torch库,用于计算机视觉和自然语言处理应用。PyTorch因其相对于其他库更直观易用、调试更容易和强大的GPU加速支持而闻名。3.tensorflow:TensorFlow是由Google Brain团队开发的另一个开源机器学习库。它旨在为机器学习提供灵活、高效和可扩展的平台,并支持广泛的神经网络架构。TensorFlow还提供了强大的分布式计算支持,使您能够在多个GPU甚至多台机器上训练大型模型。
接下来,让我们添加我们的自定义函数(就像我们在网站地图加载器中提取自定义数据时所做的那样),并将其作为文本分割器构造函数的参数使用:
代码语言:javascript复制from langchain.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from transformers import AutoTokenizer
# Use the PyPDFLoader to load and parse the PDF
loader = PyPDFLoader("./pdf_files/SpaceX_NASA_CRS-5_PressKit.pdf")
pages = loader.load_and_split()
print(f'Loaded {len(pages)} pages from the PDF')
tokenizer = AutoTokenizer.from_pretrained('gpt2')
def tokens(text: str) -> int:
return len(tokenizer.encode(text))
# Note that we reduce to 250 the chunk size since we are working with tokens now
text_splitter = RecursiveCharacterTextSplitter(
chunk_size = 250,
chunk_overlap = 20,
length_function = tokens,
add_start_index = True,
)
texts = text_splitter.split_documents(pages)
print(f'Split the pages in {len(texts)} chunks')
print(texts[0])
print(texts[1])
请注意,现在我们正在使用标记(tokens)进行操作,因此我将块大小从1000更改为250,并将重叠量从100更改为20。
运行两者,你会发现使用标记的分割器创建了更均匀的块,这可能有助于模型获取上下文。
尽管如此,常规的分割器效果非常好,对于处理简单文本来说可能是最佳选择,因为它更易于管理。
代码分割器
正如我们之前提到的,LangChain根据使用情况提供了多种分割器。现在让我们看看如果只处理代码时可以使用哪些分割器。
在文档中找到code text splitter[19]。
CodeTextSplitter
允许将一段代码拆分为较小的部分,例如,分析或单独处理它们。它基于特定语言的语法规则和约定进行拆分。RecursiveCharacterTextSplitter
是CodeTextSplitter
的一个具体实现,它使用字符或字符序列来拆分代码。
让我们看看如何使用它来拆分我们自己的代码:
代码语言:javascript复制from langchain.text_splitter import (
RecursiveCharacterTextSplitter,
Language,
)
# Print a list of the available languages
for code in Language:
print(code)
# The code to split
python = """from langchain.document_loaders import PyPDFLoaderfrom langchain.text_splitter import RecursiveCharacterTextSplitterfrom transformers import AutoTokenizer# Use the PyPDFLoader to load and parse the PDFloader = PyPDFLoader("./pdf_files/SpaceX_NASA_CRS-5_PressKit.pdf")pages = loader.load_and_split()print(f'Loaded {len(pages)} pages from the PDF')tokenizer = AutoTokenizer.from_pretrained('gpt2')def tokens(text: str) -> int: return len(tokenizer.encode(text))# Note that we reduce to 250 the chunk size since we are working with tokens nowtext_splitter = RecursiveCharacterTextSplitter( chunk_size = 250, chunk_overlap = 20, length_function = tokens, add_start_index = True,)texts = text_splitter.split_documents(pages)print(f'Split the pages in {len(texts)} chunks')print(texts[0])print(texts[1])"""
python_splitter = RecursiveCharacterTextSplitter.from_language(
language=Language.PYTHON, chunk_size=50, chunk_overlap=0
)
python_docs = python_splitter.create_documents([python])
print(python_docs)
通过运行此代码,首先会以以下格式打印可用的编程语言列表:
代码语言:javascript复制Language.PROGRAMMING_LANGUAGE
然后,我们使用Language.PYTHON
在其中运行先前的示例。
因此,如果你只处理代码库,这个方法是理想的选择。
另一个有用的分割器是natural language tool kit[20],适用于演讲和类似场景。
结论
这是又一个较长的文章,但现在你已经具备了开始探索如何为基于AI模型的应用程序有效生成块的所需知识。
声明
本文翻译整理自:Learn how to use text splitters in LangChain[21],感兴趣的请点赞、收藏。另外,大家也可以到官方文档[22]上了解更多内容。
References
[1]
介绍: https://soosweb3.hashnode.dev/the-ultimate-langchain-series-text-splitters#heading-introduction
[2]
LangChain中的文本分割器是什么: https://soosweb3.hashnode.dev/the-ultimate-langchain-series-text-splitters#heading-what-is-a-text-splitter-in-langchain
[3]
分割一些文档: https://soosweb3.hashnode.dev/the-ultimate-langchain-series-text-splitters#heading-split-some-documents
[4]
递归字符文本分割器: https://soosweb3.hashnode.dev/the-ultimate-langchain-series-text-splitters#heading-recursive-character-text-splitter
[5]
块大小和重叠: https://soosweb3.hashnode.dev/the-ultimate-langchain-series-text-splitters#heading-chunk-size-and-overlap
[6]
自定义长度函数: https://soosweb3.hashnode.dev/the-ultimate-langchain-series-text-splitters#heading-custom-length-function
[7]
代码分割器: https://soosweb3.hashnode.dev/the-ultimate-langchain-series-text-splitters#heading-code-splitters
[8]
结论: https://soosweb3.hashnode.dev/the-ultimate-langchain-series-text-splitters#heading-conclusion
[9]
为开发LangChain设置完美的Python环境
: https://soosweb3.hashnode.dev/the-ultimate-langchain-series-environment-setup
[10]
了解LangChain项目的基本结构
: https://soosweb3.hashnode.dev/the-ultimate-langchain-series-projects-structure
[11]
学习如何使用LangChain文档加载器: https://soosweb3.hashnode.dev/the-ultimate-langchain-series-data-loaders#heading-introduction
[12]
Permalink: https://soosweb3.hashnode.dev/the-ultimate-langchain-series-text-splitters#heading-split-some-documents
[13]
了解如何加载PDF: https://soosweb3.hashnode.dev/the-ultimate-langchain-series-data-loaders#heading-general-pfd-use
[14]
Permalink: https://soosweb3.hashnode.dev/the-ultimate-langchain-series-text-splitters#heading-recursive-character-text-splitter
[15]
RecursiveCharacterTextSplitter: https://python.langchain.com/en/latest/modules/indexes/text_splitters/examples/recursive_text_splitter.html
[16]
SpaceX CRS-5 Mission Press Kit: https://www.nasa.gov/sites/default/files/files/SpaceX_NASA_CRS-5_PressKit.pdf
[17]
按照前一篇文章的步骤到达这里: https://soosweb3.hashnode.dev/the-ultimate-langchain-series-data-loaders#heading-general-pfd-use
[18]
Permalink: https://soosweb3.hashnode.dev/the-ultimate-langchain-series-text-splitters#heading-custom-length-function
[19]
code text splitter: https://python.langchain.com/en/latest/modules/indexes/text_splitters/examples/code_splitter.html
[20]
natural language tool kit: https://python.langchain.com/en/latest/modules/indexes/text_splitters/examples/nltk.html
[21]
Learn how to use text splitters in LangChain: https://soosweb3.hashnode.dev/the-ultimate-langchain-series-text-splitters
[22]
官方文档: https://python.langchain.com/docs/modules/data_connection/document_transformers/text_splitters/recursive_text_splitter