赞
踩
资源下载地址:https://download.csdn.net/download/sheziqiong/85736065
资源下载地址:https://download.csdn.net/download/sheziqiong/85736065
文本摘要有两种实现方式,一种是基于生成的方式,通过使用RNN等神经网络进实现,另外一种是抽取的方式。
本次作业重点关注基于抽取式的文本自动摘要的实现,以及实现的算法——textrank。
pagerank算法应用于谷歌等搜索引擎中,通过网页链接的数量和质量来初略估计网页的重要性,从而对网页进行排名。textrank是基于pagerank算法的一种改进,它利用一篇文章内部词语共同出现的语义信息即可对一篇文章进行关键词抽取和关键句抽取,并且不需要依赖于语料库等训练数据。
下面从关键词抽取和关键句抽取两方面进行介绍。
使用textrank进行关键词抽取的核心思想为利用词之间的相邻关系来构建一个词网络,然后使用pagerank的方式进行迭代计算,得到每个节点的权值,一个节点的权值越高其重要程度越高,故而得到关键词。
但可以发现,一个句子中有一些常用的词,如“的”、“了”等出现频率非常高,这类词也被称为停用词,尽管出现频率高但不可能作为关键词,所以一个句子总是将停用词去掉后再进行关键词的抽取。
具体算法描述如下:
给定输入句子 i n p u t = w 1 w 2 w 3 . . . w n input=w_1w_2w_3...w_n input=w1w2w3...wn
使用jieba进行分词,得到输入句子的词序列 i n p u t S e q = [ w 1 , w 2 , . . . , w n ] inputSeq=[w_1,w_2,...,w_n] inputSeq=[w1,w2,...,wn]
加载停用词表,对 i n p u t S e q inputSeq inputSeq中的词进行过滤,经过停用词表过滤后的词序列为 w o r d S e q = [ w 1 , w 2 , . . . , w k ] wordSeq=[w_1,w_2,...,w_k] wordSeq=[w1,w2,...,wk]。
由于在此步骤中主要操作可以描述为:查询一个给定词 w w w是否在一个词典中,所以使用了字典树数据结构来进行优化,避免了一次查询需要扫描一次词典的低效操作。
计算邻接矩阵 T i , j T_{i,j} Ti,j,其中 T i , j = k T_{i,j}=k Ti,j=k表示从词 i i i转移到词 j j j的次数为 k k k,为了定义转移使用了一个共现窗口来定义,窗口的大小默认定义为 3 3 3。那么如果 ∣ i − j ∣ ≤ 3 |i-j|\le 3 ∣i−j∣≤3,则 w i w_i wi可以转移到 w j w_j wj且 w j w_j wj也可以转移到 w i w_i wi,也就是说 T T T是一个对称矩阵。
在完成转移次数的计算后,对 T T T进行归一化计算 T i , j = T i , j ∑ k = 1 n T i , k T_{i,j}=\frac{T_{i,j}}{\sum_{k=1}^{n}T_{i,k}} Ti,j=∑k=1nTi,kTi,j,即 T i , j T_{i,j} Ti,j为从 w i w_i wi到 w j w_j wj转移的概率。
通过如下公式对词网络进行迭代计算,直到收敛:
v
a
l
i
=
(
1
−
d
)
+
d
×
∑
j
∈
i
n
(
i
)
W
j
,
i
×
v
a
l
j
∑
k
∈
o
u
t
(
j
)
W
j
,
k
=
(
1
−
d
)
+
d
×
∑
j
∈
i
n
(
i
)
T
i
,
j
v
a
l
j
val_i=(1-d)+d\times \sum_{j\in in(i)}\frac{W_{j,i}\times val_j}{\sum_{k\in out(j)W_{j,k}}} \\=(1-d)+d\times \sum_{j\in in(i)}T_{i,j}val_j
vali=(1−d)+d×j∈in(i)∑∑k∈out(j)Wj,kWj,i×valj=(1−d)+d×j∈in(i)∑Ti,jvalj
为了判断收敛,我通过计算迭代前后两个
r
a
n
k
rank
rank相邻的二范数之差,如果两者之差小于
0.0001
0.0001
0.0001则认为
r
a
n
k
rank
rank向量收敛
根据 r a n k rank rank向量,选择 r a n k rank rank值最大的几个词当作本篇文章的关键词。
基于textrank的关键句抽取核心思想与上述关键词抽取基本相同,在关键句抽取中以句子作为单位,而不是以此作为单位。
此时,邻接矩阵
T
i
,
j
T_{i,j}
Ti,j不再是词
i
i
i转移到词
j
j
j的概率,而是句子
i
i
i与句子
j
j
j的相似性,关于句子
i
i
i与句子
j
j
j的相似度定义为:
s
i
m
i
l
a
r
i
t
y
i
,
j
=
∣
{
w
k
∣
w
k
∈
S
i
∧
w
k
∈
S
j
}
∣
log
(
∣
s
i
∣
)
×
log
(
∣
s
j
∣
)
similarity_{i,j}=\frac{|\{w_k|w_k \in S_i \wedge w_k \in S_j\}|}{\log(|s_i|)\times \log(|s_j|)}
similarityi,j=log(∣si∣)×log(∣sj∣)∣{wk∣wk∈Si∧wk∈Sj}∣
这一相似度的定义虽然在某种程度上使两个句子之间建立了联系,但是没有考虑到词语与词语之间的词性相近等因素,如在句子
S
i
S_i
Si中有一个词"巨大",而在
S
j
S_j
Sj中有一个词"庞大",由于这两个词不同所以对相似度没有正贡献。但是两个词的含义非常相近,应该考虑其对相似度的贡献,而不是由于词不同而简单省略。
因此,在此处对传统的textrank算法进行一个改进,转而使用word2vec词向量表示来计算句子的相似度,词向量能够考虑到词与词之间的距离,从而能更好地解决句子之间相似度的计算问题。
考虑使用一个
n
n
n维的向量来表示词,那么定义一个句子
S
=
w
1
w
2
.
.
.
w
k
S=w_1w_2...w_k
S=w1w2...wk的
n
n
n维句向量:
v
e
c
=
1
k
∑
i
=
1
k
v
e
c
w
i
vec=\frac{1}{k}\sum_{i=1}^kvec_{w_i}
vec=k1i=1∑kvecwi
式子中
v
e
c
w
i
vec_{w_i}
vecwi是
w
i
w_i
wi的词向量,即对句子中所有词的向量做一个简单的平均,得到句子的句向量。在得到句子的句向量后,句子的相似度定义为:
s
i
m
i
l
a
r
i
t
y
i
,
j
=
∑
k
=
1
n
v
e
c
i
k
×
v
e
c
j
k
(
∑
k
=
1
n
v
e
c
i
k
2
)
×
(
∑
k
=
1
n
v
e
c
j
k
2
)
similarity_{i,j}=\frac{\sum_{k=1}^nvec_{ik}\times vec_{jk}}{\sqrt(\sum^n_{k=1} vec_{ik}^2) \times \sqrt(\sum^n_{k=1} vec_{jk}^2) }
similarityi,j=(
∑k=1nvecik2)×(
∑k=1nvecjk2)∑k=1nvecik×vecjk
即两个句子的相似度定义为两个句向量的点乘除于两个相邻二范数的乘积。
具体算法描述如下:www.biyezuopin.vip
系统架构如下图所示
本系统基于django框架设计前后端web项目,可分为如下几个模块:
三个模块的基本关系如下图所示:
下面分别介绍各个模块的具体实现:
导航栏使用Flex弹性布局方式,导航栏内的元素进行两端对齐。左端元素为Logo,右端为用户相关内容。www.biyezuopin.vip
页面的主体为左右布局,实现左右布局的方式为Flex
弹性布局。页面之中的各个小组件,诸如按钮,单选框,标题,输入框均采用bootstrap
内置的样式。另外,不允许用户复制输入框以及输出框以外的内容。
对DOM
节点的控制使用JavaScript的经典库JQuery
,当点击“提交”按钮的时候,通过JQuery获取文章输入框以及处理算法选择框的内容,获取内容之后,向后台发送Ajax
请求,将文章内容以及处理方式发送到后台,获取后台的信息之后,对后台发送过来的内容进行分析,如果状态正常,则将摘要以及关键词现实在右边的对应方框之中。
$('#btnEditSave').click(function () { let postDate = {}; $('#errorMag').text(''); $('#input_area').find('input, textarea').each(function () { let v = $(this).val(); let n = $(this).attr('name'); if (n === 'handle_method'){ if ($(this).prop('checked')) { postDate[n] = v; } } else{ postDate[n] = v; } }); console.log(postDate); $.ajax({ url:'/extract/', type:'POST', data:postDate, success:function (arg) { console.log(arg); console.log(typeof(arg)); var dict = JSON.parse(arg); if(dict.status){ console.log(dict); $('#output_area').find('#keyword').val(dict.keyword); $('#output_area').find('#pdigest').val(dict.pdigest); }else{ $('#errorMag').text(dict.message); } } }) }) })
点击“清空”按钮,将所有框之中的内容进行清除
//重置
$('#clear_text').click(function () {
$('#input_passage').val('');
$('#pdigest').val('');
$('#keyword').val('');
$('#errorMag').text('');
$("input[type=radio][name='handle_method'][value='text_rank']").prop("checked",true);
});
因为使用前后端不分离的模式,通过Django的模板渲染显示前端页面,所以在settings.py
修改以下内容
# 部署到服务器 ALLOWED_HOSTS = ['0x404.tech', 'www.0x404.tech', 'localhost', '127.0.0.1'] # 添加模板渲染 TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')] , 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] # 静态资源访问 STATIC_URL = '/static/' STATICFILES_DIRS = ( os.path.join(BASE_DIR, 'static'), )
对访问的URL进行正则表达式方式的匹配,并与对应的Controller进行对应。
在urls.py
之中添加显示以下内容
urlpatterns = [
re_path('admin/', admin.site.urls),
re_path('^$', views.index),
re_path('^extract/', views.extract)
]
在views.py
之中对前端的请求进行处理
主页面渲染
def index(request):
return render(request, 'index.html')
关键词摘要请处理
返回给前端页面json
格式的数据,message表示错误信息,当处理失败的时候,前端通过后端传来的status
以及message
两个关键词进行相应的错误处理。
def extract(request): respone = {'status': True, 'message': None, 'keyword': None, 'pdigest': None} try: input_psg = request.POST.get('input_passage') handle_method = request.POST.get('handle_method') word3 = wordRank.wordRank(input_psg, limit = 3) digest = sentenceRank.sentenceRank(input_psg, limit = 1) keyword, pdigest = "", "" for w in word3: keyword += w[1] + " " keyword = keyword[0 : len(keyword) - 1] for w in digest: pdigest += w[1] + " " respone['keyword'] = keyword respone['pdigest'] = pdigest except Exception as e: print(e) respone['status'] = False respone['message'] = '获取结果失败' result = json.dumps(respone, ensure_ascii=False) return HttpResponse(result)
算法模块涉及到的内容以及在“核心思想与算法描述”中介绍,将实现textrank所需要的各种数据集,数据结构和代码封装成一个包,并向外提供两个接口:
将该算法工具包嵌入到django的框架中,通过后端响应模块的view函数进行调用,实现模块之间数据的传递
本作业通过ROUGE来对算法进行评测,ROUGE通过将自动生成的摘要与一组摘要(一般由人工生成)进行比较计算,得到相应的分数,依此来衡量自动生成的摘要与参考摘要之间的相似度。
ROUGE-N的定义为:
R
O
U
G
E
−
N
=
∑
S
∈
r
e
f
∑
g
r
a
m
n
∈
S
C
o
u
n
t
m
a
t
c
h
(
g
r
a
m
n
)
∑
S
∈
r
e
f
∑
g
r
a
m
n
∈
S
C
o
u
n
t
(
g
r
a
m
n
)
ROUGE-N=\frac{\sum_{S\in ref}\sum_{gram_n\in S}Count_{match}(gram_n)}{\sum_{S\in ref}\sum_{gram_n\in S}Count(gram_n)}
ROUGE−N=∑S∈ref∑gramn∈SCount(gramn)∑S∈ref∑gramn∈SCountmatch(gramn)
其中分母为测试集中每个测试用例中
g
r
a
m
n
gram_n
gramn的个数总和,而分子为每个每个测试用例中共有的
g
r
a
m
n
gram_n
gramn的总和
本作业使用NLPCC2017的摘要数据(数据来源),选择前 200 200 200篇文章对textrank摘要抽取算法进行计算,计算其ROUGE-1得到的结果如下表所示:
P | R | F | |
---|---|---|---|
ROUGE-1 | 0.05 0.05 0.05 | 0.1 0.1 0.1 | 0.06666666222222252 0.06666666222222252 0.06666666222222252 |
由于使用的是抽取式的计算方法,摘要为原文中的一句话,而无法生成摘要会导致准确率不高的情况出现,从实验结果中也可见准确率要低于召回率。
要解决此此问题,提高算法性能则需要考虑使用生成式的摘要方法,当前比较主流的有如transformer模型或者Seq2seq模型+注意力机制的算法。在本次作业原先的规划中,要实现一个transformer模型来完成摘要的生成,并对比摘要抽取和摘要生成之间的性能差异。但由于时间和小组成员较为有限,且对pytorch不够熟悉,没有能够完成预期的实现。
虽然没有完成预期的摘要生成模型,但我们会在接下来假期的时间里继续研究这个问题,进一步学习基于神经网络的摘要生成算法。本作业github仓库,将在作业提交截止后开放。
虽然使用抽取式的算法召回率并不高,但在实际使用中还是能够一定的效果:
随便选择一个当天的新闻进行摘要,结果如下:
而当“摘要生成”遇到“废话生成器”,结果如下:
资源下载地址:https://download.csdn.net/download/sheziqiong/85736065
资源下载地址:https://download.csdn.net/download/sheziqiong/85736065
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。