基于 TF 和 word2vec + kmeans 抽取文本摘要

我们可能经常碰到这样的一种需求,需要对一篇文章、一大串关于某个主题的文本内容进行摘要的提取。文本摘要的提取思路主要有抽取式、生成式:

  1. 抽取式主要是从文章中抽取一些重要句子组成文本的摘要;
  2. 生成式则需要学习文章内容,然后生成一片文章内容作为文本摘要。

接下来,主要是实现了两种简单的抽取式的文本摘要,它们分别是:

  1. 基于词频(TF)的抽取式文本摘要
  2. 基于 word2vec、kmeans 的抽取式文本摘要

1. 基于词频(TF)的抽取式文本摘要

基于词频的摘要抽取过程较为简单,其基本的思路如下:

  1. 文本加载:按行读取文件内容,由于一行可能会包含多个句子,所以对每一行继续根据句号再次进行分割;
  2. 文本分词:对每一个句子进行分词,并使用停用词过滤,生成有效词列表;
  3. 词频计算:计算每个句子中有效词的词频,词频越高则分数越高,累加每个句子词频值作为打分值;
  4. 摘要抽取:最后,从原始句子中抽取 N 个分数最高的句子组成文本摘要。

实现代码如下:

import jieba
from sklearn.feature_extraction.text import CountVectorizer
import numpy as np


def extract_abstract(filename, sentence_number):
    """
    :param filename: 抽取的新闻内容
    :param sentence_number: 抽取几个句子
    :return: 文本摘要内容
    """

    # 1. 读取停用词
    stopwords = []
    with open('data/stopword.txt', 'r') as file:
        for word in file:
            stopwords.append(word.strip())

    # 2. 读取文本内容
    sentences = []
    content_cut_word = []

    for line in open(filename, 'r'):
        # 去除两侧空格、换行符、删除空行
        line = line.strip()
        if 0 == len(line):
            continue

        # 一行可能包含多个句子, 将句子以句号切分
        lines = line.split('。')
        for line in lines:
            if 0 == len(line):
                continue

            # 存储原始句子
            sentences.append(line)
            # 存储句子的分词
            content_cut_word.append(' '.join(jieba.lcut(line)))

    # 3. 根据词频对每个句子打分
    converter = CountVectorizer(stop_words=stopwords)
    sentences_tf = converter.fit_transform(content_cut_word)
    sentences_tf = sentences_tf.toarray()
    # 出现 feature_names 中的词的频数越大, 则句子分数越高
    scores = np.array(sentences_tf).sum(axis=1)

    # 4. 抽取句子形成摘要
    abstract = []
    sentence_indexes = np.argsort(scores)[-3:]
    for index in sentence_indexes:
        abstract.append(sentences[index] + '。')

    return ''.join(abstract)


if __name__ == '__main__':
    abstract = extract_abstract('data/news1.txt', 5)
    print(abstract)

    abstract = extract_abstract('data/news2.txt', 5)
    print(abstract)

第一篇摘要:

2022年冬奥会将在北京举办,这是中国体育同世界奥林匹克运动开创双赢局面的良好契机,将激发中国民众对奥林匹克运动的热情。要坚定信心、振奋精神、再接再厉,全面落实简约、安全、精彩的办赛要求,抓紧抓好最后阶段各项赛事组织、赛会服务、指挥调度等准备工作,确保北京冬奥会、冬残奥会圆满成功。在北京举办一场全球瞩目的冬奥盛会,必将极大振奋民族精神,有利于凝聚海内外中华儿女为实现中华民族伟大复兴而团结奋斗,也有利于向世界进一步展示我国改革开放成就、和平发展主张。

第二篇摘要:

自2018年底消费扶贫工作全面启动以来,东部战区陆军按照“军地搭台、企业唱戏、群众受益”思路,努力打通帮扶地区农产品直达军营的“最后一公里”,走出一条消费帮扶新路子。”东部战区陆军保障部物资供应处助理员黄曦介绍,为最大限度形成消费帮扶规模效应,他们把所属单位划分成福州、厦门、南京、杭州、徐州等多个片区,开展“绿色产品进军营”集中消费活动。达成合作意向后,东部战区陆军先后在所属部队军营超市开设百余个“贵州绿色农产品扶贫专区(柜)”,积极拓展消费帮扶渠道,并逐步辐射到福建、江西等革命老区和部队帮扶地区。

2. 基于 word2vec + kmeans 实现抽取式文本摘要

