在处理长文本文件(如PDF)时,我们可能会遇到文本长度超过语言模型的上下文窗口的问题。为了解决这一问题,我们可以考虑以下策略:
- 更换LLM:选择支持更大上下文窗口的LLM。
- 暴力拆分:将文档分块,并从每个块中提取内容。
- RAG(检索增强生成):分块文档,索引这些块,然后仅从看似“相关”的子集块中提取内容。
本文将展示如何实现策略2和策略3。
技术背景介绍
在大多数实际场景中,长文本处理的挑战主要体现在如何有效分割并处理文本数据,以便在不影响准确度的情况下最大化信息提取。上下文窗口的限制促使我们寻找替代方法来解决这一问题。
核心原理解析
暴力拆分:通过将文本分块,使每个块能够适应模型的上下文窗口,从而逐块提取内容。
RAG方法:通过向量存储索引文本块,并添加检索步骤以确保处理最相关的文本块。
代码实现演示
初始化环境
首先,我们需要下载一些示例数据并加载为文档。
import re
import requests
from langchain_community.document_loaders import BSHTMLLoader
# 下载示例数据
response = requests.get("https://en.wikipedia.org/wiki/Car")
# 保存到本地文件
with open("car.html", "w", encoding="utf-8") as f:
f.write(response.text)
# 使用HTML解析器加载文档
loader = BSHTMLLoader("car.html")
document = loader.load()[0]
# 清理文档内容
document.page_content = re.sub("\n\n+", "\n", document.page_content)
print(len(document.page_content)) # 输出文档长度
信息抽取的模式定义
使用Pydantic定义我们希望从文本中抽取的信息模式,包括年份、描述以及相关证据。
from typing import List
from langchain_core.pydantic_v1 import BaseModel, Field
class KeyDevelopment(BaseModel):
"""汽车历史发展的信息。"""
year: int = Field(..., description="历史发展的年份。")
description: str = Field(..., description="这一年发生了什么发展。")
evidence: str = Field(..., description="提取年份和描述信息的句子。")
class ExtractionData(BaseModel):
"""提取的汽车历史发展信息。"""
key_developments: List[KeyDevelopment]
定义抽取器和分块
选择并设置一个支持工具调用的LLM,然后创建一个信息抽取器。
from langchain_openai import ChatOpenAI
client = openai.OpenAI(
base_url='https://yunwu.ai/v1', # 国内稳定访问
api_key='your-api-key'
)
llm = ChatOpenAI(model="gpt-4-0125-preview", temperature=0)
# 暴力拆分文档
from langchain_text_splitters import TokenTextSplitter
text_splitter = TokenTextSplitter(chunk_size=2000, chunk_overlap=20)
texts = text_splitter.split_text(document.page_content)
# 并行抽取信息
extractions = extractor.batch(
[{"text": text} for text in texts],
{"max_concurrency": 5} # 限制并发
)
RAG方法实现
使用FAISS向量存储来索引文档块,并执行检索增强生成(RAG)步骤。
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings
texts = text_splitter.split_text(document.page_content)
vectorstore = FAISS.from_texts(texts, embedding=OpenAIEmbeddings())
retriever = vectorstore.as_retriever(search_kwargs={"k": 1})
rag_extractor = {"text": retriever | (lambda docs: docs[0].page_content)} | extractor
results = rag_extractor.invoke("汽车相关的关键发展")
应用场景分析
暴力拆分适用于全文都可能含有有用信息的场景,而RAG则适用于文本内信息密度较低且关键信息分布不均的场景。
实践建议
- 在使用暴力拆分时,要注意块间重叠可能导致重复数据的提取。
- 在RAG方法中,选择合适的检索策略以确保对文本块的相关信息进行合理筛选。
如果遇到问题欢迎在评论区交流。
—END—
1088

被折叠的 条评论
为什么被折叠?



