本人项目地址大全:Victor94-king/NLP__ManVictor: 优快云 of ManVictor
写在前面: 笔者更新不易,希望走过路过点个关注和赞,笔芯!!!
写在前面: 笔者更新不易,希望走过路过点个关注和赞,笔芯!!!
写在前面: 笔者更新不易,希望走过路过点个关注和赞,笔芯!!!
大多数常用的数据分块方法(chunking)都是基于规则的,采用 fixed chunk size(译者注:将数据或文本按照固定的大小进行数据分块)或 overlap of adjacent chunks(译者注:让相邻的数据块具有重叠内容,确保信息不会丢失。) 等技术。对于具有多个层级结构的文档,可以使用 Langchain 提供的 RecursiveCharacterTextSplitter,这种方法允许将文档按照不同的层级进行分割。
然而,在实际应用中,由于预定义的规则(比如数据分块大小(chunk size)或重叠部分的大小(size of overlapping parts))过于死板,基于规则的数据分块方法很容易导致检索到的上下文(retrieval contexts)不完整或包含 noise(译者注:指不需要的、干扰性的信息或数据,可能会对分析或处理造成干扰或误导的数据。) 的数据块过大等问题
-
长文本向量化的挑战
在基于 Transformer 架构的向量化模型中,每个词汇都会被映射为一个高维向量。为了表示整段文本的语义,通常采用对词向量取平均,或使用特殊标记(如
[CLS]
)位置的向量作为整体表示。然而,当直接对过长的文本进行向量化时,会面临以下挑战:- 语义信息稀释 :长文本往往涵盖多个主题或观点,整体向量难以准确捕捉细节语义,导致语义信息被稀释,无法充分体现文本的核心内容。
- 计算开销增大 :处理长文本需要更多的计算资源和存储空间,增加了模型的计算复杂度,影响系统的性能和效率。
- 检索效率降低 :过长的向量在检索过程中可能会降低匹配精度,导致检索结果的相关性下降,同时也会降低检索的速度和效率。
-
提升检索和生成质量的必要性
为了克服上述挑战,合理的文本分块策略显得尤为重要。通过对文本进行适当的切分,可以有效提升检索和生成的质量:
- 提高检索准确率 :将文本分块后,得到的文本片段具有更精细的粒度,能够更准确地匹配用户的查询意图,提升检索结果的相关性和准确度。
- 优化系统性能 :缩短单个文本块的长度,减少了模型在向量化和检索过程中的计算和存储开销,提高了系统的处理效率和响应速度。
- 增强大模型的回答质量 :为大语言模型提供更相关和精炼的文本块,有助于模型更好地理解语境,从而生成更准确、连贯和贴切的回答。
通过对文本进行合理的分块,不仅可以提高向量化过程中的语义表达能力,还能在检索阶段取得更高的匹配精度,最终使得 RAG 系统能够为用户提供更优质的服务。
1.文本分块策略对大模型输出的影响
1.1 文本分块过长的影响
在构建 RAG(Retrieval-Augmented Generation)系统时,文本分块的长度对大模型的输出质量有着至关重要的影响。过长的文本块会带来一系列问题:
- 语义模糊 :当文本块过长时,在向量化过程中,细节语义信息容易被平均化或淡化。这是因为向量化模型需要将大量的词汇信息压缩成固定长度的向量表示,导致无法精准捕捉文本的核心主题和关键细节。结果就是,生成的向量难以代表文本的重要内容,降低了模型对文本理解的准确性。
- 降低召回精度 :在检索阶段,系统需要根据用户的查询从向量数据库中检索相关文本。过长的文本块可能涵盖多个主题或观点,增加了语义的复杂性,导致检索模型难以准确匹配用户的查询意图。这样一来,召回的文本相关性下降,影响了大模型生成答案的质量。
- 输入受限 :大语言模型(LLM)对输入长度有严格的限制。过长的文本块会占据更多的输入空间,减少可供输入的大模型的文本块数量。这限制了模型能够获取的信息广度,可能导致遗漏重要的上下文或相关信息,影响最终的回答效果。
1.2 文本分块过短的影响
相反,过短的文本块也会对大模型的输出产生不利影响,具体表现为:
- 上下文缺失 :短文本块可能缺乏必要的上下文信息。上下文对于理解语言的意义至关重要,缺乏上下文的文本块会让模型难以准确理解文本的含义,导致生成的回答不完整或偏离主题。
- 主题信息丢失 :段落或章节级别的主题信息需要一定的文本长度来表达。过短的文本块可能只包含片段信息,无法完整传达主要观点或核心概念,影响模型对整体内容的把握。
- 碎片化问题 :大量的短文本块会导致信息碎片化,增加检索和处理的复杂度。系统需要处理更多的文本块,增加了计算和存储的开销。同时,过多的碎片化信息可能会干扰模型的判断,降低系统性能和回答质量。
通过上述分析,可以得出结论: 合理的文本分块策略是提升 RAG 系统性能和大模型回答质量的关键 。为了在实际应用中取得最佳效果,需要在以下方面进行权衡和优化:
- 根据文本内容选择切分策略 :不同类型的文本适合不同的切分方法。
- 逻辑紧密的文本 :对于论文、技术文档等段落内逻辑紧密的文本,应尽量保持段落的完整性,避免过度切分,以保留完整的语义和逻辑结构。
- 语义独立的文本 :对于法规条款、产品说明书等句子间逻辑相对独立的文本,可以按照句子进行切分。这种方式有助于精确匹配特定的查询内容,提高检索的准确性。
- 考虑向量化模型的性能 :评估所使用的向量化模型对于不同长度文本的处理能力。
- 长文本处理 :如果向量化模型在处理长文本时容易丢失信息,应适当缩短文本块的长度,以提高向量表示的精确度。
- 短文本优化 :对于能够有效处理短文本的模型,可以适当切分文本,但要注意保留必要的上下文信息。
- 关注大模型的输入限制 :大语言模型对输入长度有一定的限制,需要确保召回的文本块能够全部输入模型。
- 输入长度优化 :在切分文本块时,控制每个块的长度,使其既包含完整的语义信息,又不超过模型的输入限制。
- 信息覆盖 :确保切分后的文本块能够覆盖知识库中的关键信息,避免遗漏重要内容。
- 实验与迭代 :没有一种放之四海而皆准的最佳实践,需要根据具体的应用场景进行实验和调整。
- 性能评估 :通过实验评估不同切分策略对检索准确性和生成质量的影响,从而选择最适合的方案。
- 持续优化 :根据模型的表现和用户反馈,不断优化切分策略,提升系统的整体性能。
2. 常见的文本分块策略
在 RAG(Retrieval-Augmented Generation,检索增强生成)系统中,文本分块策略的选择对系统性能和大模型的生成质量有着重要影响。合理的文本分块能提高检索准确性,并为大模型生成提供更好的上下文支持。下面,将深入探讨几种常见的文本分块方法及其应用场景。
常用的文本分块方法包括: 固定大小分块、基于 NTLK 分块、特殊格式分块、深度学习模型分块、智能体式分块 。
2.1 固定大小文本切块(递归方法)
固定大小文本切块是最简单直观的文本分块方法。它按照预先设定的固定长度,将文本划分为若干块。这种方法实现起来相对容易,但在实际应用中,需要注意以下几点:
-
问题与挑战
- 上下文割裂 :简单地按照固定字符数截断文本,可能会打断句子或段落,导致上下文信息丢失。这会影响后续的文本向量化效果和语义理解。
- 语义完整性受损 :文本块可能包含不完整的句子或思想,影响检索阶段的匹配精度,以及大模型生成回答的质量。
-
改进方法
- 引入重叠 :在相邻文本块之间引入一定的重叠部分,确保上下文的连贯性。比如,每个文本块与前一个块有 50 个字符的重叠。这有助于保留句子的完整性和段落的连贯性。
- 智能截断 :在切分文本时,尽量选择在标点符号或段落结束处进行截断,而不是严格按照字符数。这可以避免打断句子,保持语义的完整性。
-
实践工具:LangChain 的 RecursiveCharacterTextSplitter**
大模型应用开发框架 LangChain 提供了 RecursiveCharacterTextSplitter
,优化了固定大小文本切块的缺陷,推荐在通用文本处理中使用。
使用示例 :
from langchain.text_splitter import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=200,
chunk_overlap=50,
length_function=len,
separators=["\n", "。", ""]
)
text = "..."# 待处理的文本
texts = text_splitter.create_documents([text])
for doc in texts:
print(doc)
参数说明 :
chunk_size
:文本块的最大长度(如 200 个字符)。chunk_overlap
:相邻文本块之间的重叠长度(如 50 个字符)。length_function
:用于计算文本长度的函数,默认为len
。separators
:定义了一组分割符列表,用于在切分文本时优先选择合适的位置。
工作原理 :
RecursiveCharacterTextSplitter
按照 separators
中的分割符顺序,递归地对文本进行切分:
- 初步切分 :使用第一个分割符(如
"\n"
,表示段落分隔)对文本进行初步切分。 - 检查块大小 :如果得到的文本块长度超过了
chunk_size
,则使用下一个分割符(如"。"
,表示句子分隔)进一步切分。 - 递归处理 :依次使用剩余的分割符,直到文本块长度符合要求或无法再切分。
- 合并块 :如果相邻的文本块合并后长度不超过
chunk_size
,则进行合并,确保块的长度尽可能接近chunk_size
,同时保留上下文完整性。
2.2 基于 NLTK 、spaCy 的文本切块
NLTK(Natural Language Toolkit) 是广泛使用的 Python 自然语言处理库,提供了丰富的文本处理功能。其中,sent_tokenize
方法可用于自动将文本切分为句子。
原理 :sent_tokenize
基于论文 《Unsupervised Multilingual Sentence Boundary Detection》 的方法,使用无监督算法为缩写词、搭配词和句子开头的词建立模型,然后利用这些模型识别句子边界。这种方法在多种语言(主要是欧洲语言)上都取得了良好效果。
- 预训练模型缺失 :NLTK 官方并未提供中文分句模型的预训练权重,需要用户自行训练。
- 训练接口可用 :NLTK 提供了训练接口,用户可以基于自己的中文语料库训练分句模型。
- 在 LangChain 中的应用
LangChain 集成了 NLTK 的文本切分功能,方便用户直接调用。
使用示例 :
from langchain.text_splitter import NLTKTextSplitter
text_splitter = NLTKTextSplitter()
text = "..."# 待处理的文本
texts = text_splitter.split_text(text)
for doc in texts:
print(doc)
- 扩展:基于 spaCy 的文本切块
spaCy 是另一款强大的自然语言处理库,具备更高级的语言分析能力。LangChain 也集成了 spaCy 的文本切分方法。
使用方法 :
只需将 NLTKTextSplitter
替换为 SpacyTextSplitter
:
from langchain.text_splitter import SpacyTextSplitter
text_splitter = SpacyTextSplitter()
text = "..."# 待处理的文本
texts = text_splitter.split_text(text)
for doc in texts:
print(doc)
提示:使用 spaCy 时,需要先下载对应语言的模型。例如,处理中文文本时,需要下载中文模型包。
2.3 特殊格式文本切块(HTML、Markdown)
在实际应用中,常常需要处理具有特殊内在结构的文本,如 HTML、Markdown、LaTeX、代码文件 等。这些文本的结构信息对于理解其内容至关重要,简单的文本切分方法可能会破坏其原有结构,导致上下文信息丢失。
- 保留结构信息 :在切分文本时,应尽量保留其内在的结构,如标签、标题、代码块等。
- 减少上下文损失 :避免在关键位置切分文本,以免丢失重要的上下文信息。
- LangChain 提供的特殊文本切块方法
LangChain 为用户提供了针对多种特殊格式文本的切块类,方便用户处理不同类型的文本。
表 4-1 LangChain 提供的特殊文本切块方法
文本格式 | 类名 |
---|---|
Python | PythonTextSplitter |
markdown | MarkdownTextSplitter |
latex | LatexTextSplitter |
html | HTMLHeaderTextSplitter |
以处理 Markdown 文本为例:
from langchain.text_splitter import MarkdownTextSplitter
text_splitter = MarkdownTextSplitter()
text = "..."# 待处理的 Markdown 文本
texts = text_splitter.split_text(text)
for doc in texts:
print(doc)
这些特殊文本切块类针对不同的文本格式,预设了适合的分割符列表,然后调用 RecursiveCharacterTextSplitter
进行进一步的切分。例如:
PythonCodeTextSplitter
:针对 Python 代码的结构,设置适合的分隔符,如函数定义、类定义、注释等。MarkdownTextSplitter
:根据 Markdown 的结构,如标题、列表、段落等,进行切分。LatexTextSplitter
:识别 LaTeX 文档的章节、公式、环境等进行切分。HTMLHeaderTextSplitter
:针对 HTML 文档的标签结构,按照元素层级进行切分。- 4.3.5 自定义扩展
LangChain 还预定义了其他编程语言(如 Go、C++、Java)等的分割符列表,方便用户快速定义新的文本切块类。如果需要处理未提供的文本格式,可以参照已有的类实现。
自定义示例 :创建一个用于切分 Java 代码的文本切块类。
from langchain.text_splitter import RecursiveCharacterTextSplitter
class JavaCodeTextSplitter(RecursiveCharacterTextSplitter):
def __init__(self, **kwargs):
separators = [
"\n\n", # 空行
"\n", # 换行
";", # 语句结束
" ", # 空格
""# 无分隔符
]
super().__init__(separators=separators, **kwargs)
text_splitter =