用PyTorch搭修抽取式摘要系统

作家:哈工大SCIR硕士生 赵怀鹏

导读:好的东西能让人事半功倍。神经收集框架PyTorch具有很强的灵敏性,而且代码可读性很高,可以帮帮你疾速完成本人的念法,于是学术圈越来越风行。本文讲解怎样应用PyTorch搭修一个粗浅的单文档抽取式摘要系统。

一:PyTorch简介

PyTorch是一种十分简单,斯文的动态图框架。其接口和模块的计划比较分明,可以帮帮你疾速完成本人的念法。下面借用李飞飞传授公然课cs231n第8讲Deep Learning Software的实质先容下动态图比较静态图的少许优势。当然静态图也有本人的优势,这里不加议论,大师可以精细看下这一讲的实质帮帮你挑选心仪的框架。下面先容静态图和动态图的一个主要区别:轮回和条件判别。

① 条件判别

假设我们需求判别的正负号来施行差别的条件。PyTorch版本

if z > 0:
    ...
else:    ...

Tensorflow版本:

tf.cond(tf.less(z,0),f1,f2)

② 轮回

PyTorch版本

for t in range(T):
   ...

Tensorflow版本:

tf.fold1(f,arg1,arg2)

通过上面的伪代码我们可以看到PyTorch更加接近python语法,让我们写代码更加自然。而Tensorflow需求厉厉恪守其定义的一套API。当然Tensorflow也有本人的优势。这里的例子仅展现了动态图框架的灵敏性。

二:抽取式摘要简介

摘假如对新闻的高度轮廓,它可以帮帮我们海量数据中疾速获取本人念要的新闻。新闻爆炸的时代,只靠人工写摘假如不实行的,于是我们需求一套主动摘要系统来帮帮人们疾速获取念要的新闻。主动摘要按生成摘要的方法可以分为抽取式摘要和生成式摘要,按照文本类型可以分为单文档摘要和众文档摘要。

无监视进修是古板抽取式摘要的主流方法。基于无监视进修的抽取式摘要可以分为三类[1]:A. 向量空间模子(The Vector-Space Methods). 其思念便是用向量外示句子和文档,然后盘算其相似度来决议哪些句子主要。代外的方法有LSA 等。B. 基于图的模子(The Graph-Based Methods). 图模子把作品修模成图,节点外示一个句子,边外示句子睹的相似或相关程度。图模子的主要表面依据是中心思论,认为假如一个句子和四周的句子都很相似,那么这个句子是可以代外作品新闻的。TextRank[2]便是此中主要的模子。C. 组合优化方法(The Combinational Optimization Methods).组合优化方法便是把抽取式摘要看做一个组合优化题目,代外的算法有ILP(the integer linear programming method)和次模函数(submodular functions)。

近些年来,跟着深度进修自然言语处理范畴的广泛运用,基于有监视进修的抽取式摘要的义务渐渐成为主流。其代外的义务有Jianpeng Cheng et.al[3]2016年提出的基于Seq2Seq的抽取式摘要方法,并抵达了当时的state of the art。另外这篇义务基于DailyMail数据集应用无监视进修构制一份抽取式摘要的数据集,本次实行也是应用的这份公然数据集。本年AAAI上Ramesh et. al.[4]提出SummaRuNNer,而且抵达了目前的state of the art。本文实行便是复现这篇义务。下面简单先容一下这个模子。

图1: SummaRuNNer模子

如图1所示,该模子是有一个两层RNN构成。最底层是词的输入,第二层是词级另外双向GRU,用来修模句子外示。第二层的每个句子的隐层各自做average pooling举措各自句子的外示。第三层是句子级另外双向GRU,输入是上一层的句子外示。取得隐层再做average pooling就可以取得文档的外示。着末应用文档的外示来帮帮我们依次对句仔■分类。  着末分类层的公式如下:

图2: 分类层公式

此中是抵达第个位置的曾经生成的摘要的外示,是作品的外示。 外示第个句子的新闻,盘算的是目今的句子和作品外示的相似度,外示的是目今的句子可以带来众少“新”的新闻,接下来的三项区分外示绝对位置,相对位置和偏置。可以看出通通公式十分直观,可标明性很强。

着末Loss采用的负对数似然。最终采纳摘要的时分并不是简单的分类,而是依据每个句子的概率上下排序,挑选概率最高的前几句即可。关于模子再进一步的议论和细节读者可以参考原文,这里不再作议论和扩展。

三:代码完成

下面简单先容下用PyTorch完成该模子的要害方法,精细细节可以参考完备代码:https://github.com/hpzhao/SummaRuNNer

① 模子教练

了解一个框架最主要的便是看它怎样教练,测试。我们先把SummaRuNNer这个模子看成一个黑盒,看下怎样应用这个模子来教练一个神经收集。

起首我们申请一个模子:

net = SummaRuNNer(config)
net.cuda()

这里的net便是我们的收集。接下来定义耗损函数和优化器:

# Loss and Optimizer
criterion = nn.BCELoss() optimizer = torch.optim.Adam(net.parameters(), lr=args.lr)

接下来我们将一篇作品的句子举措模子的输入用来取得每个句子的分类概率,这也便是前向进程:

 outputs = net(sents)

