文本摘要的一些探索

首先,简单阐述一下文本摘要的任务:该任务的输入是一段文本(例如新闻),输出是能够概括输入的简短文本。

目前文本摘要问题主要分为两类,extractive summarization (抽取式摘要)与 abstractive summarization(生成式摘要)。其中抽取式摘要是从原文本中,挑选出重要的句子,并将这些句子组成摘要。而生成式摘要则是让模型理解输入,使用encoder-decoder模型,生成出摘要。工业界普遍使用抽取式摘要,学术界主要研究生成式摘要。就摘要语句的流畅度而言,抽取式摘要要好于生成式摘要。

1.基于TextRank的抽取式摘要

1.1 PageRank

在了解textrank之前,首先我们需要了解PageRank,推荐大家去阅读《统计学习方法》(第二版)第21章

(1)概述:

PageRank算法是图的链接分析的代表性算法,属于图数据上的无监督学习方法。最初作为互联网网页重要度的计算方法,用于谷歌搜索引擎的网页排序,后来被应用到社会影响力分析,文本摘要等多个问题。

它的基本思想为:在有向图上定义一个随机游走模型,即一阶马尔可夫链,描述随机游走者沿着有向图随机访问各个结点的行为。在一定条件下,极限情况访问的各个结点的概率收敛到平稳分布,这时各个结点的平稳概率值就是其PageRank值,表示结点的重要度

(2)随机游走模型(Random Walk)


懒得画图了,直接拍了之前做的笔记

如上图可知,每个结点访问其链接的结点的概率是相同的。

(3)PageRank的基本定义

给定一个包含n个结点的强连通且非周期性的有向图,在其基础上定义随机游走模型,假设转移矩阵为M,在时刻0,1,2,3,...,t,...访问各个结点的概率分布为R0,MR0,M平方R0,M立方R0,...,以此类推。

则极限\lim\nolimits_{t\to\propto } yMtR0 = R存在(M的t次方乘以R0),极限向量R表示马尔科夫链的平稳分布,满足MR=R。平稳分布R称为这个有向图的PageRank,R的各个分量称为各个结点的PageRank值

极限存在定理:不可约且非周期的有限状态马尔可夫链,有唯一平稳分布存在,而且当时间趋于无穷时状态分布收敛于唯一的平稳分布。

(4)PageRank的一般定义

我们设想一下,在成千上万个网页中,大部分网页并没有超链接,所以基本定义不适用于这种情况,于是提出了PageRank的一般定义,一般定义的思想就是在基本定义上引入平滑项,这里就不细述了。

(5)PageRank的计算

①迭代计算法:更新足够小时,停止迭代。

②幂法:近似计算矩阵的主特征值和主特征向量。

③代数算法:R = ( I - dM)^-1 *(1-d)/n  * 1

1.2 TextRank

