图片PDF向量化处理的探索与实践
在构建AI本地知识库时,我们不可避免地需要对PDF文件进行处理。PDF文件大致分为两种:扫描的图片文件和非图片文件。对于非图片类型的PDF,可以直接提取文本并进行向量化处理;但对于图片类型的PDF(如扫描件),处理起来就复杂得多。
问题背景
图片类型的PDF文件通常存在以下问题:
- 扫描件可能不是一页一页扫描的,而是多页合并在一起。
- 文字方向多样,可能包含横排、竖排甚至不同语言的文字。
- 不同段落的文字容易被错误合并成一行,导致内容混乱。
幸运的是,借助现代AI技术,我们可以高效地解决这些问题。
处理流程
以下是图片PDF向量化处理的整体流程:
graph TD
A[PDF输入] --> B[文本检测+方向分类+文本识别]
B --> D[Ai整理合并]
D --> E[小模型Ai校对]
E --校对失败--> D
E --校对成功--> F[Ai输出清理]
F --> G[小模型校验]
G --校验失败--> F
G --校验成功--> H[输出整理完成的文档]
H --> I[向量化]
-
PDF输入
将PDF文件作为输入源。 -
文本检测与方向分类
使用OCR技术检测文字区域,并识别文字方向。提取文字内容,生成初步的文本数据。 -
AI整理与合并
使用大模型对提取的文本进行整理和合并,确保段落结构正确。 -
小模型校对
使用小模型对整理后的文本进行校对。如果校对失败,则重新执行AI整理与合并步骤。 -
AI输出清理
对文本进行进一步清理,去除冗余信息。 -
小模型校验
再次使用小模型对清理后的文本进行校验。如果校验失败,则重新执行清理步骤。 -
输出整理完成的文档
将最终整理好的文档输出。 -
向量化
将整理完成的文档转化为向量形式,用于后续的知识库构建。
技术选型
1. 文本检测与识别
经过多次测试,发现百度飞桨(PaddlePaddle)的PP-OCR模型效果最佳,能够很好地识别繁体中文和其他复杂字符。相比一些收费的OCR工具,PP-OCR的表现更加出色。
2. AI整理与合并
为了确保文本整理的准确性,我选择了通义千问的Qwen2.5-72B-Instruct模型。该模型具有强大的指令遵循能力,并且通过将temperature设置为0,可以严格遵循输入文档的结构,避免不必要的改动。
3. 校对与校验
在校对环节,我使用了DeepSeek-R1:7B模型。经过测试,即使是较小的DeepSeek-R1:1.5B模型也能满足需求,但为了保险起见,最终选择了7B版本。
示例代码
以下是关键步骤的部分代码示例:
文本检测与识别
from paddleocr import PaddleOCR
# OCR核心配置
ocr = PaddleOCR(
use_angle_cls=True, # 自动旋转文本方向
lang="ch", # 支持繁体识别
det_model_dir='ch_PP-OCRv4_det_infer', # 文本检测模型
rec_model_dir='ch_PP-OCRv4_rec_infer', # 文本识别模型
gpu_mem=4000 # 控制显存占用
)
AI整理与合并
def merge_text_with_ai(blocks_metadata, page_size, model_name=MERGE_MODEL):
"""智能版面分析及多语言文本合并"""
system_prompt = """你是一个专业的OCR文本处理专家,擅长分析和重组OCR识别的文本块。
你的任务是将散乱的文本块重新组织成有序、连贯的文章。
只能输出重组后的纯文本内容,不要解释处理过程。"""
user_prompt = """# OCR文本重组任务
【输入信息】
页面尺寸:{width}x{height}
文本块数据:
{blocks_metadata}
【重组规则】
1. 文本分析
- 识别段落关系:通过内容连贯性和位置关系
- 重排段落:基于语义和排版位置
2. 内容处理
- 合并断行:修复被错误分割的句子
- 保持段落完整性
3. 质量要求
- 保持语义连贯性
- 避免重复内容
- 维护完整段落结构
- 保证内容完整不遗漏"""
# 初始化merged变量
merged = ""
original_text = '\n'.join(block['text'] for block in blocks_metadata)
for attempt in range(MAX_RETRIES):
try:
print("开始合并文本...")
response = client_sf.chat.completions.create(
model=model_name,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt.format(
width=page_size[0],
height=page_size[1],
blocks_metadata=blocks_metadata # 修正变量名
)}
],
temperature=0,
timeout=MODEL_TIMEOUT
)
merged = response.choices[0].message.content.strip()
# 后处理流程
merged = re.sub(r'<think>.*?</think>', '', merged, flags=re.DOTALL)
merged = re.sub(r'[※★◆▁▂▃▄▅▆▇█▏▎▍▌▋▊▉]+', '', merged)
merged = re.sub(r'[,,]+', ',', merged)
merged = re.sub(r'\n{3,}', '\n\n', merged)
if validate_output(original_text, merged):
print("校验通过,返回合并结果")
return merged
print(f"校验未通过,开始第{attempt+1}次重试...")
except Exception as e:
if 'timeout' in str(e).lower():
logging.error(f"AI响应超时({MODEL_TIMEOUT}秒),尝试第{attempt+1}次重试...")
else:
logging.error(f"合并失败: {str(e)}")
if attempt == MAX_RETRIES - 1:
break
time.sleep(RETRY_BASE_DELAY ** attempt)
return original_text # 如果所有尝试都失败,返回原始文本
小模型校对
def validate_output(original_text, processed_text):
"""严格单结果校验"""
system_prompt = """你是一个严格的文本质量检查员。
你的任务是对比原始文本和处理后的文本,检查是否存在内容缺失、非法删除或重复问题。
只需回答"是"或"否",不需要解释原因。"""
user_prompt = """请对比原文和处理后文本,严格检查:
1. 内容缺失:处理后文本是否删除了原文中的句子
2. 非法删除:是否错误删除正文内容
3. 重复问题:是否产生了原文中不存在的重复内容
▼原始文本▼
{original_sample}
▼处理后文本▼
{processed_sample}
如果存在任何一个问题,请回答[否]
如果全部符合要求,请回答[是]
仅允许输出[是]或[否]"""
try:
print("开始校验...")
print("原始文本:",original_text.replace('\n', ' '))
print("处理后文本:",processed_text.replace('\n', ' '))
response = client_local.chat.completions.create(
model=VALIDATE_MODEL,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt.format(
original_sample=original_text[:300].replace('\n', ' '),
processed_sample=processed_text[:500].replace('\n', ' ')
)}
],
temperature=0,
timeout=VALIDATE_TIMEOUT
)
# 严格清洗响应
print("校验结果:",response.choices[0].message.content)
clean_res = re.sub(r'<think>.*?</think>', '', response.choices[0].message.content, flags=re.DOTALL)
clean_res = re.sub(r'[^是否]', '', clean_res)
return '否' not in clean_res
except Exception as e:
logging.error(f"校验失败: {str(e)}")
return False
数据清理
def clean_text_with_ai(text_chunk, model_name=CLEAN_MODEL):
"""基于上下文优化OCR文本的专业清理"""
system_prompt = """你是一个专业的OCR文本清理专家。
你的任务是优化和清理OCR识别的文本,修正错误并提高文本质量。
直接输出清理后的纯文本,不要解释,不要道歉,不要表明你是AI。"""
user_prompt = """请处理OCR识别文本:
【页面特征】
1. 可能存在:竖排文字、印章标记、装订线痕迹
2. 典型干扰:水渍斑点、页码标记、装订孔洞
3. 专业术语:命理学术语(如"正官格"、"伤官见官")
【处理规则】
1. 删除:
- 页码标记(如"- 5 -", "Page 12")
- 版权信息(ISBN/CIP/条形码)
- 装订线痕迹/扫描伪影
- 空白行和多余空格
2. 修正:
- 排版错误(合并错误换行的句子)
- 形近字错误(己→已,未→末)
- 排版错误(合并被错误换行的句子)
3. 保留:
- 专业术语原貌
- 重要标点(。!?「」)
4. 根据上下文修复残缺文本
【完整上下文】▼
{full_context}
【待优化段落】▼
{target_text}"""
try:
response = client_sf.chat.completions.create(
model=model_name,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt.format(
full_context=text_chunk['context'],
target_text=text_chunk['target']
)}
],
temperature=0.2,
timeout=MODEL_TIMEOUT
)
# 后处理流程
cleaned = response.choices[0].message.content.strip()
cleaned = re.sub(r'<think>.*?</think>', '', cleaned, flags=re.DOTALL) # 移除think标签
cleaned = re.sub(r'[※★◆]+', '', cleaned) # 去除装饰符号
return re.sub(r'\n{3,}', '\n\n', cleaned) # 标准化换行
except Exception as e:
logging.warning(f"清理请求失败: {str(e)}")
return text_chunk['target'] # 失败时返回原始文本
原始图像

数据合并后效果


浙公网安备 33010602011771号