文本预处理:让机器读懂金融文本的必修课
文本预处理,说白了就是给原始文本「洗澡」。金融文本里充斥着各种噪音——乱码、特殊符号、无意义的语气词,还有那些让分词器头疼的专有名词。我刚开始做金融NLP时,觉得这步可有可无,直到模型在「科创板」和「创业板」上频频翻车……嗯,从那以后我再也不敢跳过预处理了。
核心要点:文本预处理的质量,直接决定了RAG系统的检索精度。你喂给模型什么,它就消化什么。垃圾进,垃圾出。
1. 文本清洗:先去掉那些没用的东西
金融文本来源五花八门——研报、公告、新闻、论坛帖子。每个来源都有自己的「脏数据」特点。我个人习惯先做这几步:
- 去除HTML标签:爬虫拿到的文本经常夹带<div>、<p>之类的标签
- 统一全角半角:中文全角逗号和英文半角逗号混在一起,分词器会懵
- 过滤特殊字符:比如「◆」「★」这些装饰符号,对语义理解没帮助
- 去除多余空白:连续的空格、换行符,压缩成一个空格就行
我的经验:清洗规则别写太死。我在项目中遇到过一份财报,里面用「◆」标记重要数据,直接删掉反而丢了关键信息。建议先做数据探索,再定清洗策略。
import re
def clean_text(text):
# 去除HTML标签
text = re.sub(r'<[^>]+>', '', text)
# 统一全角半角
text = text.replace(',', ',').replace('。', '.').replace(';', ';')
# 过滤特殊字符(保留中文、英文、数字、常见标点)
text = re.sub(r'[^\u4e00-\u9fa5a-zA-Z0-9\s,.;:!?()()【】《》]', '', text)
# 压缩多余空白
text = re.sub(r'\s+', ' ', text).strip()
return text
2. 中文分词:jieba 的正确打开方式
中文不像英文天然有空格分隔。分词是第一步,也是最容易出问题的一步。jieba 是主流工具,但默认词典对金融领域不太友好。
为什么会这样?因为「做多」「量化宽松」「逆回购」这些词,jieba 默认会切成单字或错误组合。你想想看,「逆回购」被切成「逆」「回购」,检索时根本匹配不到正确的金融实体。
2.1 加载自定义词典
我建议把金融专有名词整理成一个词典文件,每行一个词,格式如下:
量化宽松 5 n
逆回购 5 n
科创板 5 n
注册制 5 n
北向资金 5 n
加载方式很简单:
import jieba
# 加载自定义词典
jieba.load_userdict('finance_dict.txt')
text = "央行开展逆回购操作,北向资金持续流入科创板"
words = jieba.lcut(text)
print(words)
# 输出:['央行', '开展', '逆回购', '操作', ',', '北向资金', '持续', '流入', '科创板']
注意:词典文件编码必须是 UTF-8,否则 jieba 会报错。我曾经因为这个坑排查了半天……
2.2 调整分词模式
jieba 有三种模式:精确模式、全模式、搜索引擎模式。对于 RAG 系统,我推荐用精确模式。全模式虽然召回率高,但会产生大量冗余词,增加向量存储的压力。
# 精确模式(推荐)
words = jieba.lcut(text, cut_all=False)
# 搜索引擎模式(适合长文本检索)
words = jieba.lcut_for_search(text)
3. 停用词过滤:去掉那些「废话」
停用词就是「的」「了」「在」「是」这类高频但无实际意义的词。金融文本里还有一些特殊的停用词,比如「本公司」「上述」「如下」——这些词在检索时只会增加噪音。
我一般准备两份停用词表:
- 通用停用词表:覆盖常见的中文停用词
- 金融专用停用词表:针对财报、公告中的套话
def remove_stopwords(words, stopwords_set):
return [w for w in words if w not in stopwords_set and len(w) > 1]
# 加载停用词表
with open('stopwords.txt', 'r', encoding='utf-8') as f:
stopwords = set([line.strip() for line in f])
filtered_words = remove_stopwords(words, stopwords)
避坑指南:我曾经把「不」加入停用词表,结果「不看好」「不达标」这类否定表达全被过滤了,模型完全理解反了意思。停用词表要谨慎,尤其是金融领域,否定词往往很关键。
4. 实体识别:金融专有名词的精准捕获
实体识别(NER)是文本预处理的高级阶段。金融领域有大量专有名词——公司名、产品名、指标名、政策名。这些实体如果识别不出来,检索时就会漏掉关键信息。
4.1 基于词典的匹配
最简单也最可靠的方法,就是用词典做最大正向匹配。我习惯把金融实体分成几类:
| 实体类型 | 示例 | 来源 |
|---|---|---|
| 公司名称 | 贵州茅台、宁德时代、中国平安 | 上市公司列表 |
| 金融产品 | 沪深300ETF、中证500指数 | 交易所产品目录 |
| 经济指标 | CPI、PMI、LPR、GDP | 统计局/央行发布 |
| 政策术语 | 注册制、科创板、北交所 | 监管文件 |
class FinanceNER:
def __init__(self, entity_dict):
self.entity_dict = entity_dict # {实体: 类型}
self.max_len = max(len(e) for e in entity_dict)
def extract(self, text):
entities = []
i = 0
while i < len(text):
matched = False
for j in range(self.max_len, 0, -1):
if i + j <= len(text):
word = text[i:i+j]
if word in self.entity_dict:
entities.append((word, self.entity_dict[word], i, i+j))
i += j
matched = True
break
if not matched:
i += 1
return entities
4.2 结合分词结果
更聪明的做法是把实体识别和分词结合起来。先用自定义词典让 jieba 正确切分专有名词,再通过词性标注提取实体。
import jieba.posseg as pseg
def extract_finance_entities(text):
words = pseg.lcut(text)
entities = []
for word, flag in words:
# 自定义词典中标记为 n 的金融名词
if flag == 'n' and word in finance_dict:
entities.append(word)
return entities
5. 完整预处理流程
把上面几步串起来,就是一个完整的预处理管线。我画了一张流程图,帮你理清逻辑:
嗯,流程看起来不复杂,但每一步都有坑。我建议你在实际项目中,先拿一小批数据跑通流程,再逐步优化。别一上来就追求完美,迭代才是王道。
最终产出:经过预处理后的文本,应该是干净、结构化、实体清晰的。这样的文本送入向量模型,才能生成高质量的嵌入向量,支撑后续的精准检索。