TF-IDF與餘弦相似性的應用(三):自動摘要

有時候,很簡單的數學方法,就可以完成很複雜的任務。

這個系列的前兩部分就是很好的例子。僅僅依靠統計詞頻,就能找出關鍵詞相似文章。雖然它們算不上效果最好的方法,但肯定是最簡便易行的方法。今天,依然繼續這個主題。討論如何通過詞頻,對文章進行自動摘要(Automatic summarization)。

        如果能從3000字的文章,提煉出150字的摘要,就可以爲讀者節省大量閱讀時間。由人完成的摘要叫"人工摘要",由機器完成的就叫"自動摘要"。許多網站都需要它,比如論文網站、新聞網站、搜索引擎等等。2007年,美國學者的論文《A Survey on Automatic Text Summarization》(Dipanjan Das, Andre F.T. Martins, 2007)總結了目前的自動摘要算法。其中,很重要的一種就是詞頻統計。這種方法最早出自1958年的IBM公司科學家H.P. Luhn的論文《The Automatic Creation of Literature Abstracts》。Luhn博士認爲,文章的信息都包含在句子中,有些句子包含的信息多,有些句子包含的信息少。"自動摘要"就是要找出那些包含信息最多的句子。句子的信息量用"關鍵詞"來衡量。如果包含的關鍵詞越多,就說明這個句子越重要。Luhn提出用"簇"(cluster)表示關鍵詞的聚集。所謂"簇"就是包含多個關鍵詞的句子片段。

上圖就是Luhn原始論文的插圖,被框起來的部分就是一個"簇"。只要關鍵詞之間的距離小於"門檻值",它們就被認爲處於同一個簇之中。Luhn建議的門檻值是4或5。也就是說,如果兩個關鍵詞之間有5個以上的其他詞,就可以把這兩個關鍵詞分在兩個簇。

下一步,對於每個簇,都計算它的重要性分值。

以前圖爲例,其中的簇一共有7個詞,其中4個是關鍵詞。因此,它的重要性分值等於 ( 4 x 4 ) / 7 = 2.3。

然後,找出包含分值最高的簇的句子(比如5句),把它們合在一起,就構成了這篇文章的自動摘要。具體實現可以參見《Mining the Social Web: Analyzing Data from Facebook, Twitter, LinkedIn, and Other Social Media Sites》(O'Reilly, 2011)一書的第8章,python代碼見github

類似的算法已經被寫成了工具,比如基於Java的Classifier4J庫的SimpleSummariser模塊、基於C語言的OTS庫、以及基於classifier4J的C#實現python實現

 

python實現:

# -*- coding: utf-8 -*-
import sys
import json
import nltk
import numpy

N = 100  # 要考慮的字數
CLUSTER_THRESHOLD = 5  # 要考慮的單詞之間的距離
TOP_SENTENCES = 5  # “前n個”摘要的返回句子數


# 從H.P.的“文獻摘要自動創作”中獲取的方法盧恩

def _score_sentences(sentences, important_words):
    scores = []
    sentence_idx = -1

    for s in [nltk.tokenize.word_tokenize(s) for s in sentences]:

        sentence_idx += 1
        word_idx = []

        # For each word in the word list...
        for w in important_words:
            try:
                # 計算句子中任何重要單詞出現位置的索引
                word_idx.append(s.index(w))
            except ValueError as e:  # 不是在這個特定的句子
                print(e)

        word_idx.sort()

        # 有些句子可能根本不包含任何重要的單詞
        if len(word_idx) == 0:
            continue

        # 使用單詞index,使用最大距離閾值計算集羣
        # 任何兩個連續的單詞

        clusters = []
        cluster = [word_idx[0]]
        i = 1
        while i < len(word_idx):
            if word_idx[i] - word_idx[i - 1] < CLUSTER_THRESHOLD:
                cluster.append(word_idx[i])
            else:
                clusters.append(cluster[:])
                cluster = [word_idx[i]]
            i += 1
        clusters.append(cluster)

        # 爲每個羣集打分。 任何給定羣集的最高分數是分數
        # for the sentence

        max_cluster_score = 0
        for c in clusters:
            significant_words_in_cluster = len(c)
            total_words_in_cluster = c[-1] - c[0] + 1
            score = 1.0 * significant_words_in_cluster \
                    * significant_words_in_cluster / total_words_in_cluster

            if score > max_cluster_score:
                max_cluster_score = score

        scores.append((sentence_idx, score))

    return scores


def summarize(txt):
    sentences = [s for s in nltk.tokenize.sent_tokenize(txt)]
    normalized_sentences = [s.lower() for s in sentences]

    words = [w.lower() for sentence in normalized_sentences for w in
             nltk.tokenize.word_tokenize(sentence)]

    fdist = nltk.FreqDist(words)

    top_n_words = [w[0] for w in fdist.items()
                   if w[0] not in nltk.corpus.stopwords.words('english')][:N]

    scored_sentences = _score_sentences(normalized_sentences, top_n_words)

    # Summaization Approach 1:
    # Filter out non-significant sentences by using the average score plus a
    # fraction of the std dev as a filter

    avg = numpy.mean([s[1] for s in scored_sentences])
    std = numpy.std([s[1] for s in scored_sentences])
    mean_scored = [(sent_idx, score) for (sent_idx, score) in scored_sentences
                   if score > avg + 0.5 * std]

    # Summarization Approach 2:
    # Another approach would be to return only the top N ranked sentences

    top_n_scored = sorted(scored_sentences, key=lambda s: s[1])[-TOP_SENTENCES:]
    top_n_scored = sorted(top_n_scored, key=lambda s: s[0])

    # Decorate the post object with summaries

    return dict(top_n_summary=[sentences[idx] for (idx, score) in top_n_scored],
                mean_scored_summary=[sentences[idx] for (idx, score) in mean_scored])


if __name__ == '__main__':

    # Load in output from blogs_and_nlp__get_feed.py

    BLOG_DATA = sys.argv[1]
    blog_data = json.loads(open(BLOG_DATA).read())

    for post in blog_data:
        post.update(summarize(post['content']))
        print(post['title'])
        print('-' * len(post['title']))
        print('-------------')
        print('Top N Summary')
        print('-------------')
        print(' '.join(post['top_n_summary']))
        print('-------------------')
        print('Mean Scored Summary')
        print('-------------------')
        print(' '.join(post['mean_scored_summary']))


轉載自:http://www.ruanyifeng.com/blog/2013/03/automatic_summarization.html

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章