关键词提取做得好不好,直接影响搜索、推荐、摘要生成这些下游任务的效果。很多人遇到过这样的情况:一段明明围绕某个主题展开的文本,提取出来的关键词却把核心概念漏掉了,反而列出一些出现频率高但毫无信息量的词。这个问题不能简单归结为算法不行,更多时候是因为对TF-IDF和TextRank的原理理解有偏差,或者在预处理、参数配置上留下了漏洞。本文把这两个算法的机制掰开讲清楚,再针对关键词遗漏给出可落地的改进办法。

一、TF-IDF为什么会漏掉关键词
TF-IDF的核心思想是:一个词在当前文档里出现得越多(TF高),同时在其他文档里很少出现(IDF高),它就越能代表这篇文档。TF部分很好理解,就是词频统计。IDF的计算公式为 log(总文档数 / (1 + 包含该词的文档数)),作用是打压那些到处都出现的通用词,比如“的”“我们”“问题”这类词,虽然频率极高,但IDF值接近于零,整体得分就被压下去了。
那遗漏是怎么发生的?第一种情况是语料库不匹配。IDF依赖于一个背景语料库,如果你提取的是医疗领域的文本,用的却是新闻语料库训练出来的IDF值,很多领域术语在其他文档里也频繁出现,IDF会被低估,关键词直接掉出榜单。第二种情况是文本经过去重或者规范化处理后,某些关键词的TF被稀释。比如文中反复用代词指代某个实体,实体名本身只出现两三次,TF不够高,就会被漏掉。第三种情况是分词错误,一个专业术语被切碎成两个普通词,两个碎片各自的TF和IDF都不突出,自然提取不出来。
from jieba.analyse import extract_tags
text = "本文提出一种基于图神经网络的异常检测方法,该方法在工业数据集上表现优异。"
# 直接使用默认IDF语料库提取
keywords = extract_tags(text, topK=5, withWeight=True)
for kw, w in keywords:
print(kw, w)上面这段代码用jieba的默认IDF词典,如果换成你自己领域的语料统计IDF,效果通常会有明显差别。解决语料库不匹配的办法是自建IDF:把领域内几千篇文档做分词统计,生成idf文件后通过set_idf_path加载,这样领域术语的IDF值才贴近真实分布。
二、TextRank的投票机制与它的盲区
TextRank借鉴了网页排序的思路,把每个词看作图上的一个节点,词与词之间如果在某个窗口距离内共同出现,就建立一条边。然后迭代计算每个节点的重要性,本质上是让重要的词给与它相邻的词投票,一个词被越多的重要词包围,它自己的得分就越高。公式上就是不断迭代 VR(Vi) = (1-d) + d * Σ(VR(Vj)/Out(Vj)),直到收敛。这个过程不需要语料库,只看当前这篇文本自身的结构,所以它对单文档、短文本的适配性比TF-IDF好。
TextRank的遗漏往往来自三个方面。首先是窗口大小设置不当,默认窗口一般是5,如果一个核心概念在文中的表述间隔超过5个词,边就建立不起来,这个词会变成孤立节点或者弱连接节点,得分上不去。其次是词性过滤太粗暴,有些实现只保留名词,但很多领域关键词是动词性或形容词性的,比如“异常检测”里的“检测”,过滤后短语就断了。最后是停用词处理不彻底,虚词残留在图里会占据大量边资源,把真正有意义的词挤出高分圈。
from jieba.analyse import textrank
text = "图神经网络通过消息传递机制聚合邻居节点信息,"
"消息传递的效率决定了模型在大规模图上的可扩展性。"
# allowPOS放宽到名词、动词、动词性名词
keywords = textrank(text, topK=5, withWeight=True,
allowPOS=('ns', 'n', 'vn', 'v'))
for kw, w in keywords:
print(kw, round(w, 4))把allowPOS参数放宽,允许动词和动名词参与构图,短文本的关键词召回会有肉眼可见的提升。另外jieba的textrank没有直接暴露窗口参数,如果用gensim或者自己实现,可以尝试把窗口从3调到10做对比实验,观察核心概念是否都能连成子图。
三、减少遗漏的四个实用改进方案
方案一:完善停用词表。通用的哈工大停用词表只覆盖常见虚词,领域文本里还有一些高频但无信息量的词,比如做技术文档时“如下”“说明”“注意”这类词。把它们补充进停用词表,能明显减少无效词占据TopK名额的情况,给真正的关键词腾出位置。停用词表建议每处理一批新文本就人工检查一次提取结果并增量维护。
方案二:保留短语和实体。单字词和被切碎的术语是遗漏重灾区。可以在分词阶段加入自定义词典,把领域术语、产品名固定成整体;或者先做命名实体识别,把识别出的实体作为候选关键词直接保送进结果,再对剩余文本做算法提取。这样即使实体的词频不高,也不会被算法漏掉。
import jieba
from jieba.analyse import extract_tags
# 把领域术语加入自定义词典,避免被切碎
jieba.add_word('图神经网络', freq=200000, tag='n')
text = "基于图神经网络的异常检测框架已开源。"
words = jieba.lcut(text)
print(words)
# 提取时合并结果,实体保送 + TF-IDF补充
print(extract_tags(text, topK=5))方案三:两算法融合取并集。TF-IDF擅长抓住全局稀有词,TextRank擅长抓住文本内部的语义结构,两者的遗漏模式不一样。做法是分别取两个算法的TopK结果,做加权合并:两个算法都命中的词权重最高排前面,只被单一算法命中的词按该算法的分数排序补位。实践里这种并集策略比单算法的召回率普遍高出一截。
方案四:控制TopK并引入权重阈值。很多人把TopK写死成5或10,文本长短差异大时这个数字很僵硬。更稳妥的做法是按权重分布动态截断,比如取累计权重占比达到80%的词,或者过滤掉权重低于最大值十分之一的词,长文本多给几个名额,短文本少给几个,避免名额浪费或拥挤。
四、如何根据场景选择算法
两个算法没有绝对优劣,选择要看文本特点。有大规模背景语料可用、文本偏长、需要突出文档间差异性的场景,选TF-IDF更合适,比如搜索引擎的文档索引、跨文档的主题对比。单篇处理、短文本、没有现成语料库的场景,TextRank更省事,比如舆情单条分析、用户评论打标。
| 对比维度 | TF-IDF | TextRank |
|---|---|---|
| 是否需要语料库 | 需要IDF统计 | 不需要 |
| 短文本表现 | 偏弱,词频不稳定 | 较好,依赖共现结构 |
| 计算开销 | 低,查表即可 | 较高,需迭代收敛 |
| 典型遗漏原因 | 语料不匹配、术语被切碎 | 窗口太小、词性过滤过严 |
最后要强调一点,关键词遗漏的排查应该形成固定流程:先看分词结果对不对,再看停用词和词性过滤有没有误伤,然后检查算法参数(窗口、IDF路径、TopK),最后才考虑换算法或做融合。绝大多数遗漏问题在前两步就能定位,盲目换算法反而会掩盖预处理阶段的缺陷。把预处理做扎实,再配合两算法融合的并集策略,关键词提取的完整性和准确性都能稳定提升。