en-heng

很久以前,我用过TFIDF做过行业关键词提取。TFIDF仅仅从词的统计信息出发,而没有充分考虑词之间的语义信息。现在本文将介绍一种考虑了相邻词的语义关系、基于图排序的关键词提取算法TextRank。

1. 介绍

TextRank由Mihalcea与Tarau于EMNLP\'04 [1]提出来,其思想非常简单:通过词之间的相邻关系构建网络,然后用PageRank迭代计算每个节点的rank值,排序rank值即可得到关键词。PageRank本来是用来解决网页排名的问题,网页之间的链接关系即为图的边,迭代计算公式如下:

\[PR(V_i) = (1-d) + d * \sum_{j \in In(V_i)} \frac{1}{|Out(V_j)|}PR(V_j) \]

其中,\(PR(V_i)\)表示结点\(V_i\)的rank值,\(In(V_i)\)表示结点\(V_i\)的前驱结点集合,\(Out(V_j)\)表示结点\(V_j\)的后继结点集合,\(d\)为damping factor用于做平滑。

网页之间的链接关系可以用图表示,那么怎么把一个句子(可以看作词的序列)构建成图呢?TextRank将某一个词与其前面的N个词、以及后面的N个词均具有图相邻关系(类似于N-gram语法模型)。具体实现:设置一个长度为N的滑动窗口,所有在这个窗口之内的词都视作词结点的相邻结点;则TextRank构建的词图为无向图。下图给出了由一个文档构建的词图(去掉了停用词并按词性做了筛选):

考虑到不同词对可能有不同的共现(co-occurrence),TextRank将共现作为无向图边的权值。那么,TextRank的迭代计算公式如下:

\[WS(V_i) = (1-d) + d * \sum_{j \in In(V_i)} \frac{w_{ji}}{\sum_{V_k \in Out(V_j)} w_{jk}} WS(V_j) \]

2. 评估

接下来将评估TextRank在关键词提取任务上的准确率、召回率与F1-Measure,并与TFIDF做对比;准确率计算公式如下:

\[Precision = \frac{1}{N} \sum_{i=0}^{N-1} \frac{\left|P_i \cap T_i\right|}{\left|P_i\right|} \]

其中,\(N\)为文档数量,\(P_i\)为文档\(i\)所提取出的关键词,\(T_i\)为文档的标注关键词。召回率与F1的计算公式如下:

\[Recall = \frac{1}{N} \sum_{i=0}^{N-1} \frac{\left|P_i \cap T_i\right|}{\left|T_i\right|} \]

\[F1 = \frac{2*Precision*Recall}{Precision + Recall} \]

测试集是由刘知远老师提供的网易新闻标注数据集,共有13702篇文档。Jieba完整地实现了关键词提取TFIDF与TextRank算法,基于Jieba-0.39的评估实验代码如下:

import jieba.analyse
import json
import codecs


def precision_recall_fscore_support(y_true, y_pred):
    """
    evaluate macro precision, recall and f1-score.
    """
    doc_num = len(y_true)
    p_macro = 0.0
    r_macro = 0.0
    for i in range(doc_num):
        tp = 0
        true_len = len(y_true[i])
        pred_len = len(y_pred[i])
        for w in y_pred[i]:
            if w in y_true[i]:
                tp += 1
        p = 1.0 if pred_len == 0 else tp / pred_len
        r = 1.0 if true_len == 0 else tp / true_len
        p_macro += p
        r_macro += r
    p_macro /= doc_num
    r_macro /= doc_num
    return p_macro, r_macro, 2 * p_macro * r_macro / (p_macro + r_macro)


