文本摘要的一些探索

首先,簡(jiǎn)單闡述一下文本摘要的任務(wù):該任務(wù)的輸入是一段文本(例如新聞),輸出是能夠概括輸入的簡(jiǎn)短文本。

目前文本摘要問題主要分為兩類,extractive summarization (抽取式摘要)與 abstractive summarization(生成式摘要)。其中抽取式摘要是從原文本中,挑選出重要的句子,并將這些句子組成摘要。而生成式摘要?jiǎng)t是讓模型理解輸入,使用encoder-decoder模型,生成出摘要。工業(yè)界普遍使用抽取式摘要,學(xué)術(shù)界主要研究生成式摘要。就摘要語句的流暢度而言,抽取式摘要要好于生成式摘要。

1.基于TextRank的抽取式摘要

1.1 PageRank

在了解textrank之前,首先我們需要了解PageRank,推薦大家去閱讀《統(tǒng)計(jì)學(xué)習(xí)方法》(第二版)第21章

(1)概述:

PageRank算法是圖的鏈接分析的代表性算法,屬于圖數(shù)據(jù)上的無監(jiān)督學(xué)習(xí)方法。最初作為互聯(lián)網(wǎng)網(wǎng)頁(yè)重要度的計(jì)算方法,用于谷歌搜索引擎的網(wǎng)頁(yè)排序,后來被應(yīng)用到社會(huì)影響力分析,文本摘要等多個(gè)問題。

它的基本思想為:在有向圖上定義一個(gè)隨機(jī)游走模型,即一階馬爾可夫鏈,描述隨機(jī)游走者沿著有向圖隨機(jī)訪問各個(gè)結(jié)點(diǎn)的行為。在一定條件下,極限情況訪問的各個(gè)結(jié)點(diǎn)的概率收斂到平穩(wěn)分布,這時(shí)各個(gè)結(jié)點(diǎn)的平穩(wěn)概率值就是其PageRank值,表示結(jié)點(diǎn)的重要度。

(2)隨機(jī)游走模型(Random Walk)


懶得畫圖了,直接拍了之前做的筆記

如上圖可知,每個(gè)結(jié)點(diǎn)訪問其鏈接的結(jié)點(diǎn)的概率是相同的。

(3)PageRank的基本定義

給定一個(gè)包含n個(gè)結(jié)點(diǎn)的強(qiáng)連通且非周期性的有向圖,在其基礎(chǔ)上定義隨機(jī)游走模型,假設(shè)轉(zhuǎn)移矩陣為M,在時(shí)刻0,1,2,3,...,t,...訪問各個(gè)結(jié)點(diǎn)的概率分布為R0,MR0,M平方R0,M立方R0,...,以此類推。

則極限\lim\nolimits_{t\to\propto } yMtR0?= R存在(M的t次方乘以R0),極限向量R表示馬爾科夫鏈的平穩(wěn)分布,滿足MR=R。平穩(wěn)分布R稱為這個(gè)有向圖的PageRank,R的各個(gè)分量稱為各個(gè)結(jié)點(diǎn)的PageRank值

極限存在定理:不可約且非周期的有限狀態(tài)馬爾可夫鏈,有唯一平穩(wěn)分布存在,而且當(dāng)時(shí)間趨于無窮時(shí)狀態(tài)分布收斂于唯一的平穩(wěn)分布。

(4)PageRank的一般定義

我們?cè)O(shè)想一下,在成千上萬個(gè)網(wǎng)頁(yè)中,大部分網(wǎng)頁(yè)并沒有超鏈接,所以基本定義不適用于這種情況,于是提出了PageRank的一般定義,一般定義的思想就是在基本定義上引入平滑項(xiàng),這里就不細(xì)述了。

(5)PageRank的計(jì)算

①迭代計(jì)算法:更新足夠小時(shí),停止迭代。

②冪法:近似計(jì)算矩陣的主特征值和主特征向量。

③代數(shù)算法:R = ( I - dM)^-1 *(1-d)/n? *?1

1.2 TextRank