接下来是反向进程,当我们取得Loss之后就可以对其求梯度了:

optimizer.zero_grad()
loss = criterion(outputs, labels)
loss.backward()

这里我们起首要清空上一次盘算存留的梯度值,然后盘算Loss,着末再用backward()这个函数来主动求梯度,如许看是不是很简单直观呢。我们求过梯度之后就能举行反向传达了:

# gradient clipping     
torch.nn.utils.clip_grad_norm(net.parameters(), 1e-4) optimizer.step()

这里做了gradient clipping,为了进修更加的稳定。最中心的便是optimizer.step()这一步应用我们之前定义好的Adam算法来举行参数更新。

至此我们可以说完毕了应用PyTorch来教练一个神经收集。比起Tensorflow定义了一套本人的语法框架,PyTorch可以说好坏常简单,直观。着末我们保管教练的模子:

torch.save(net.state_dict(), args.model_file)

② 模子测试

模子测试最中心的是加载模子,加载模子之后我们可以通过之前的前向进程取得每个句子的预测概率。

net = SummaRuNNer(config).cuda()
net.load_state_dict(torch.load(args.model_file))
for index, docs in enumerate(test_loader):    doc = docs[0]    x, y = prepare_data(doc, word2id)    sents = Variable(torch.from_numpy(x)).cuda()    outputs = net(sents)

至此我们曾经完毕了模子的教练和预测部分的中心代码。

③ 收集搭修

上面的例子中我们把SummaRuNNer模子看成一个黑盒,通过传入篇章来取得我们念要的结果。接下来我们需求搭修收集。

PyTorch搭修收集一般需求承袭nn.Module这个类,并完成内中的forward()函数。虽然一开端觉得不太灵敏,但nn.Module为我们封装了少许操作,为我们编程带来便当,比如net.parameters()可取得收集所有需求教练的参数。另外,这种方法也让代码可读性更高。下面是应用  nn.Module搭修收集的代码框架:

class SummaRuNNer(nn.Module):
    def __init__(self, config):
        super(SummaRuNNer, self).__init__()
        ...    
   def forward():        ...

接下来我们来完美前向进程。联合模子图,我们起首搭修词级别RNN:

# word level GRU
word_features = self.word_embedding(x) word_outputs, _ = self.word_GRU(word_features)

接下来我们搭修句子级别RNN:

# sentence level GRU
# 句子级别RNN的输入是上一层词级别RNN的隐层做average pooling
sent_features = self._avg_pooling(word_outputs, sequence_length) sent_outputs, _ = self.sent_GRU(sent_features.view(1, -1, self.sent_input_size))

接下来我们应用句子级的GRU来取得篇章的外示:

# document representation
doc_features = self._avg_pooling(sent_outputs, [[x.size(0)]]) doc = torch.transpose(self.tanh(self.fc1(doc_features)), 0, 1)

着末是分类层,我们依据前面取得的外示来完成上述公式:

# classifier layer
outputs = [] sent_outputs = sent_outputs.view(-1, 2 * self.sent_GRU_hidden_units)

# 初始化目今摘要外示        
s = Variable(torch.zeros(100, 1)).cuda()

# 分类层
for position, sent_hidden in enumerate(sent_outputs):    h = torch.transpose(self.tanh(self.fc2(sent_hidden.view(1, -1))), 0, 1)    position_index = Variable(torch.LongTensor([[position]])).cuda()    p = self.position_embedding(position_index).view(-1, 1)    content = torch.mm(self.Wc, h)    salience = torch.mm(torch.mm(h.view(1, -1), self.Ws), doc)    
   # 这里用tanh(s)而不是直接用s的启事是让s的值保持必定体量    novelty = -1 * torch.mm(torch.mm(h.view(1, -1), self.Wr), self.tanh(s))    position = torch.mm(self.Wp, p)    bias = self.b    Prob = self.sigmoid(content + salience + novelty + position + bias)    s = s + torch.mm(h, Prob)    outputs.append(Prob)
   
return torch.cat(outputs, dim = 0)

至此我们完毕了摘要收集的构修。

四:总结

通过上面的例子我们对PyTorch有了一个比较直观的了解。初学者可以看一下PyTorch官网的初学教程:Deep Learning with PyTorch: A 60 Minute Blitz.

参考文献

[1] Chen K Y, Liu S H, Chen B, et al. Extractive broadcast news summarization leveraging recurrent neural network language modeling techniques[J]. IEEE/ACM Transactions on Audio, Speech and Language Processing (TASLP), 2015, 23(8): 1322-1334.[2]  Mihalcea R, Tarau P. TextRank: Bringing Order into Text[C]//EMNLP. 2004, 4: 404-411.[3]     Cheng J, Lapata M. Neural summarization by extracting sentences and words[J]. arXiv preprint arXiv:1603.07252, 2016.[4]  Nallapati R, Zhai F, Zhou B. SummaRuNNer: A recurrent neural network based sequence model for extractive summarization of documents[J]. hiP (yi= 1| hi, si, d), 2017, 1: 1.

本期义务编辑: 张伟男

本期编辑: 刘元兴

哈工大SCIR
哈工大SCIR

哈尔滨工业大学社会盘算与新闻检索研讨中心

工程工程完成PyTorchPython主动摘要NLP
2
暂无评论
暂无评论~