第三章:信息预处理——文本清洗、PDF解析与OCR识别
大家好,我是你们的老朋友。今天我们来聊聊研报Agent的“耳朵”和“眼睛”——信息预处理。
说实话,很多做量化的人,一上来就急着建模、调参数。但我个人习惯,花在数据清洗上的时间,至少占整个项目的40%。为什么?因为垃圾进,垃圾出。你喂给模型一堆乱码、重复文本、扫描歪了的PDF,它再聪明也白搭。
这一章,我们就来拆解三个核心环节:文本清洗(去噪、去重)、PDF解析、OCR识别。这三板斧抡好了,你的研报数据才算真正“能用”。
3.1 文本清洗:去噪与去重
先说说文本清洗。你以为从PDF里抽出来的文字就能直接用?太天真了。
我在项目中遇到过最离谱的情况:一份研报里,光“免责声明”就重复了8遍,中间还夹杂着“数据来源:Wind”、“分析师:张三”这种模板化噪音。如果不处理,模型会把“免责声明”当成核心内容去学习。
去噪,说白了就是干掉那些没用的东西。
- 规则过滤:用正则表达式干掉页眉页脚、表格边框、特殊符号(比如“■”、“●”)。
- 停用词移除:去掉“的”、“了”、“是”这类高频但无意义的词。但注意,金融领域要保留“风险”、“收益”这类关键词。
- 空白字符压缩:把多个空格、换行符合并成一个,避免模型被格式干扰。
核心原则:去噪不是越狠越好。我曾经把“同比增长”里的“同比”当成噪音去掉了,结果模型死活学不会趋势判断。所以,保留业务语义是第一位的。
去重,则是另一个大坑。
你想想看,同一份研报,可能被不同券商转载,内容一模一样。或者同一家券商,在不同时间点发布了内容高度相似的周报。如果不做去重,模型会严重过拟合。
我常用的方法是SimHash算法。它能把一段文本压缩成一个64位的指纹,然后通过计算海明距离来判断相似度。距离小于3的,基本就是重复内容。
# 一个简单的SimHash示例(伪代码)
def simhash(text):
# 1. 分词
words = tokenize(text)
# 2. 计算每个词的hash值
v = [0] * 64
for word in words:
hash_val = hash(word)
for i in range(64):
if hash_val & (1 << i):
v[i] += 1
else:
v[i] -= 1
# 3. 生成指纹
fingerprint = 0
for i in range(64):
if v[i] > 0:
fingerprint |= (1 << i)
return fingerprint
# 计算两个指纹的海明距离
def hamming_distance(fp1, fp2):
return bin(fp1 ^ fp2).count('1')
小技巧:对于研报这种长文本,我建议先按段落去重,再按全文去重。因为有时候整篇不重复,但某一段被反复引用,也会污染数据。
3.2 PDF解析:PyMuPDF vs pdfplumber
PDF解析,是研报Agent的“眼睛”。市面上工具很多,但我最常用的就两个:PyMuPDF(fitz)和pdfplumber。
为什么?因为它们各有绝活。
| 特性 | PyMuPDF | pdfplumber |
|---|---|---|
| 解析速度 | 极快(C语言底层) | 中等(Python底层) |
| 表格提取 | 一般(需手动处理) | 优秀(自动识别表格结构) |
| 文本布局 | 保留原始位置信息 | 保留字体、字号、位置 |
| 适用场景 | 纯文本、快速批量处理 | 复杂表格、带格式的研报 |
我个人习惯:如果研报主要是文字,比如宏观分析、策略点评,我用PyMuPDF,因为它快,一秒能解析几十页。但如果研报里有大量表格,比如财务数据、估值对比,我必用pdfplumber。
举个例子,用pdfplumber提取表格数据:
import pdfplumber
with pdfplumber.open("report.pdf") as pdf:
for page in pdf.pages:
tables = page.extract_tables()
for table in tables:
for row in table:
print(row) # 每一行是一个列表
避坑指南:我曾经用PyMuPDF解析一份带水印的研报,结果水印文字和正文混在一起,模型把“机密文件”当成了正文内容。后来我改用pdfplumber,通过设置page.filter(lambda obj: obj['object_type'] != 'image')过滤掉水印层,才解决问题。
3.3 OCR识别:PaddleOCR实战
最后说说OCR。很多研报是扫描件,或者图片格式的PDF。这时候,PyMuPDF和pdfplumber都无能为力,必须上OCR。
我推荐PaddleOCR。为什么?因为它开源、免费、中文识别率极高,而且支持GPU加速。
它的核心逻辑很简单:先检测出图片中的文字区域,再识别每个区域里的文字。
from paddleocr import PaddleOCR
# 初始化OCR模型
ocr = PaddleOCR(use_angle_cls=True, lang='ch')
# 识别图片
result = ocr.ocr('scanned_report.png', cls=True)
# 提取文字
for line in result:
for word_info in line:
text = word_info[1][0]
confidence = word_info[1][1]
print(f"文字: {text}, 置信度: {confidence:.2f}")
嗯,这里要注意:置信度阈值。我一般设置0.8以上才保留,低于这个值的,要么是模糊的,要么是识别错了。曾经有一次,PaddleOCR把“净利润”识别成了“净利闰”,就是因为置信度只有0.6,我没过滤掉。
实战建议:对于扫描件PDF,我通常先转成图片,再用PaddleOCR批量识别。如果图片分辨率太低,可以先用OpenCV做一次超分辨率处理,识别率能提升10%以上。
3.4 本章知识体系
为了让你更直观地理解这三者的关系,我画了一张流程图:
你看,整个流程其实不复杂。先判断输入是PDF还是图片,然后选择合适的工具解析,最后统一做清洗。但每一步都有坑,我踩过的,希望你别再踩。
好了,这一章就到这里。记住:数据预处理做得好,模型训练没烦恼。下一章我们聊聊如何把这些清洗好的文本,喂给大模型做摘要生成。
公众号:蓝海资料掘金营,微信deep3321