3. 数据清洗与预处理:文本去噪、分词技术、停用词过滤、实体识别基础

好,咱们进入第三章。说实话,金融事件分析这个事儿,很多人一上来就急着上模型、跑算法。我见过不少团队,数据还没洗干净呢,就开始调参了。结果呢?模型跑出来的结果,连自己都不敢信。

数据清洗与预处理,说白了就是给原始文本“洗澡”。金融文本有多脏?我举个例子——你从交易所拿到的公告原文,里面可能夹杂着HTML标签、乱码字符、甚至还有OCR识别错的符号。这些垃圾不清理掉,后面的分词、实体识别全都会跑偏。

3.1 文本去噪:把垃圾扔出去

文本去噪,我的理解就是“保留有用的,扔掉没用的”。金融文本里常见的噪声包括:

  • HTML/XML标签:比如 <div><p> 这些
  • 特殊符号:乱码、不可见字符、多余的标点
  • URL链接:很多公告里会带网址,分析时基本没用
  • 数字与单位混排:比如“1,234.56元”这种,需要规范化

我个人习惯用正则表达式做第一轮清洗。给你看一段我常用的代码:

import re

def clean_financial_text(text):
    # 去掉HTML标签
    text = re.sub(r'<[^>]+>', '', text)
    # 去掉URL
    text = re.sub(r'http[s]?://\S+', '', text)
    # 保留中文、英文、数字、常用标点
    text = re.sub(r'[^\u4e00-\u9fa5a-zA-Z0-9,。!?、;:""''()【】]', '', text)
    # 合并多余空格
    text = re.sub(r'\s+', ' ', text).strip()
    return text
我的经验:清洗规则不要一次写太死。我建议先跑100条样本看看效果,再迭代调整。我曾经因为一个正则写得太狠,把“ST股票”里的“ST”都给干掉了,后来查了半天才发现。

3.2 分词技术:把句子切成词

中文分词,嗯,这是个老话题了。但金融领域的分词,跟新闻领域完全不是一回事。你想想看,“中国人民银行”这个词,如果被切成“中国”、“人民”、“银行”,那意思就全变了。

目前主流的分词工具,我比较推荐这几个:

工具 特点 金融场景适用性
jieba 轻量、速度快、支持自定义词典 ★★★★☆
HanLP 功能全面、支持命名实体识别 ★★★★★
LAC (百度) 词性标注准确率高 ★★★★☆
pkuseg 北大出品,多领域模型 ★★★☆☆

我个人在金融项目里用得最多的是 jieba + 自定义词典 的组合。为什么?因为金融术语太特殊了。比如“定向增发”、“股权质押”、“可转债”,这些词通用分词器很容易切错。

给你看个例子:

import jieba

# 加载金融自定义词典
jieba.load_userdict('finance_dict.txt')

text = "公司拟通过定向增发募集资金用于收购标的公司股权"
words = jieba.lcut(text)
print(words)
# 输出:['公司', '拟', '通过', '定向增发', '募集资金', '用于', '收购', '标的公司', '股权']
关键点:自定义词典是金融分词的灵魂。我建议你把常见的金融术语、上市公司简称、产品名称都加进去。词典质量直接决定分词效果。

3.3 停用词过滤:去掉没营养的词

停用词,说白了就是那些出现频率高但没啥实际意义的词。比如“的”、“了”、“在”、“是”、“一个”等等。在金融文本里,还有一些特殊的停用词,比如“本公司”、“上述”、“如下”这类。

为什么要过滤?你想想看,如果“的”这个词在每篇文档里都出现几百次,它对区分不同事件有什么帮助?几乎没有。反而会稀释掉真正重要的关键词。

我常用的做法是:

  1. 用一份通用中文停用词表(网上有很多开源版本)
  2. 再补充一份金融领域停用词表
  3. 两份合并去重后使用
def load_stopwords():
    stopwords = set()
    with open('cn_stopwords.txt', 'r', encoding='utf-8') as f:
        for line in f:
            stopwords.add(line.strip())
    with open('finance_stopwords.txt', 'r', encoding='utf-8') as f:
        for line in f:
            stopwords.add(line.strip())
    return stopwords

stopwords = load_stopwords()
filtered_words = [w for w in words if w not in stopwords and len(w) > 1]
注意:停用词表不是万能的。有些词在A场景是停用词,在B场景可能就有意义。比如“不”这个字,在情感分析里就很重要。我建议你根据具体任务动态调整停用词表。

3.4 实体识别基础:找到关键信息

实体识别,英文叫NER(Named Entity Recognition)。在金融事件分析里,我们要找的实体通常包括:

  • 公司名称:比如“贵州茅台”、“中国平安”
  • 人名:比如“董事长”、“总经理”
  • 金额:比如“10亿元”、“500万美元”
  • 日期:比如“2024年3月15日”
  • 金融产品:比如“可转债”、“ETF”

实体识别的方法,我把它分成三个阶段:

第一阶段:基于规则
用正则表达式匹配固定模式。比如金额可以用 \d+\.?\d*\s*[万亿]?元 来匹配。这个方法简单粗暴,适合快速验证。

第二阶段:基于词典
把已知的实体列表做成词典,直接匹配。比如上市公司名单、高管名单。我做过一个项目,光公司词典就整理了3000多家。

第三阶段:基于模型
用预训练模型(比如BERT)来做序列标注。这个方法准确率高,但需要标注数据。我一般在前两个阶段搞不定的时候才上模型。

给你看个简单的基于词典的实体识别:

def entity_recognition(text, entity_dict):
    entities = []
    for entity_name, entity_type in entity_dict.items():
        if entity_name in text:
            entities.append({
                'text': entity_name,
                'type': entity_type,
                'start': text.index(entity_name),
                'end': text.index(entity_name) + len(entity_name)
            })
    return entities

# 示例
entity_dict = {
    '贵州茅台': '公司',
    '中国平安': '公司',
    '马明哲': '人物'
}
text = "贵州茅台发布公告,中国平安董事长马明哲出席会议"
result = entity_recognition(text, entity_dict)
print(result)
避坑指南:我曾经在实体识别上犯过一个低级错误——没有处理实体重叠的情况。比如“中国平安银行”这个文本里,“中国平安”和“平安银行”都是实体,但位置重叠了。后来我加了一个“最长匹配优先”的策略才解决。

3.5 本章知识体系

下面这张图,是我梳理的本章核心逻辑。你可以把它当作一个处理流水线来看:

数据清洗与预处理流程 原始金融文本 步骤1:文本去噪 步骤2:分词技术 步骤3:停用词过滤 步骤4:实体识别基础 关键产出 干净文本 分词结果 关键词列表 实体标注 结构化数据

你看,整个流程是串行的。每一步的输出,就是下一步的输入。我建议你在实际项目中,每一步都做一次质量检查。比如分词之后,随机抽100条看看切得对不对。别等到最后才发现问题,那时候改起来成本就高了。

好了,这一章的内容就这些。数据清洗虽然看起来基础,但说实话,它决定了整个分析系统的上限。你想想看,输入都是垃圾,输出能是黄金吗?

专注资料整理