file_path = \'data/163_chinese_news_dataset_2011.dat\'
with codecs.open(file_path, \'r\', \'utf-8\') as fr:
    y_true = []
    y_pred = []
    for line in fr.readlines():
        d = json.loads(line)
        content = d[\'content\']
        true_key_words = [w for w in set(d[\'tags\'])]
        y_true.append(true_key_words)
        # for w in true_key_words:
        #     jieba.add_word(w)
        key_word_pos = [\'x\', \'ns\', \'n\', \'vn\', \'v\', \'l\', \'j\', \'nr\', \'nrt\', \'nt\', \'nz\', \'nrfg\', \'m\', \'i\', \'an\', \'f\', \'t\',
                        \'b\', \'a\', \'d\', \'q\', \'s\', \'z\']
        extract_key_words = jieba.analyse.extract_tags(content, topK=2, allowPOS=key_word_pos)
        # trank = jieba.analyse.TextRank()
        # trank.span = 5
        # extract_key_words = trank.textrank(content, topK=2, allowPOS=key_word_pos)
        y_pred.append(extract_key_words)
    prf = precision_recall_fscore_support(y_true, y_pred)
    print(\'precision: {}\'.format(prf[0]))
    print(\'recall: {}\'.format(prf[1]))
    print(\'F1: {}\'.format(prf[2]))

其中,每个文档提取的关键词数为2,并按词性做过滤;span表示TextRank算法中的滑动窗口的大小。评估结果如下:

方法 Precision Recall F1-Measure
TFIDF 0.2697 0.2256 0.2457
TextRank span=5 0.2608 0.2150 0.2357
TextRank span=7 0.2614 0.2155 0.2363

如果将标注关键词添加到自定义词典,则评估结果如下:

方法 Precision Recall F1-Measure
TFIDF 0.3145 0.2713 0.2913
TextRank span=5 0.2887 0.2442 0.2646
TextRank span=7 0.2903 0.2455 0.2660

直观感受下关键词提取结果(添加了自定义词典):

// TFIDF, TextRank, labelled
[\'文强\', \'陈洪刚\'] [\'文强\', \'陈洪刚\'] {\'文强\', \'重庆\'}
[\'内贾德\', \'伊朗\'] [\'伊朗\', \'内贾德\'] {\'制裁\', \'世博\', \'伊朗\'}
[\'调控\', \'王珏林\'] [\'调控\', \'楼市\'] {\'楼市\', \'调控\'}
[\'罗平县\', \'男子\'] [\'男子\', \'罗平县\'] {\'被砍\', \'副局长\', \'情感纠葛\'}
[\'佟某\', \'黄玉\'] [\'佟某\', \'黄现忠\'] {\'盲井\', \'伪造矿难\'}
[\'女生\', \'聚众淫乱\'] [\'女生\', \'聚众淫乱\'] {\'聚众淫乱\', \'东莞\', \'不雅视频\'}
[\'马英九\', \'和平协议\'] [\'马英九\', \'推进\'] {\'国台办\', \'马英九\', \'和平协议\'}
[\'东帝汶\', \'巡逻艇\'] [\'东帝汶\', \'中国\'] {\'东帝汶\', \'军舰\', \'澳大利亚\'}
[\'墨西哥\', \'警方\'] [\'墨西哥\', \'袭击\'] {\'枪手\', \'墨西哥\', \'打死\'}

从上述两组实验结果,可以发现:

  • TextRank与TFIDF均严重依赖于分词结果——如果某词在分词时被切分成了两个词,那么在做关键词提取时无法将两个词黏合在一起(TextRank有部分黏合效果,但需要这两个词均为关键词)。因此是否添加标注关键词进自定义词典,将会造成准确率、召回率大相径庭。
  • TextRank的效果并不优于TFIDF。
  • TextRank虽然考虑到了词之间的关系,但是仍然倾向于将频繁词作为关键词。

此外,由于TextRank涉及到构建词图及迭代计算,所以提取速度较慢。

3. 参考资料

[1] Rada, Mihalcea, and Paul Tarau. "TextRank: Bringing Order into Texts." empirical methods in natural language processing (2004): 404-411.

分类:

技术点:

相关文章: