代码1 cleaning.py:
def clean_text(text: str) -> str:
text = re.sub(r"[^\w\s.,!?]", " ", text)
text = re.sub(r"\s+", " ", text)
return text.strip()
代码2 chunking.py:
from langchain.text_splitter import RecursiveCharacterTextSplitter, SentenceTransformersTokenTextSplitter
def chunk_text(text: str, chunk_size: int = 500, chunk_overlap: int = 50) -> list[str]:
character_splitter = RecursiveCharacterTextSplitter(separators=["\n\n"], chunk_size=chunk_size, chunk_overlap=0)
text_split_by_characters = character_splitter.split_text(text)
token_splitter = SentenceTransformersTokenTextSplitter(
chunk_overlap=chunk_overlap,
tokens_per_chunk=embedding_model.max_input_length,
model_name=embedding_model.model_id,
)
chunks_by_tokens = []
for section in text_split_by_characters:
chunks_by_tokens.extend(token_splitter.split_text(section))
return chunks_by_tokens
def chunk_document(text: str, min_length: int, max_length: int) -> list[str]:
"""Alias for chunk_article()."""
return chunk_article(text, min_length, max_length)
def chunk_article(text: str, min_length: int, max_length: int) -> list[str]:
sentences = re.split(r"(?, text)
extracts = []
current_chunk = ""
for sentence in sentences:
sentence = sentence.strip()
if not sentence:
continue
if len(current_chunk) + len(sentence) <= max_length:
current_chunk += sentence + " "
else:
if len(current_chunk) >= min_length:
extracts.append(current_chunk.strip())
current_chunk = sentence + " "
if len(current_chunk) >= min_length:
extracts.append(current_chunk.strip())
return extracts
文本预处理的核心操作函数,主要有两个文件:cleaning.py 和 chunking.py。
这两个文件包含的函数就像是文本处理的"工具箱",负责清洗和分割文本。
def clean_text(text: str) -> str:
text = re.sub(r"[^\w\s.,!?]", " ", text)
text = re.sub(r"\s+", " ", text)
return text.strip()
这个函数做了三件事:
假设我们有这样一段文本:
Hello world! This is a #test@ with some $special% characters.
经过 clean_text
处理后,会变成:
Hello world! This is a test with some special characters.
注意以下变化:
#@$%
都被替换为空格.!
被保留了这个函数的目的是让文本更干净、更标准化,便于后续处理。
这个文件包含了几个函数,用于将长文本分割成更小的块。
def chunk_text(text: str, chunk_size: int = 500, chunk_overlap: int = 50) -> list[str]:
character_splitter = RecursiveCharacterTextSplitter(separators=["\n\n"], chunk_size=chunk_size, chunk_overlap=0)
text_split_by_characters = character_splitter.split_text(text)
token_splitter = SentenceTransformersTokenTextSplitter(
chunk_overlap=chunk_overlap,
tokens_per_chunk=embedding_model.max_input_length,
model_name=embedding_model.model_id,
)
chunks_by_tokens = []
for section in text_split_by_characters:
chunks_by_tokens.extend(token_splitter.split_text(section))
return chunks_by_tokens
这个函数分两步进行文本分块:
\n\n
)分割文本,每块最大500字符假设我们有一篇1500字符的文章,包含3个段落:
处理过程:
def chunk_article(text: str, min_length: int, max_length: int) -> list[str]:
sentences = re.split(r"(?, text)
extracts = []
current_chunk = ""
for sentence in sentences:
sentence = sentence.strip()
if not sentence:
continue
if len(current_chunk) + len(sentence) <= max_length:
current_chunk += sentence + " "
else:
if len(current_chunk) >= min_length:
extracts.append(current_chunk.strip())
current_chunk = sentence + " "
if len(current_chunk) >= min_length:
extracts.append(current_chunk.strip())
return extracts
这个函数更智能地分割文章:
max_length
min_length
假设我们有以下文本:
我喜欢编程。Python是一种很棒的语言。它简单易学。我每天都在使用它。机器学习是一个有趣的领域。深度学习更是令人着迷。
假设 min_length=50
,max_length=100
,每个句子的长度如下:
处理过程:
max_length=100
)min_length=50
)最终得到两个文本块:
块1:
我喜欢编程。Python是一种很棒的语言。它简单易学。我每天都在使用它。
块2:
机器学习是一个有趣的领域。深度学习更是令人着迷。
def chunk_document(text: str, min_length: int, max_length: int) -> list[str]:
"""Alias for chunk_article()."""
return chunk_article(text, min_length, max_length)
这只是 chunk_article
函数的一个别名,功能完全相同。
这些函数在项目中的作用非常关键:
这些预处理操作是构建高效 RAG(检索增强生成) 系统的基础。
通过合理的清洗和分块,可以:
简单来说,这些函数就像是文本的"切菜师傅",它们把原始的长文本"切"成大小合适、干净整洁的小块,为后续的 AI 模型处理做好准备。
它们都用于将长文本分割成更小的块,但采用了不同的策略和目的。
分割策略不同
chunk_text 方法:
def chunk_text(text: str, chunk_size: int = 500, chunk_overlap: int = 50) -> list[str]:
character_splitter = RecursiveCharacterTextSplitter(separators=["\n\n"], chunk_size=chunk_size, chunk_overlap=0)
text_split_by_characters = character_splitter.split_text(text)
token_splitter = SentenceTransformersTokenTextSplitter(
chunk_overlap=chunk_overlap,
tokens_per_chunk=embedding_model.max_input_length,
model_name=embedding_model.model_id,
)
chunks_by_tokens = []
for section in text_split_by_characters:
chunks_by_tokens.extend(token_splitter.split_text(section))
return chunks_by_tokens
\n\n
)分割RecursiveCharacterTextSplitter
和 SentenceTransformersTokenTextSplitter
chunk_size=500
(字符),chunk_overlap=50
(token)chunk_article 方法:
def chunk_article(text: str, min_length: int, max_length: int) -> list[str]:
sentences = re.split(r"(?, text)
extracts = []
current_chunk = ""
for sentence in sentences:
sentence = sentence.strip()
if not sentence:
continue
if len(current_chunk) + len(sentence) <= max_length:
current_chunk += sentence + " "
else:
if len(current_chunk) >= min_length:
extracts.append(current_chunk.strip())
current_chunk = sentence + " "
if len(current_chunk) >= min_length:
extracts.append(current_chunk.strip())
return extracts
min_length
和 max_length
参数不同
chunk_text
使用字符数和重叠 token 数作为参数chunk_article
使用最小和最大字符长度作为参数应用场景不同
chunk_text
更适合用于嵌入模型处理,因为它考虑了 token 限制chunk_article
更适合用于生成摘要或提取,因为它保持了句子的完整性目的相同:两者都是为了将长文本分割成更小的、可管理的块
别名关系:chunk_document
是 chunk_article
的别名
def chunk_document(text: str, min_length: int, max_length: int) -> list[str]:
"""Alias for chunk_article()."""
return chunk_article(text, min_length, max_length)
尊重文本自然边界:
chunk_text
尊重段落边界chunk_article
尊重句子边界预处理管道中的角色:它们都是文本处理管道的一部分
假设有一篇包含多个段落的长文章:
人工智能正在改变世界。它影响了从医疗到金融的各个领域。
机器学习是AI的一个子领域。深度学习是机器学习中特别强大的一种方法。它基于神经网络,可以学习复杂的模式。
自然语言处理让计算机能够理解人类语言。这使得像聊天机器人这样的应用成为可能。它还支持机器翻译和情感分析。
使用 chunk_text
处理:
\n\n
分割成3个段落使用 chunk_article
处理:
min_length
和 max_length
合并句子这两个方法可以同时使用,它们在不同的处理阶段有各自的优势。
chunk_article
按语义分块,再用 chunk_text
确保每块符合嵌入模型的要求chunk_article
处理要展示给用户的内容chunk_text
处理要输入到模型的内容假设我们有一篇长文章,需要先按语义分块,然后确保每块都适合嵌入模型处理:
from llm_engineering.application.preprocessing.operations.chunking import chunk_article, chunk_text
def process_long_document(document: str) -> list[str]:
"""
处理长文档的两阶段分块策略:
1. 首先使用chunk_article按语义分块,保持句子完整性
2. 然后使用chunk_text确保每块不超过嵌入模型的限制
"""
# 第一阶段:按语义分块,每块500-2000字符
semantic_chunks = chunk_article(document, min_length=500, max_length=2000)
# 第二阶段:确保每块适合嵌入模型
final_chunks = []
for chunk in semantic_chunks:
# 如果语义块太长,进一步分割
if len(chunk) > 1000: # 假设1000字符是一个安全阈值
sub_chunks = chunk_text(chunk, chunk_size=800, chunk_overlap=100)
final_chunks.extend(sub_chunks)
else:
final_chunks.append(chunk)
return final_chunks
# 使用示例
long_article = """
人工智能(AI)正在彻底改变我们的生活和工作方式。从智能手机上的语音助手到自动驾驶汽车,AI技术无处不在。
机器学习是AI的核心技术之一。它允许计算机从数据中学习,而无需明确编程。深度学习是机器学习的一个子集,它使用多层神经网络来模拟人脑的工作方式。这些网络可以识别模式、分类数据并做出预测。
自然语言处理(NLP)是AI的另一个重要分支。它使计算机能够理解、解释和生成人类语言。NLP技术支持了从机器翻译到情感分析的各种应用。最近的大型语言模型(LLM)如GPT和BERT在这一领域取得了突破性进展。
计算机视觉让机器能够"看到"和理解视觉世界。它在医疗诊断、安全监控和自动驾驶等领域有广泛应用。结合深度学习技术,现代计算机视觉系统可以识别物体、人脸和活动,有时甚至比人类更准确。
强化学习是AI的另一个关键领域,它专注于如何通过试错来学习最佳行动。这种方法已被用于训练AI玩游戏、控制机器人和优化系统。
尽管AI技术取得了令人印象深刻的进展,但它也面临着伦理、隐私和安全方面的挑战。确保AI的负责任发展和使用是研究人员、企业和政策制定者的共同责任。
"""
chunks = process_long_document(long_article)
for i, chunk in enumerate(chunks):
print(f"块 {i+1} ({len(chunk)} 字符):")
print(chunk[:100] + "..." if len(chunk) > 100 else chunk)
print("-" * 50)
假设我们在构建一个 RAG 系统,需要同时为用户展示和为模型准备内容:
from llm_engineering.application.preprocessing.operations.chunking import chunk_article, chunk_text
def prepare_document_for_rag(document: str):
"""
为RAG系统准备文档:
1. 使用chunk_article创建用户友好的摘要块
2. 使用chunk_text创建适合嵌入模型的块
"""
# 为用户界面创建可读性好的块
user_friendly_chunks = chunk_article(document, min_length=200, max_length=1000)
# 为嵌入模型创建优化的块
embedding_chunks = chunk_text(document, chunk_size=500, chunk_overlap=50)
# 创建映射关系,将每个嵌入块关联到最相似的用户友好块
chunk_mapping = {}
for emb_chunk in embedding_chunks:
best_match = None
max_overlap = 0
for uf_chunk in user_friendly_chunks:
# 简单计算文本重叠度
overlap = sum(1 for word in emb_chunk.split() if word in uf_chunk.split())
if overlap > max_overlap:
max_overlap = overlap
best_match = uf_chunk
chunk_mapping[emb_chunk] = best_match
return {
"user_chunks": user_friendly_chunks, # 用于展示给用户
"embedding_chunks": embedding_chunks, # 用于向量数据库
"mapping": chunk_mapping # 关联两种块
}
# 使用示例
document = """
检索增强生成(RAG)是一种结合了检索系统和生成AI的强大技术。它通过从外部知识库检索相关信息来增强语言模型的输出。
RAG的工作原理是首先将查询发送到检索系统,该系统从知识库中找到相关文档。然后,这些文档与原始查询一起提供给语言模型,使其能够生成更准确、更相关的回答。
这种方法解决了大型语言模型的一个关键限制:它们只能基于训练数据生成内容,而训练数据可能过时或不完整。通过RAG,模型可以访问最新和专业的信息。
实现高效的RAG系统需要几个关键组件:文档处理管道、嵌入模型、向量数据库和生成模型。文档处理包括清洗、分块和嵌入文本。向量数据库存储这些嵌入,并支持相似性搜索。
RAG系统的性能很大程度上取决于文本分块的质量。块太大会包含太多信息,可能稀释相关内容;块太小可能会丢失上下文。找到合适的平衡是构建有效RAG系统的关键。
"""
result = prepare_document_for_rag(document)
print(f"用户友好块数量: {len(result['user_chunks'])}")
print(f"嵌入模型块数量: {len(result['embedding_chunks'])}")
print("\n示例用户友好块:")
print(result['user_chunks'][0][:150] + "..." if len(result['user_chunks'][0]) > 150 else result['user_chunks'][0])
print("\n示例嵌入块:")
print(result['embedding_chunks'][0][:150] + "..." if len(result['embedding_chunks'][0]) > 150 else result['embedding_chunks'][0])
想象你是一个图书管理员,需要整理一本厚重的百科全书:
chunk_article 就像按章节分割:
chunk_text 就像制作索引卡片:
同时使用两种方法就像你既为人类读者准备了章节目录,又为电脑检索系统准备了索引卡片:
这种组合使用的方法既满足了人类对内容连贯性和可读性的需求,又满足了 AI 模型对输入大小和格式的技术要求,是构建高效 RAG 系统的常用策略。