將文本中的每個(gè)句子分別看作一個(gè)節(jié)點(diǎn),如果兩個(gè)句子有相似性,那么認(rèn)為這兩個(gè)句子對(duì)應(yīng)的節(jié)點(diǎn)之間存在一條無向邊??疾炀渥酉嗨贫鹊姆椒ǎ?img class="math-inline" src="https://math.jianshu.com/math?formula=Similarity(si%2Csj)%20%3D%20%5Cfrac%7B%5Cvert%20%5Cleft%5C%7B%20wk%7Cwk%5Cin%20si%2Cwk%5Cin%20sj%20%5Cright%5C%7D%20%20%5Cvert%20%7D%7Blog(%5Cvert%20si%20%5Cvert%20)%2Blog(%5Cvert%20sj%20%5Cvert%20)%7D%20" alt="Similarity(si,sj) = \frac{\vert \left\{ wk|wk\in si,wk\in sj \right\} \vert }{log(\vert si \vert )+log(\vert sj \vert )} " mathimg="1">(分母這么設(shè)置的原因是可以遏制較長(zhǎng)的句子在相似度計(jì)算上的優(yōu)勢(shì)

根據(jù)以上相似度公式循環(huán)計(jì)算任意兩個(gè)節(jié)點(diǎn)之間的相似度,根據(jù)閾值去掉兩個(gè)節(jié)點(diǎn)之間相似度較低的邊連接。構(gòu)建出節(jié)點(diǎn)連接圖,然后計(jì)算TextRank值,最后對(duì)所有的TextRank值排序,選出TextRank值最高的幾個(gè)節(jié)點(diǎn)對(duì)應(yīng)的句子作為摘要。

1.3 實(shí)戰(zhàn)

參考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)?

? ? # 如果句子整個(gè)被過濾掉了,如:'02-2717:56'被過濾,那就返回[],保持句子的數(shù)量不變

? ? return word_list

word_embeddings = {}

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

for line in f:

? ? # 把第一行的內(nèi)容去掉

? ? if '365113 300\n' not in line:

? ? ? ? values = line.split()

#? ? ? ? print(values)

? ? ? ? # 第一個(gè)元素是詞語

? ? ? ? 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('這個(gè)問題我還暫時(shí)無法處理:')

? ? ? ? ? ? ? ? print(values[:10])

f.close()

print("一共有"+str(len(word_embeddings))+"個(gè)詞語/字。")

# 文檔所在的文件夾

c_root = 'D://文本摘要/textrank/textrank_易會(huì)滿/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_易會(huì)滿/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設(shè)為300維元素為0的向量。

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

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

? ? ? ? else:

? ? ? ? # 如果句子為[],那么就向量表示為300維元素為0個(gè)向量。

? ? ? ? ? ? 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)

? ? # 根據(jù)textrank值對(duì)未處理的句子進(jìn)行排序

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

? ? # 取出得分最高的前10個(gè)句子作為摘要

? ? 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()#存儲(chǔ)最終摘要

? ? 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的代碼里有非常詳細(xì)的備注,請(qǐng)大家移步至https://github.com/DengYangyong/textrank_summarization

以上代碼,也是借鑒了該作者的代碼,進(jìn)行了少量修改,用于我自己的一個(gè)新聞?wù)降娜蝿?wù)。

2. 基于BERT的抽取式摘要

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

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

2.1 Paper解讀

利用BERT做抽取式文本摘要

此前的DL在抽取式摘要任務(wù)方面已經(jīng)達(dá)到了瓶頸,直到BERT橫空出世。作者嘗試了多種BERT變體,最終定義了如下模型:


BERTSUM模型框架

首先,BERT原型并不適合文本摘要,BERT有表示不同句子的分段嵌入,但它只有兩個(gè)標(biāo)簽,而文本摘要?jiǎng)t是多個(gè)句子,因此需要做一些改進(jìn)。作者在每個(gè)句子前加【CLS】標(biāo)簽,后加【SEP】標(biāo)簽。利用【CLS】獲取到每個(gè)句子的sentence-level的特征。之后的Summarization layer作者做了不同的嘗試:①設(shè)置一個(gè)簡(jiǎn)單的線性分類器+sigmoid ②使用transformer+sigmoid ③使用LSTM+sigmoid


實(shí)驗(yàn)結(jié)果

我們可以看出,最好的方法是使用②transformer(作者又做了對(duì)比實(shí)驗(yàn),分別測(cè)試了1,2,3層的transformer的效果,實(shí)驗(yàn)結(jié)果顯示2層的transformer效果最好),值得注意的是使用LSTM的方法甚至不如一個(gè)簡(jiǎn)單的線性分類器。

2.2 代碼實(shí)戰(zhàn)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容