文本分割:构建Rag应用的关键第一步
在大型语言模型应用中,Embedding技术扮演着至关重要的角色。但在此之前,有一个同样重要的步骤往往被忽视——文本分割。本文将深入探讨文本分割的重要性,并结合实际代码示例展示不同的分割策略。
什么是Embedding?
Embedding是将文本转换为数值向量表示的过程,这些向量能够捕捉文本的语义信息。通过Embedding,我们可以:
-
计算文本之间的相似度
-
进行语义搜索和检索
-
作为机器学习模型的输入特征
-
构建推荐系统和分类器
文本分割策略详解
1. 基于Token数量的分割
from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import TokenTextSplitter
# 加载文件
load = TextLoader("公司人事管理流程章程.txt", encoding="utf-8")
documents = load.load()
# 按token数量分割
print("使用TokenTextSplitter按token分割")
print("*" * 200)
# 创建一个基于token的文本分割器实例
# 该分割器用于将长文本按照指定的token数量进行分割
#
# 参数说明:
# encoding_name: 指定使用的编码器名称,"cl100k_base"是OpenAI的编码器
# chunk_size: 每个文本块的最大token数量,设置为100个token
# chunk_overlap: 相邻文本块之间的重叠token数量,设置为20个token
#
# 返回值:
# TokenTextSplitter实例,可用于分割文本为指定大小的token块
text_splitter = TokenTextSplitter(encoding_name="cl100k_base", chunk_size=100, chunk_overlap=20)
split_documents = text_splitter.split_documents(documents)
for i in split_documents:
print(i.page_content)
print("-" * 50)
适用场景:当需要严格控制输入模型的token数量时,这种方法非常有效,特别适合有严格token限制的API调用。
2. 基于段落的分割
from langchain_text_splitters import RecursiveCharacterTextSplitter
print("*" * 200)
print("使用RecursiveCharacterTextSplitter按段落(\\n\\n)分割")
# 创建一个递归字符文本分割器实例,用于将长文本分割成指定大小的块
#
# 参数说明:
# separators: 列表类型,指定文本分割的分隔符,这里使用双换行符"\n\n"作为主要分隔符
# chunk_size: 整数类型,指定每个文本块的最大字符数为300
# chunk_overlap: 整数类型,指定相邻文本块之间的重叠字符数为4,用于保持上下文连续性
#
# 该分割器会优先使用指定的分隔符进行分割,当无法按分隔符分割且文本块超过大小限制时,
# 会使用递归方式在字符级别进行分割,确保所有文本块都不超过指定大小
text_splitter = RecursiveCharacterTextSplitter(separators=["\n\n"], chunk_size=300, chunk_overlap=50)
split_documents = text_splitter.split_documents(documents)
for i in split_documents:
print(i.page_content)
print("-" * 50)
适用场景:处理结构化文档时,保持段落的完整性非常重要,这种方法能确保语义连贯性。
3. 基于自定义标识符的分割
print("*" * 200)
print("使用RecursiveCharacterTextSplitter按自定义标识符 '---#*split*#---' 分割")
text_loader = TextLoader("公司人事管理流程章程-V1.txt", encoding="utf-8")
documents = text_loader.load()
# 创建一个递归字符文本分割器实例,用于将长文本按照指定分隔符和块大小进行分割
#
# 参数说明:
# separators: 列表类型,包含文本分割时使用的分隔符字符串"---#*split*#---"
# chunk_size: 整数类型,指定每个文本块的最大字符数为200
# chunk_overlap: 整数类型,指定相邻文本块之间的重叠字符数为20,用于保持上下文连续性
#
# 该分割器会递归地尝试使用分隔符列表中的字符来分割文本,直到满足块大小要求
text_splitter = RecursiveCharacterTextSplitter(separators=["---#*split*#---"], chunk_size=200, chunk_overlap=20)
split_documents = text_splitter.split_documents(documents)
for i in split_documents:
print(i.page_content.replace("---#*split*#---", ""))
print("-" * 50)
适用场景:当文档有特定的结构或标记时,使用自定义分隔符可以精确控制分割位置。
文本分割的最佳实践
1. 选择合适的块大小
-
太小:可能失去上下文信息
-
太大:可能包含不相关的内容,且处理成本高
2. 设置合理的重叠区域
重叠区域确保重要信息不会在分割边界丢失,通常设置为块大小的10-20%。
3. 保持语义完整性
优先在自然边界(如段落、章节)处分割,而不是简单地在固定位置切割。
4. 考虑后续应用
不同的下游任务可能需要不同的分割策略:
-
问答系统:需要更细粒度的分割
-
文档摘要:可以接受较大的文本块
-
语义搜索:平衡精度和召回率
实际应用建议
-
实验不同参数:针对你的具体数据,测试不同的chunk_size和overlap设置
-
评估分割质量:不仅要看技术指标,还要评估分割后的语义连贯性
-
结合领域知识:了解你的文档结构特点,选择最合适的分割策略
-
监控性能:在实际应用中监控检索质量和系统性能,持续优化
结论
文本分割是Embedding流水线中至关重要但常被忽视的一环。合适的分割策略能显著提升后续Embedding和检索的效果。通过理解不同的分割方法及其适用场景,你可以为你的应用选择最合适的策略。
记住,没有一种分割策略适合所有场景。最好的方法是通过实验和评估,找到最适合你特定需求和数据的方案。随着LangChain等工具库的发展,文本分割变得更加简单和灵活,为构建高效的NLP应用提供了强大基础。
更多推荐
所有评论(0)