4、情感词典构建:通用情感词典与金融领域定制
情感词典这东西,说白了就是一张「词-情绪」的映射表。你给模型一个词,它告诉你这是正面还是负面。听起来简单,但实际做起来坑不少。我刚开始做情感分析时,直接拿通用词典往金融文本上套,结果惨不忍睹——「负债」在通用词典里是中性词,但在财报里明显是负面信号。嗯,这就是我们今天要聊的核心问题。
4.1 通用情感词典:Loughran-McDonald 词典
先说说最经典的通用词典。Loughran-McDonald(简称LM词典)是金融领域绕不开的基准。它基于10-K年报语料构建,专门针对金融文本做了优化。
LM词典的核心特点:
- 六大类别:正面、负面、不确定性、诉讼、强语气、弱语气
- 词量:约8.5万个词条,其中负面词约2,300个
- 去歧义处理:比如「liability」在通用词典里是中性,LM标注为负面
我个人习惯用LM词典做基线。为什么?因为它公开、稳定、被引用了上千次。你想想看,如果你要发论文或者做策略回测,用LM词典至少不会被审稿人质疑「你的词典哪来的」。
关键洞察:LM词典的负面词比正面词更有效。我在项目中测试过,负面词占比与股票收益率的相关性(-0.12)远高于正面词(0.03)。说白了,市场对坏消息更敏感。
下面是我常用的加载方式:
import pandas as pd
# 加载LM词典(假设已下载CSV格式)
lm_dict = pd.read_csv('LoughranMcDonald_MasterDictionary.csv')
negative_words = set(lm_dict[lm_dict['Negative'] > 0]['Word'].str.lower())
positive_words = set(lm_dict[lm_dict['Positive'] > 0]['Word'].str.lower())
# 快速测试
sample_text = "The company reported a loss due to litigation costs."
words = sample_text.lower().split()
neg_count = sum(1 for w in words if w in negative_words)
pos_count = sum(1 for w in words if w in positive_words)
print(f"负面词数: {neg_count}, 正面词数: {pos_count}")
4.2 金融领域情感词典定制
通用词典有个硬伤——它不懂金融黑话。比如「write-down」(资产减记)、「restructuring」(重组),这些词在通用词典里可能是中性,但在金融语境下明显偏负面。
定制词典的三种思路:
- 基于种子词扩展:先找50-100个金融情感种子词,然后用Word2Vec找相似词
- 基于语料统计:用大量金融文本,计算每个词在正面/负面文档中的TF-IDF差异
- 半自动标注:用少量人工标注+主动学习迭代
我推荐第二种方法,效率最高。具体怎么做?
from sklearn.feature_extraction.text import TfidfVectorizer
import numpy as np
# 假设我们有标注好的金融文本(正面/负面)
positive_docs = [...] # 正面财报、新闻
negative_docs = [...] # 负面财报、新闻
# 分别计算TF-IDF
vec = TfidfVectorizer(max_features=5000, stop_words='english')
pos_matrix = vec.fit_transform(positive_docs)
neg_matrix = vec.transform(negative_docs)
# 计算每个词在正/负语料中的平均TF-IDF差异
pos_avg = np.array(pos_matrix.mean(axis=0)).flatten()
neg_avg = np.array(neg_matrix.mean(axis=0)).flatten()
diff = pos_avg - neg_avg
# 提取情感词
feature_names = vec.get_feature_names_out()
custom_dict = {}
for i, word in enumerate(feature_names):
if diff[i] > 0.1: # 正面倾向
custom_dict[word] = 'positive'
elif diff[i] < -0.1: # 负面倾向
custom_dict[word] = 'negative'
避坑指南:我曾经直接用全量语料跑TF-IDF,结果「the」「and」这种词也进了词典。记得一定要先去掉停用词,并且设置合理的阈值。我一般用0.05作为门槛,低于这个值的词直接扔掉。
4.3 词典覆盖度评估
词典建好了,怎么知道它好不好用?三个指标:覆盖度、准确率、区分度。
覆盖度评估:
- 词级覆盖:词典能覆盖语料中多少比例的情感词
- 文档级覆盖:多少文档至少有一个情感词被命中
- 情感密度:每千词中情感词的数量
我一般用这个函数快速评估:
def evaluate_coverage(texts, sentiment_dict):
total_words = 0
matched_words = 0
doc_covered = 0
for text in texts:
words = text.lower().split()
total_words += len(words)
matched = sum(1 for w in words if w in sentiment_dict)
matched_words += matched
if matched > 0:
doc_covered += 1
coverage = matched_words / total_words if total_words > 0 else 0
doc_coverage = doc_covered / len(texts) if texts else 0
return {
'word_coverage': coverage,
'doc_coverage': doc_coverage,
'avg_density': matched_words / len(texts) if texts else 0
}
下面这张图展示了词典构建的核心流程:
注意:覆盖度不是越高越好。我曾经见过有人把词典扩到10万词,覆盖度到了15%,但准确率掉到了40%。为什么?因为很多词在不同语境下情感极性会反转。比如「volatility」在期权交易里是中性,但在散户新闻里是负面。所以评估时一定要同时看准确率。
准确率评估方法:
- 随机抽取200-500条文本,人工标注情感
- 用词典预测,计算精确率、召回率、F1
- 重点关注负面词的召回率——金融策略里漏掉坏消息比误报好消息更致命
最后说一句,词典构建是个迭代活。我一般先跑LM词典做基线,然后针对特定领域(比如财报、新闻、社交媒体)分别定制子词典。每个子词典单独评估覆盖度,最后合并成一个综合词典。这样既能保证通用性,又能提升领域精度。
公众号:蓝海资料掘金营,微信deep3321