3、文本预处理实战:中文分词(Jieba)、去停用词、词性标注、命名实体识别(NER)在金融文本中的应用
做新闻情感因子,第一步就是跟文本打交道。
说实话,金融文本比普通新闻难搞多了。什么「降准」、「逆回购」、「黑天鹅」,这些词在通用分词器里经常被切得七零八落。我刚开始做的时候,就吃过这个亏——好好的「量化宽松」被切成了「量化」和「宽松」,情感打分直接跑偏。
所以今天这一章,咱们就把文本预处理的几个核心环节过一遍。说白了,就是教会机器怎么读懂金融新闻。
3.1 中文分词:Jieba 的金融定制
分词是中文NLP的起点。英文单词自带空格,中文没有。你得告诉机器,「中国人民银行」是一个整体,而不是「中国」、「人民」、「银行」三个词。
Jieba 是目前用得最广的分词库。它有三种模式:精确模式、全模式和搜索引擎模式。做情感分析,我一般只用精确模式。
import jieba
import jieba.posseg as pseg
# 加载金融自定义词典
jieba.load_userdict('finance_dict.txt')
text = "央行今日开展逆回购操作,释放流动性3000亿元"
words = jieba.lcut(text)
print(words)
# 输出:['央行', '今日', '开展', '逆回购', '操作', ',', '释放', '流动性', '3000亿元']
你看,「逆回购」被正确识别了。如果没有自定义词典,它大概率会被切成「逆」和「回购」——意思完全变了。
3.2 去停用词:别让噪音干扰模型
分词之后,你会发现一堆「的」、「了」、「在」、「是」这类词。它们对情感分析毫无贡献,反而会稀释有效信息。
去停用词,就是把这些垃圾词过滤掉。
# 加载停用词表
stopwords = set()
with open('stopwords.txt', 'r', encoding='utf-8') as f:
for line in f:
stopwords.add(line.strip())
# 过滤停用词
filtered_words = [w for w in words if w not in stopwords and len(w) > 1]
print(filtered_words)
# 输出:['央行', '今日', '开展', '逆回购', '操作', '释放', '流动性', '3000亿元']
嗯,这里要注意:金融文本里的数字和单位不要随便去掉。比如「3000亿元」对判断政策力度很重要,你把它拆了或者删了,信息就丢了。
3.3 词性标注:给每个词打上标签
词性标注就是告诉程序:这个词是名词、动词还是形容词。为什么需要这个?因为情感词大多是形容词和副词,而实体词大多是名词。
# 词性标注
words_with_pos = pseg.lcut(text)
for word, flag in words_with_pos:
print(f'{word}: {flag}')
# 输出示例:
# 央行: n
# 今日: t
# 开展: v
# 逆回购: n
# 操作: vn
# 释放: v
# 流动性: n
# 3000亿元: m
你看,「释放」是动词,「流动性」是名词。如果我们要提取政策信号,动词「释放」加上名词「流动性」就是一个完整的动作描述。
我个人习惯把词性标注的结果存成DataFrame,方便后续做特征工程。比如只保留形容词做情感打分,或者只保留名词做主题提取。
3.4 命名实体识别:抓住关键信息
命名实体识别(NER)是文本预处理的进阶操作。它要识别出人名、地名、机构名、时间、金额等实体。
在金融文本里,NER特别重要。因为「美联储加息」和「中国央行加息」完全是两码事,你得知道主体是谁。
import jieba.analyse
# 基于TF-IDF提取关键词
keywords = jieba.analyse.extract_tags(text, topK=5, withWeight=True)
for word, weight in keywords:
print(f'{word}: {weight:.4f}')
# 输出:逆回购: 0.4521, 央行: 0.3892, 流动性: 0.3215, 释放: 0.2876, 3000亿元: 0.2154
不过Jieba自带的NER能力有限。做生产级应用时,我一般会用HanLP或者LAC来替代。但教学阶段,Jieba足够让你理解整个流程。
3.5 实战流程:把上面串起来
光讲理论没意思。咱们写一个完整的预处理函数,把分词、去停用词、词性标注、NER全部串起来。
def financial_text_preprocess(text):
# 1. 分词 + 词性标注
words_with_pos = pseg.lcut(text)
# 2. 去停用词 + 过滤标点
stopwords = load_stopwords() # 自定义加载函数
filtered = []
for word, flag in words_with_pos:
if word not in stopwords and flag not in ['x', 'w']:
filtered.append((word, flag))
# 3. 提取实体(这里用简单规则)
entities = []
for word, flag in filtered:
if flag in ['n', 'nr', 'ns', 'nt', 'nz']:
entities.append(word)
# 4. 提取情感词(形容词+副词)
sentiment_words = []
for word, flag in filtered:
if flag in ['a', 'ad', 'd']:
sentiment_words.append(word)
return {
'tokens': [w for w, _ in filtered],
'entities': entities,
'sentiment_words': sentiment_words
}
# 测试
news = "美联储主席鲍威尔表示,通胀数据超预期,加息步伐可能加快"
result = financial_text_preprocess(news)
print(result)
这个函数虽然简单,但已经能应付大部分场景了。我在实盘策略里用的预处理流水线,核心逻辑跟这个一模一样,只是加了更多规则和异常处理。
3.6 知识体系总览
为了让你对整个流程有个直观印象,我画了一张图。你看一眼就明白了。
这张图把整个流程串起来了。从原始新闻进去,经过分词、去停用词、词性标注和NER,最后输出结构化特征数据。后面做情感打分和因子构建,就是在这个基础上进行的。
好了,文本预处理这块就讲到这里。记住一句话:预处理做得好,模型训练少烦恼。我见过太多人急着调模型,结果数据质量一塌糊涂,最后白忙活一场。