基于 word2vec + kmeans 实现文本摘要的思路如下:

  1. 文本加载:按行读取文件内容,由于一行可能会包含多个句子,所以对每一行继续根据句号再次进行分割;
  2. 文本分词:对每一个句子进行分词,并使用停用词过滤,生成有效词列表;
  3. 句子向量:获得每一个词的词向量,由词向量来组成句子。接下来,把一个句子中的所有词向量加起来取均值作为当前句子的词向量表示;
  4. 句子聚类:对所有的句子聚类为 N 个簇,我们可以简单理解为这篇文章有 N 个重要思想;
  5. 摘要抽取:从每一个簇中获取距离质心最近的句子组成文本摘要。

在这里,我们使用 word2vec 来获得每一个词的词向量,我们可以通过下面的命令获得 word2vec 的中文词向量模型:

# 下载 cc.zh.300.bin.gz 词向量模型
# cc.zh.300.bin 使用 common crawl 和 wikipedia 数据集训练中文词向量模型
wget https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.zh.300.bin.gz’

# 使用下面句子加载词向量模型
model = fasttext.load_model('cc.zh.300.bin')

下面是具体的实现代码:

import fasttext
import jieba
import numpy as np
from sklearn.cluster import KMeans
import re


def extract_abstract(filename, sentence_number):
    """
    :param filename: 抽取的新闻内容
    :param sentence_number: 抽取几个句子
    :return: 文本摘要内容
    """

    # 1. 读取停用词
    stopwords = []
    with open('data/stopword.txt', 'r') as file:
        for word in file:
            stopwords.append(word.strip())

    # 2. 读取文本内容
    sentences = []
    content_cut_word = []

    for line in open(filename, 'r'):
        # 去除两侧空格、换行符、删除空行
        line = line.strip()
        if 0 == len(line):
            continue

        # 一行可能包含多个句子, 将句子以句号切分
        lines = line.split('。')
        for line in lines:
            if 0 == len(line):
                continue

            # 删除句子的特殊开头
            line = re.sub(r'^[^a-zA-Z0-9\u4e00-\u9fa5]+', '', line)

            # 存储原始句子
            sentences.append(line)
            # 存储句子的分词
            cut_words = jieba.lcut(line)
            sentence_words = []
            # 去除停用词
            for word in cut_words:
                if word not in stopwords:
                    sentence_words.append(word)

            content_cut_word.append(sentence_words)

    # 3. 将句子转换为词向量,并获得句子向量表示
    model = fasttext.load_model('cc.zh.300.bin')
    sentences_vector = []
    for line_word in content_cut_word:
        sentence_vector = np.zeros(300)
        word_count = 0
        for word in line_word:
            sentence_vector += model.get_word_vector(word)
            word_count += 1

        sentences_vector.append((sentence_vector / word_count).tolist())

    # 4. 使用聚类对所有的句子进行聚类分析
    estimator = KMeans(n_clusters=sentence_number, random_state=66)
    y_pred = estimator.fit_predict(sentences_vector)


    # 5. 生成文本摘要
    abstract = []
    for index in range(sentence_number):

        # 属于 index 簇的文本列表
        ss = np.array(sentences)[np.array(y_pred) == index]
        # 属于 index 簇的词向量列表
        vs = np.array(sentences_vector)[np.array(y_pred) == index]
        # 当前簇的中心
        c = estimator.cluster_centers_[index]

        close_index = 0
        center_distance = 0
        for index, v in enumerate(vs):
            current = np.sqrt(np.sum((v - c) ** 2))
            if current > center_distance:
                center_distance = current
                close_index = index

        # 从当前簇中抽取一个句子,存储到 abstract 中
        abstract.append(ss[close_index]+'。')

    return ''.join(abstract)


if __name__ == '__main__':
    abstract = extract_abstract('data/news1.txt', 5)
    print(abstract)

    abstract = extract_abstract('data/news2.txt', 5)
    print(abstract)

第一篇摘要:

我和亿万中国人民,欢迎全世界的朋友,2022年相约北京!欢迎你们,欢迎朋友们!。2015年7月31日,致信国际奥委会主席巴赫。我们不仅要办好一届冬奥盛会,而且要办出特色、办出精彩、办出独一无二来。我们以筹办北京冬奥会为契机,推动冰雪运动普及发展。新华社记者 谢环驰 摄。

第二篇摘要:

村合作社饲养的2000多只河田鸡均已到出栏期,每天的饲料和人工费用都是一笔不小的开支。得知当地果农的现实困难后,该旅组织官兵训练之余帮助果农采摘水果,并按照市场价收购。周全良的两个儿子在外务工,他自己又行动不便,不少果实成熟后烂在树上。2021年末,该旅在有着“蜜橘之乡”美称的赣南某地驻训。消费帮扶将进一步巩固拓展脱贫攻坚成果、全面推进乡村振兴。

未经允许不得转载:一亩三分地 » 基于 TF 和 word2vec + kmeans 抽取文本摘要
评论 (0)

9 + 5 =