找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

2412

积分

1

好友

333

主题
发表于 3 天前 | 查看: 6| 回复: 0

在工作中,我们常常会遇到这样的场景:产品同事发来一段长长的活动文案,希望我们能从中自动提取几个关键词,用于标签、搜索或推荐。当文本只有一段,而非海量语料时,如何快速、准确地揪出那些最具代表性的词汇呢?

Python 来解决这个问题,有多种成熟的思路,从简单高效的统计方法到利用预训练模型的语义方法各有千秋。本文将介绍四种实用的方法,并提供可直接运行的代码示例。

首先,我们约定一个示例文本,后续所有方法都将基于它进行演示:

text = """
双十一大促活动正式开始!今年我们主打「环保生活」主题,
所有绿色商品满200减50,新用户还可以叠加一张20元优惠券。
活动期间下单的用户,将有机会获得限量帆布袋和不锈钢保温杯。
"""

方法一:词频统计与停用词过滤

这是最基础也最可控的方法。思路非常直接:将文本分词,统计每个词出现的频率,过滤掉常见的无意义词(停用词),最后按词频排序。

以下是可直接运行的代码实现:

import jieba
from collections import Counter

# 简单的停用词列表,实际应用中建议从文件加载更全的版本
STOPWORDS = set([
    "的", "了", "和", "是", "我们", "还有", "可以",
    "将", "一个", "以及", "进行", "用户", "新用户",
])

def extract_keywords_by_freq(text: str, top_k: int = 10):
    # 分词
    words = jieba.lcut(text)

    # 过滤:长度>=2,且不在停用词里
    filtered = [
        w for w in words
        if len(w) >= 2 and w not in STOPWORDS
    ]

    # 统计词频
    counter = Counter(filtered)

    # 取前 top_k 个
    return counter.most_common(top_k)

if __name__ == "__main__":
    print(extract_keywords_by_freq(text, top_k=8))

运行结果可能如下:

[('活动', 2), ('大促', 1), ('正式开始', 1), ('环保生活', 1),
 ('绿色商品', 1), ('优惠券', 1), ('限量帆布袋', 1), ('保温杯', 1)]

可以看到,结果中混入了一些如“正式开始”这样可能并不理想的词。但此方法的优势非常明显:

  • 完全透明可控:任何结果都可追溯,易于调试。
  • 灵活调整权重:可以方便地手动增加或减少特定词的权重。
  • 速度极快:非常适合嵌入小程序、脚本或对实时性要求高的场景。

如果你的项目刚起步,或仅需处理日志分析、运维脚本等任务,这个简单方案往往就足够了。

方法二:TF-IDF算法

单纯统计词频有一个问题:它只关注词在当前文本中的出现次数。例如,在一批技术文档中,“错误”、“接口”等词可能频繁出现,但真正有区分度的可能是某个具体的“错误码”。

TF-IDF(词频-逆文档频率)引入了“全局视野”。其核心思想是:

  • TF(词频):词在当前文档中出现的频率越高,其TF值越高。
  • IDF(逆文档频率):包含该词的文档越少,其IDF值越高(越稀有)。
  • TF-IDF:将两者相乘,得分高的词被认为是当前文档更具代表性的关键词。

严格来说,TF-IDF需要在多篇文档构成的语料库上计算。但在单文档场景下,我们可以构建一个小型的背景语料库来模拟这个过程。

import jieba
from sklearn.feature_extraction.text import TfidfVectorizer

# 假设这是你系统中已有的其他文案,作为背景语料
corpus = [
    “双十一活动预热中,全场商品五折起。”,
    “夏季清凉节,主打轻薄服饰和防晒用品。”,
    “环保主题周,推广可降解包装和循环利用。”,
]

def jieba_tokenizer(text: str):
    return [w for w in jieba.lcut(text) if len(w) >= 2]

def extract_keywords_by_tfidf(text: str, top_k: int = 10):
    # 将当前文本加入语料库
    all_docs = corpus + [text]

    vectorizer = TfidfVectorizer(
        tokenizer=jieba_tokenizer,
        stop_words=list(STOPWORDS),
    )

    tfidf_matrix = vectorizer.fit_transform(all_docs)

    # 矩阵的最后一行对应我们的目标文本
    tfidf_vec = tfidf_matrix[-1]
    feature_names = vectorizer.get_feature_names_out()

    # 提取非零权重项
    coo = tfidf_vec.tocoo()
    pairs = list(zip(coo.col, coo.data))

    # 按权重降序排序
    pairs = sorted(pairs, key=lambda x: x[1], reverse=True)[:top_k]

    keywords = [(feature_names[idx], float(score)) for idx, score in pairs]
    return keywords

if __name__ == "__main__":
    print(extract_keywords_by_tfidf(text, top_k=8))

与纯词频法相比,TF-IDF能有效抑制“活动”、“商品”等常见词的权重,同时提升像“环保生活”、“帆布袋”这类更具主题特色词汇的排名。

需要注意的几点

  • 需要维护一个具有代表性的背景语料库,否则IDF计算可能不准。
  • 对于只出现一次的长尾词,其权重可能被低估。
  • 本质上仍是基于词面统计,无法理解语义。

尽管如此,在运营文案分析、日志分类等场景中,TF-IDF通常能提供显著优于词频统计的效果。

方法三:TextRank算法

TextRank算法借鉴了Google的PageRank思想,将文本中的词汇视为图中的节点。如果两个词在一定的窗口范围内共现,则认为它们之间存在一条边。通过迭代计算每个节点的权重(重要性),最终得分高的词即为关键词。

对于中文文本,我们可以直接使用 jieba 库中集成的TextRank接口,非常方便。

首先确保已安装 jieba

pip install jieba

使用示例:

