文本分块是自然言语处理(NLP)中的一项要害技能,其作用是将较长的文本切割成更小、更易于处理的片段。这种切割一般是依据单词的词性和语法结构,例如将文本拆分为名词短语、动词短语或其他语义单位。这样做有助于更高效地从文本中提取要害信息。

在本文中,我将介绍在实际项目中用到几种文本分块战略,了解它们在构建依据大型言语模型(如RAG)的体系中的重要性。此外,我还将介绍如何有用地运用这些文本分块战略。经过这些战略,能够更好地处理和解析大规模文本数据,前进信息提取和言语了解的准确性和功率。

文本分块战略解析:找到合适你项目的文本分块战略

1、文本分块的重要性

在运用大型言语模型(LLM)时,文本分块的重要性不容忽视。这是由于几个要害因素对处理成果有着显著影响。

以一个实例来阐明:假定你手头有一个15页的文档,满篇都是文字,你想对其进行摘要和问答处理。在这个进程中,最首要的步骤是提取整个文档的嵌入向量,这里就会遇到一些问题:

  • 信息丢掉问题:假如一次性提取整个文档的嵌入向量,尽管能够考虑到整个文档的上下文,但可能会丢掉关于特定主题的很多有价值信息。这会导致言语模型供给的信息不够准确或存在遗失。
  • 分块巨细约束:运用像OpenAI这样的模型时,需求特别重视分块巨细。例如,GPT-4有32K的窗口巨细约束。尽管这个约束一般不会形成问题,但从一开始就留心这一点是十分必要的。

因而,正确地运用文本分块技能,不只能前进文本的整体质量和可拜访性,还能防止因信息丢掉或分块不妥而导致的问题。这就是为何在处理大型文档时,采纳文本分块而不是直接运用整个文档至关重要的原因。

2、文本分块战略

在自然言语处理中,不同的文本分块战略各具特色,适用于各种场景。接下来,咱们将评论这些战略的优缺点及其适用环境。

2.1传统办法拆分

(1) 简略粗暴办法(正则拆分)

正则表达式是一种在文本处理中广泛运用的东西,它能够经过特定的形式匹配来有用地处理字符串。在中文文本分块的场景中,正则表达式能够用来辨认中文标点符号,然后将文本拆分成单独的语句。这种办法依赖于中文句号、“问号”、“感叹号”等标点符号作为语句完毕的标志。尽管这种依据形式匹配的办法可能不如依据复杂语法和语义剖析的办法准确,但它在大多数状况下足以满足根本的语句切割需求,而且完成起来更为简略直接。

import re
def split_sentences(text):
    # 运用正则表达式匹配中文语句完毕的标点符号
    sentence_delimiters = re.compile(u'[。?!;]|n')
    sentences = sentence_delimiters.split(text)
    # 过滤掉空字符串
    sentences = [s.strip() for s in sentences if s.strip()]
    return sentences
text ="文本分块是自然言语处理(NLP)中的一项要害技能,其作用是将较长的文本切割成更小、更易于处理的片段。这种切割一般是依据单词的词性和语法结构,例如将文本拆分为名词短语、动词短语或其他语义单位。这样做有助于更高效地从文本中提取要害信息。"
sentences = split_sentences(text)
print(sentences)

在上面例子中,咱们并没有选用任何特定的办法来切割语句。另外,还有许多其他的文本分块技能能够运用,例如词汇化(tokenizing)、词性标注(POS tagging)等。

#output
['文本分块是自然言语处理(NLP)中的一项要害技能,其作用是将较长的文本切割成更小、更易于处理的片段', '这种切割一般是依据单词的词性和语法结构,例如将文本拆分为名词短语、动词短语或其他语义单位', '这样做有助于更高效地从文本中提取要害信息']

(2) Spacy Text Splitter

Spacy是一个用于履行自然言语处理(NLP)各种使命的库。它具有文本拆分器功用,能够在进行文本切割的同时,保存切割成果的上下文信息。

import spacy
input_text = "文本分块是自然言语处理(NLP)中的一项要害技能,其作用是将较长的文本切割成更小、更易于处理的片段。这种切割一般是依据单词的词性和语法结构,例如将文本拆分为名词短语、动词短语或其他语义单位。这样做有助于更高效地从文本中提取要害信息。"  
nlp = spacy.load( "zh_core_web_sm" ) 
doc = nlp(input_text) 
for s in doc.sents: 
    print (s)

经过Spacy,咱们能够智能地处理原始文本数据。

