Pointer Networks简介及其应用

原文: https://zhuanlan.zhihu.com/p/48959800

 

本文介绍15年发表在NIPS上的一篇文章:Pointer Networks[1],以及后续应用了Pointer Networks的三篇文章:Get To The Point: Summarization with Pointer-Generator Networks[2]、Incorporating Copying Mechanism in Sequence-to-Sequence Learning [3]和Multi-Source Pointer Network for Product Title Summarization[4]。

一、从Sequence2Sequence说起

Sequence2Sequence(简称seq2seq)模型是RNN的一个重要的应用场景,顾名思义,它实现了把一个序列转换成另外一个序列的功能,并且不要求输入序列和输出序列等长。比较典型的如机器翻译,一个英语句子“Who are you”和它对应的中文句子“你是谁”是两个不同的序列,seq2seq模型要做的就是把这样的序列对应起来。

由于类似语言这样的序列都存在时序关系,而RNN天生便适合处理具有时序关系的序列,因此seq2seq模型往往使用RNN来构建,如LSTM和GRU。具体结构见Sequence to Sequence Learning with Neural Networks[5]这篇文章提供的模型结构图:

 

图1:Seq2seq模型结构

在这幅图中,模型把序列“ABC”转换成了序列“WXYZ”。分析其结构,我们可以把seq2seq模型分为encoder和decoder两个部分。encoder部分接收“ABC”作为输入,然后将这个序列转换成为一个中间向量C,向量C可以认为是对输入序列的一种理解和表示形式。然后decoder部分把中间向量C作为自己的输入,通过解码操作得到输出序列“WXYZ”。

后来,Attention Mechanism[6]的加入使得seq2seq模型的性能大幅提升,从而大放异彩。那么Attention Mechanism做了些什么事呢?一言以蔽之,Attention Mechanism的作用就是将encoder的隐状态按照一定权重加和之后拼接(或者直接加和)到decoder的隐状态上,以此作为额外信息,起到所谓“软对齐”的作用,并且提高了整个模型的预测准确度。简单举个例子,在机器翻译中一直存在对齐的问题,也就是说源语言的某个单词应该和目标语言的哪个单词对应,如“Who are you”对应“你是谁”,如果我们简单地按照顺序进行匹配的话会发现单词的语义并不对应,显然“who”不能被翻译为“你”。而Attention Mechanism非常好地解决了这个问题。如前所述,Attention Mechanism会给输入序列的每一个元素分配一个权重,如在预测“你”这个字的时候输入序列中的“you”这个词的权重最大,这样模型就知道“你”是和“you”对应的,从而实现了软对齐。

二、Pointer Networks

背景讲完,我们就可以正式进入Pointer Networks这部分了。为什么在讨论Pointer Networks之前要先说seq2seq以及Attention Mechanism呢,因为Pointer Networks正是通过对Attention Mechanism的简化而得到的。

作者开篇就提到,传统的seq2seq模型是无法解决输出序列的词汇表会随着输入序列长度的改变而改变的问题的,如寻找凸包等。因为对于这类问题,输出往往是输入集合的子集。基于这种特点,作者考虑能不能找到一种结构类似编程语言中的指针,每个指针对应输入序列的一个元素,从而我们可以直接操作输入序列而不需要特意设定输出词汇表。作者给出的答案是指针网络(Pointer Networks)。我们来看作者给出的一个例子:

 

图2:Pointer Networks实例:寻找凸包

这个图的例子是给定p1到p4四个二维点的坐标,要求找到一个凸包。显然答案是p1->p4->p2->p1。图a是传统seq2seq模型的做法,就是把四个点的坐标作为输入序列输入进去,然后提供一个词汇表:[start, 1, 2, 3, 4, end],最后依据词汇表预测出序列[start, 1, 4, 2, 1, end],缺点作者也提到过了,对于图a的传统seq2seq模型来说,它的输出词汇表已经限定,当输入序列的长度变化的时候(如变为10个点)它根本无法预测大于4的数字。图b是作者提出的Pointer Networks,它预测的时候每一步都找当前输入序列中权重最大的那个元素,而由于输出序列完全来自输入序列,它可以适应输入序列的长度变化。

那么Pointer Networks具体是怎样实现的呢?

我们首先来看传统注意力机制的公式:

 

图3:传统注意力机制公式

其中是encoder的隐状态,而是decoder的隐状态,v,W1,W2都是可学习的参数,在得到之后对其执行softmax操

Pointer Networks是一种用于组合优化问题的神经网络模型,它可以处理长度可变的序列输入并输出一个可重复的子集,这个子集的大小是固定的。 这个模型的代码可以在GitHub上找到,它被实现为一个TensorFlow模型。以下是代码的主要部分: ```python class PointerModel(object): def __init__(self, input_dim, hidden_dim, seq_len, lr=0.001): self.input_dim = input_dim self.hidden_dim = hidden_dim self.seq_len = seq_len self.lr = lr self.input_data = tf.placeholder(tf.float32, [None, self.input_dim]) self.target_data = tf.placeholder(tf.int32, [None, self.seq_len]) self.encoder_cell = tf.nn.rnn_cell.GRUCell(self.hidden_dim) self.decoder_cell = tf.nn.rnn_cell.GRUCell(self.hidden_dim) with tf.variable_scope('encoder'): self.encoder_outputs, self.encoder_final_state = tf.nn.dynamic_rnn(self.encoder_cell, self.input_data, dtype=tf.float32) with tf.variable_scope('decoder'): self.decoder_outputs, self.decoder_final_state = tf.nn.dynamic_rnn(self.decoder_cell, tf.zeros([tf.shape(self.input_data)[0], self.seq_len, self.hidden_dim]), dtype=tf.float32, initial_state=self.encoder_final_state) with tf.variable_scope('pointer'): self.W1 = tf.Variable(tf.random_normal([self.hidden_dim, self.hidden_dim], stddev=0.1)) self.W2 = tf.Variable(tf.random_normal([self.hidden_dim, self.hidden_dim], stddev=0.1)) self.v = tf.Variable(tf.random_normal([self.hidden_dim, 1], stddev=0.1)) def get_logits(i, prev_output): prev_output = tf.expand_dims(prev_output, 1) u = tf.tanh(tf.matmul(self.decoder_outputs[:, i, :], self.W1) + tf.matmul(prev_output, self.W2)) return tf.matmul(u, self.v) self.logits = [] prev_output = tf.zeros([tf.shape(self.input_data)[0], self.hidden_dim]) for i in range(self.seq_len): logit = get_logits(i, prev_output) self.logits.append(logit) prev_output = self.decoder_outputs[:, i, :] self.logits = tf.stack(self.logits, axis=1) self.probs = tf.nn.softmax(self.logits) loss_mask = 1 - tf.to_float(tf.equal(self.target_data, 0)) self.loss = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(logits=self.logits, labels=self.target_data) * loss_mask) self.train_op = tf.train.AdamOptimizer(self.lr).minimize(self.loss) ``` 这个模型的实现包括了一个编码器和一个解码器,以及一个指针网络层。编码器和解码器都是GRU单元。指针网络层使用了两个权重矩阵和一个向量来计算每个位置的指针概率,并将它们组合成一个概率分布。 在训练过程中,模型使用交叉熵损失函数来最小化预测值与真实值之间的差距,并使用Adam优化器来更新模型的参数。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值