import jieba.analyse as analyse

def extract_keywords_by_textrank(text: str, top_k: int = 10):
    # allowPOS 参数可以限定词性,例如只提取名词、动词等
    keywords = analyse.textrank(
        text,
        topK=top_k,
        withWeight=True,
        allowPOS=('ns', 'n', 'vn', 'v')  # 地名、名词、动名词、动词
    )
    return keywords

if __name__ == "__main__":
    print(extract_keywords_by_textrank(text, top_k=8))

TextRank与TF-IDF的侧重点不同,形成了有趣的互补:

  • TF-IDF 看重的是“词本身的统计特性”(局部频率和全局稀有度)。
  • TextRank 看重的是“词在文本结构中的位置关系”(通过共现网络计算出的中心性)。

例如,“优惠券”可能只出现一两次,但如果它周围频繁出现“满减”、“活动”、“下单”等重要词汇,TextRank算法就会给它一个较高的权重,认为它是语义网络中的关键节点。

方法四:基于预训练模型与语义相似度(KeyBERT思路)

前述方法均基于词面信息。随着预训练语言模型的普及,我们可以利用语义信息进行关键词提取。其核心思路是:将整个文档和候选短语分别编码成语义向量,然后计算它们之间的余弦相似度,相似度越高的候选短语,其语义越能代表整个文档。

KeyBERT 是一个实现此思路的流行库。它使用 sentence-transformers 来生成向量。

首先安装必要库:

pip install keybert sentence-transformers

代码实现如下:

from keybert import KeyBERT
import jieba

# 初始化模型,这里选用一个多语言小模型作为示例,可根据需要更换
kw_model = KeyBERT(model="paraphrase-multilingual-MiniLM-L12-v2")

def extract_keywords_by_bert(text: str, top_k: int = 10):
    # 预处理:分词并过滤,用空格连接起来作为输入
    tokens = [w for w in jieba.lcut(text) if len(w) >= 2 and w not in STOPWORDS]
    joined = " ".join(tokens)

    # use_maxsum 或 use_mmr 参数可以用于减少结果中语义重复的短语
    keywords = kw_model.extract_keywords(
        joined,
        keyphrase_ngram_range=(1, 3),  # 考虑1到3个词的短语
        stop_words=None,
        top_n=top_k,
        use_maxsum=True,
        nr_candidates=20,
    )
    return keywords

if __name__ == "__main__":
    print(extract_keywords_by_bert(text, top_k=8))

这种方法的特点

  • 环境要求较高:需要下载预训练模型,适合在服务端或离线任务中运行。
  • 能捕捉语义相似性:对于“绿色商品”、“环保好物”等不同表述但含义相近的短语,具有更好的识别能力。
  • 计算开销较大:速度比统计方法慢,但在单文本场景下通常可接受。

如果你在构建“长文自动打标签”、“知识库智能索引”等系统,基于语义的方法往往能带来质的提升。生产环境中建议为模型推理添加缓存机制以提升性能。

工程实践:封装统一调用接口

在实际业务开发中,我们不应将不同方法的调用代码散落在各处。一个良好的实践是定义一个统一的入口函数,通过参数来切换不同的提取策略。

from enum import Enum

class KeywordMethod(str, Enum):
    FREQ = "freq"
    TFIDF = "tfidf"
    TEXTRANK = "textrank"
    BERT = "bert"

def extract_keywords(text: str,
                     top_k: int = 10,
                     method: KeywordMethod = KeywordMethod.FREQ):
    if method == KeywordMethod.FREQ:
        return extract_keywords_by_freq(text, top_k)
    elif method == KeywordMethod.TFIDF:
        return extract_keywords_by_tfidf(text, top_k)
    elif method == KeywordMethod.TEXTRANK:
        return extract_keywords_by_textrank(text, top_k)
    elif method == KeywordMethod.BERT:
        return extract_keywords_by_bert(text, top_k)
    else:
        raise ValueError(f"不支持的关键词提取方法: {method}")

在业务代码中可以这样清晰、灵活地调用:

if __name__ == "__main__":
    for m in KeywordMethod:
        print("=== 方法:", m, "===")
        print(extract_keywords(text, top_k=5, method=m))
        print()

这种设计允许你在项目初期使用简单的 freqtfidf 方法快速上线,后续再针对特定场景平滑地迁移到更先进的 textrankbert 方法,并具备快速回滚的能力。

总结与选择建议

四种方法各有优劣,适用场景也不同:

  1. 词频统计简单、快速、可控,适用于对实时性要求高、逻辑需完全透明的场景,如日志实时监控、小型脚本工具。
  2. TF-IDF在统计基础上引入了文档集的对比,能提升主题关键词的区分度,适用于文档分类、内容标签化等需要一定区分能力的场景。
  3. TextRank基于图模型,关注词汇在文本结构中的重要性,能提取出在语义网络中处于中心位置的词,适用于捕捉文章核心概念。
  4. 基于语义模型能理解词语的深层含义,对同义词、近义词有更好的概括能力,适用于对准确性要求高的知识管理、智能推荐等场景,但需考虑计算成本和模型管理。

对于 Python 开发者而言,掌握从基础的 jieba 分词到利用 scikit-learn 和现代预训练模型进行文本处理,是构建智能应用的重要一环。你可以根据实际业务的数据规模、性能要求和效果预期,灵活选择或组合这些方法。如果想了解更多 Python 在数据处理和自动化方面的实战技巧,可以持续关注云栈社区的相关技术分享。




上一篇:研发同事择偶观与多线程面试题:从生活观察到并发编程实战
下一篇:Python数据类装饰器@dataclass详解:从定义、自定义字段到应用场景
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-12 04:01 , Processed in 0.252046 second(s), 39 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表