#output 
文本分块是自然言语处理(NLP)中的一项要害技能,其作用是将较长的文本切割成更小、更易于处理的片段。
这种切割一般是依据单词的词性和语法结构,例如将文本拆分为名词短语、动词短语或其他语义单位。
这样做有助于更高效地从文本中提取要害信息。

2.2递归拆分

递归拆分是一种经过迭代办法,运用一组分隔符将输入文本切分成更小片段的办法。这个进程的特色是,假如在最初的尝试中未能将文本切分成符合预期巨细的块,它会持续递归地运用不同的分隔符或规范,直到到达期望的块巨细。

以下是一个用Langchain库完成递归拆分的示例。

#input text
input_text = "文本分块是自然言语处理(NLP)中的一项要害技能,其作用是将较长的文本切割成更小、更易于处理的片段。这种切割一般是依据单词的词性和语法结构,例如将文本拆分为名词短语、动词短语或其他语义单位。这样做有助于更高效地从文本中提取要害信息。"  
from langchain.text_splitter import RecursiveCharacterTextSplitter 
text_splitter = RecursiveCharacterTextSplitter( 
    chunk_size = 100 , #设置所需的文本巨细
    chunk_overlap = 20 ) 
chunks = text_splitter.create_documents([input_text]) 
print (chunks)

递归拆分的输出如下所示:

#output
[Document(page_content='文本分块是自然言语处理(NLP)中的一项要害技能,其作用是将较长的文本切割成更小、更易于处理的片段。这种切割一般是依据单词的词性和语法结构,例如将文本拆分为名词短语、动词短语或其他语义单位。这样做有助'), Document(page_content='短语、动词短语或其他语义单位。这样做有助于更高效地从文本中提取要害信息。')]

2.3专门的结构化拆分

(1) HTML 文本拆分器

HTML文本拆分器是一种结构感知的文本分块东西。它能够在HTML元素级别上进行文本拆分,而且会为每个分块增加与之相关的标题元数据。这种拆分器的特色在于其对HTML结构的敏感性,能够精准地处理和剖析HTML文档中的内容。

#input html string
 html_string = """ 
<!DOCTYPE html> 
<html> 
<body> 
    <div> 
        <h1>Mobot</h1> 
        <p>一些关于Mobot的介绍文字。</p> 
        <div> 
            <h2>Mobot首要部分</h2> 
            <p>有关Mobot的一些介绍文本。</p> 
            <h3>Mobot第1末节</h3> 
            <p>有关Mobot第一个子主题的一些文本。</p> 
            <h3>Mobot第2末节</h3> 
            <p>关于Mobot的第二个子主题的一些文字。</p> 
        </div> 
        <div> 
            <h2>Mobot</h2> 
            <p>关于Mobot的一些文字</p> 
        </ div> 
        <br> 
        <p>关于Mobot的一些结论性文字</p> 
    </div> 
</body> 
</html> 
"""
 headers_to_split_on = [ 
    ( "h1" , "Header 1" ), 
    ( "h2" , "标题 2" ), 
    ( "h3" , "标题 3" ), 
] 
from langchain.text_splitter import HTMLHeaderTextSplitter 
html_splitter = HTMLHeaderTextSplitter(headers_to_split_on=headers_to_split_on) 
html_header_splits = html_splitter.split_text(html_string) 
print(html_header_split)

仅提取在header_to_split_on参数中指定的HTML标题。

#output
[Document(page_content='Mobot'), Document(page_content='一些关于Mobot的介绍文字。  nMobot首要部分 Mobot第1末节 Mobot第2末节', metadata={'Header 1': 'Mobot'}), Document(page_content='有关Mobot的一些介绍文本。', metadata={'Header 1': 'Mobot', '标题 2': 'Mobot首要部分'}), Document(page_content='有关Mobot第一个子主题的一些文本。', metadata={'Header 1': 'Mobot', '标题 2': 'Mobot首要部分', '标题 3': 'Mobot第1末节'}), Document(page_content='关于Mobot的第二个子主题的一些文字。', metadata={'Header 1': 'Mobot', '标题 2': 'Mobot首要部分', '标题 3': 'Mobot第2末节'}), Document(page_content='Mobot div>', metadata={'Header 1': 'Mobot'}), Document(page_content='关于Mobot的一些文字  n关于Mobot的一些结论性文字', metadata={'Header 1': 'Mobot', '标题 2': 'Mobot'})]

(2) **Markdown 文本拆分 **

Markdown文本拆分是一种依据Markdown的语法规矩(例如标题、Bash代码块、图片和列表)进行文本分块的办法。这种拆分东西具有对结构的敏感性,能够依据Markdown文档的结构特色进行有用的文本切割。