将文本中的每个句子分别看作一个节点,如果两个句子有相似性,那么认为这两个句子对应的节点之间存在一条无向边。考察句子相似度的方法:Similarity(si,sj) = \frac{\vert \left\{ wk|wk\in si,wk\in sj \right\}  \vert }{log(\vert si \vert )+log(\vert sj \vert )} (分母这么设置的原因是可以遏制较长的句子在相似度计算上的优势

根据以上相似度公式循环计算任意两个节点之间的相似度,根据阈值去掉两个节点之间相似度较低的边连接。构建出节点连接图,然后计算TextRank值,最后对所有的TextRank值排序,选出TextRank值最高的几个节点对应的句子作为摘要

1.3 实战

参考github:https://github.com/DengYangyong/textrank_summarization

import numpy as np

import pandas as pd

import re,os,jieba

from itertools import chain

from sklearn.metrics.pairwise import cosine_similarity

import networkx as nx

def seg_depart(sentence):

    # 去掉非汉字字符

    sentence = re.sub(r'[^\u4e00-\u9fa5]+','',sentence)

    sentence_depart = jieba.cut(sentence.strip())

    word_list = []

    for word in sentence_depart:

        if word not in stopwords:

            word_list.append(word) 

    # 如果句子整个被过滤掉了,如:'02-2717:56'被过滤,那就返回[],保持句子的数量不变

    return word_list

word_embeddings = {}

f = open('D://文本摘要/textrank/搜狗词向量/sgns.sogounews.bigram-char', encoding='utf-8')

for line in f:

    # 把第一行的内容去掉

    if '365113 300\n' not in line:

        values = line.split()

#        print(values)

        # 第一个元素是词语

        word = values[0]

        try:

            embedding = np.asarray(values[1:], dtype='float32')

            word_embeddings[word] = embedding

        except:

            word = values[0]+values[1]

            try:

                embedding = np.asarray(values[2:], dtype='float32')

                word_embeddings[word] = embedding

            except:

                print('这个问题我还暂时无法处理:')

                print(values[:10])

f.close()

print("一共有"+str(len(word_embeddings))+"个词语/字。")

# 文档所在的文件夹

c_root = 'D://文本摘要/textrank/textrank_易会满/cnews/'

f = open('D://newsdata.txt','r',encoding='utf-8')

data_list = f.readlines()

f.close()

stopwords = [line.strip() for line in open('D://文本摘要/textrank/textrank_易会满/stopwords.txt',encoding='UTF-8').readlines()]

stopwords = list(set(stopwords))

for i in range(len(stopwords)):

    stopwords[i] = stopwords[i].replace('\n','')


f1 = open("D://sum.txt",'w',encoding='utf-8')

for wja in range(len(data_list)):

    article = data_list[wja]

    sentences_list = []

    if article.strip():

        line_split = re.split(r'[。!;?]',article.strip())

        line_split = [line.strip() for line in line_split if line.strip() not in ['。','!','?',';'] and len(line.strip())>1]

        sentences_list.append(line_split)

    sentences_list = list(chain.from_iterable(sentences_list))

    sentence_word_list = []

    for sentence in sentences_list: 

        line_seg = seg_depart(sentence)

        sentence_word_list.append(line_seg)

    # print(sentence_word_list)

    # for i in range(len(sentence_word_list)):

    #    print(sentences_list[i])

    #    print(sentence_word_list[i])

    sentence_vectors = []

    for i in sentence_word_list:

        if len(i)!=0:

        # 如果句子中的词语不在字典中,那就把embedding设为300维元素为0的向量。

        # 得到句子中全部词的词向量后,求平均值,得到句子的向量表示

            v = sum([word_embeddings.get(w, np.zeros((300,))) for w in i])/(len(i))

        else:

        # 如果句子为[],那么就向量表示为300维元素为0个向量。

            v = np.zeros((300,))

        sentence_vectors.append(v)


    sim_mat = np.zeros([len(sentences_list), len(sentences_list)])

    for i in range(len(sentences_list)):

        for j in range(len(sentences_list)):

            if i != j:

                sim_mat[i][j] = cosine_similarity(sentence_vectors[i].reshape(1,300), sentence_vectors[j].reshape(1,300))[0,0]

    nx_graph = nx.from_numpy_array(sim_mat)

    # 得到所有句子的textrank值

    scores = nx.pagerank(nx_graph)

    # 根据textrank值对未处理的句子进行排序

    ranked_sentences = sorted(((scores[i],s) for i,s in enumerate(sentences_list)), reverse=True)

    # 取出得分最高的前10个句子作为摘要

    sn = 5

    if sn > len(sentences_list):

        sn = len(sentences_list)

    # print(sentences_list)

    summary = list()

    for i in range(sn):

        summary.append(ranked_sentences[i][1])

    sum2 = list()#存储最终摘要

    for kkk in sentences_list:

        if kkk in summary:

            sum2.append(kkk)

    summarization = '。'.join(sum2)

    f1.write(summarization+'\n')

    if wja%100 == 0:

        print('已处理了'+str(wja)+'条')

        if wja%1000 == 0:

            print(summarization)

f1.close()

原github的代码里有非常详细的备注,请大家移步至https://github.com/DengYangyong/textrank_summarization

以上代码,也是借鉴了该作者的代码,进行了少量修改,用于我自己的一个新闻摘到的任务。

2. 基于BERT的抽取式摘要

paper地址:https://arxiv.org/abs/1903.10318

code地址:https://github.com/nlpyang/BertSum

2.1 Paper解读

利用BERT做抽取式文本摘要

此前的DL在抽取式摘要任务方面已经达到了瓶颈,直到BERT横空出世。作者尝试了多种BERT变体,最终定义了如下模型:


BERTSUM模型框架

首先,BERT原型并不适合文本摘要,BERT有表示不同句子的分段嵌入,但它只有两个标签,而文本摘要则是多个句子,因此需要做一些改进。作者在每个句子前加【CLS】标签,后加【SEP】标签。利用【CLS】获取到每个句子的sentence-level的特征。之后的Summarization layer作者做了不同的尝试:①设置一个简单的线性分类器+sigmoid ②使用transformer+sigmoid ③使用LSTM+sigmoid


实验结果

我们可以看出,最好的方法是使用②transformer(作者又做了对比实验,分别测试了1,2,3层的transformer的效果,实验结果显示2层的transformer效果最好),值得注意的是使用LSTM的方法甚至不如一个简单的线性分类器。

2.2 代码实战

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容