#input markdown string
markdown_text = '# Mobotnn ## Stonenn这是python  n这是nn ## markdownnn 这是中文文本拆分' 
from langchain.text_splitter import MarkdownHeaderTextSplitter
headers_to_split_on = [
    ("#", "Header 1"),
    ("##", "Header 2"),
    ("###", "Header 3"),
]
markdown_splitter = MarkdownHeaderTextSplitter(headers_to_split_on=headers_to_split_on)
md_header_splits = markdown_splitter.split_text(markdown_text)
print(md_header_splits)

MarkdownHeaderTextSplitter 能够依据设定的 headers_to_split_on 参数,将 Markdown 文本进行拆分。这一功用使得用户能够便捷地依据指定的标题将 Markdown 文件切割成不同部分,然后前进修改和管理的功率。

#output
[Document(page_content='这是pythonn这是', metadata={'Header 1': 'Mobot', 'Header 2': 'Stone'}), Document(page_content='这是中文文本拆分', metadata={'Header 1': 'Mobot', 'Header 2': 'markdown'})]

(3) LaTex 文本拆分

LaTex文本拆分东西是一种专用于代码分块的东西。它经过解析LaTex命令来创立各个块,这些块按照逻辑安排,如章节和末节等。这种办法能够产生愈加准确且与上下文相关的分块成果,然后有用地提升LaTex文档的安排和处理功率。

#input Latex string
latex_text = """documentclass{article}begin{document}maketitlesection{Introduction}大型言语模型 (LLM) 是一种机器学习模型,能够在很多文本数据上进行练习,以生成相似人类的言语。近年来,法学硕士在各种自然言语处理使命中取得了重大进展,包含言语翻译、文本生成和情感剖析。subsection{法学硕士的历史}最早的法学硕士是在 20 世纪 80 时代开发的和 20 世纪 90 时代,但它们遭到可处理的数据量和其时可用的核算能力的约束。但是,在过去的十年中,硬件和软件的前进使得在海量数据集上练习法学硕士成为可能,然后导致subsection{LLM 的运用}LLM 在工业界有许多运用,包含谈天机器人、内容创立和虚拟助理。它们还能够在学术界用于言语学、心理学和核算言语学的研讨。end{document}""" 
from langchain.text_splitter import LatexTextSplitter 
Latex_splitter = LatexTextSplitter(chunk_size= 100 , chunk_overlap= 0 ) 
latex_splits = Latex_splitter.create_documents([latex_text]) 
print (latex_splits)

在上述示例中,咱们注意到代码切割时的堆叠部分设置为0。这是由于在处理代码切割进程中,任何堆叠的代码都可能彻底改变其原有意义。因而,为了坚持代码的原始意图和准确性,防止产生误解或过错,设置堆叠部分为0是必要的。

[Document(page_content='documentclass{article}begin{document}maketitlesection{Introduction}大型言语模型 (LLM)'), Document(page_content='是一种机器学习模型,能够在很多文本数据上进行练习,以生成相似人类的言语。近年来,法学硕士在各种自然言语处理使命中取得了重大进展,包含言语翻译、文本生成和情感剖析。subsection{法学硕士的历史'), Document(page_content='}最早的法学硕士是在'), Document(page_content='20 世纪 80 时代开发的和 20 世纪 90'), Document(page_content='时代,但它们遭到可处理的数据量和其时可用的核算能力的约束。但是,在过去的十年中,硬件和软件的前进使得在海量数据集上练习法学硕士成为可能,然后导致subsection{LLM 的运用}LLM'), Document(page_content='在工业界有许多运用,包含谈天机器人、内容创立和虚拟助理。 它们还能够在学术界用于言语学、心理学和核算言语学的研讨。end{document}')]

当你决定运用哪种分块器处理数据时,重要的一步是提取数据嵌入并将其存储在向量数据库(Vector DB)中。上面的例子中运用文本分块器结合 LanceDB 来存储数据块及其对应的嵌入。LanceDB 是一个无需配置、开源且无服务器的向量数据库,其数据持久化在硬盘驱动器上,允许用户在不超出预算的状况下完成扩展。此外,LanceDB 与 Python 数据生态体系兼容,因而你能够将其与现有的数据东西(如 pandas、pyarrow 等)结合运用。

3、总结

总结来说,文本分块是一个看似简略却极为重要的使命,它展示了多种应战。没有通用的战略适用于一切状况,也没有固定的块巨细合适一切解决方案。某些办法可能适用于特定的数据或解决方案,但对其他状况则可能不适用。经过这篇博客,咱们不只强调了文本分块的重要性,还供给了一些在履行文本分块时的新见地。