tensorflow实现seq2seq模型细节(5):如何实现带attention的beam search,tensorflow构建独立的计算图(子图),推理图加载训练图的参数达到参数共享

为什么会有这样的问题,在对encoder构建attention时,训练时传入到AttentionWrapper的参数和解码时是不一样的。

构造attention的方法:

    def build_rnn_layer_attention(self,encoder_output,decoder_layer,decoder_num_units,src_seq_len):
        #scaled_luong  normed bahdanau
        attention = None
        if self.attention_mechanism == 'scaled_luong':
            attention = tf.contrib.seq2seq.LuongAttention(
                decoder_num_units, encoder_output,
                memory_sequence_length=src_seq_len,scale=True)
        else:
            attention = tf.contrib.seq2seq.BahdanauAttention(decoder_num_units,
                            encoder_output, src_seq_len, normalize = True)
        decoder_cell = tf.contrib.seq2seq.AttentionWrapper(
            decoder_layer, attention,
            attention_layer_size=decoder_num_units,name='attention')
        return decoder_cell

构造训练时decoder的attention参数:

 self.decoder_rnn_layer = self.get_rnn_layer(decoder_rnn_layer_size, decoder_num_units, decoder_cell_type)
            #加入attention
            self.decoder_rnn_layer_attention = self.build_rnn_layer_attention(encoder_outputs, self.decoder_rnn_layer, decoder_num_units,self.src_batch_seq_len)

构造预测/推理阶段attention时,需要对encoder_outputs,encoder_state,src_seq_len(源序列长度)做tile_batch。

 memory = tf.contrib.seq2seq.tile_batch(
                  encoder_outputs, multiplier=beam_width)
                src_seq_len = tf.contrib.seq2seq.tile_batch(
                      self.src_batch_seq_len, multiplier=beam_width)
                encoder_state = tf.contrib.seq2seq.tile_batch(
                      encoder_states, multiplier=beam_width)
                batch_size = self.inference_batch_size * beam_width
                inference_rnn_layer_attention = self.build_rnn_layer_attention(memory, rnn_layer, decoder_num_units,src_seq_len)

这导致了不能直接使用variable_scope来重用attention的参数,因为无论如何你需要在推理/预测阶段重新构建一个attention,传入不同的参数。接下来看看我找到的解决方法。

在nmt的教程中,有attention的模型使用beam search我看到实现中又声明了新的attention,但是令人疑惑的就是声明新的attention如何使用到训练过的参数,因为训练过程和推理过程传入attention的参数不一样,无论如何不可能使用同一个声明的attention,这个问题困扰了我很久。

Google后我终于找到了几乎一样的问题。

Stackoverflow上:https://stackoverflow.com/questions/46021216/implementing-attention-with-beam-search-in-tensorflow

 

GitHub nmt的issue:

https://github.com/tensorflow/nmt/issues/93

 

最后讨论出的解决方案是,训练和推理构建独立的计算图,推理阶段使用训练图中的参数,具体实现待求证。

 

  1. 方法一

被AttentionWrapper包装的rnn cell有一个方法set_weights和get_weights

我尝试在训练完成后将训练的attention的weights传递给推理的attention

但是weights的个数竟然不一样,传递了shape相同的2个weight后,测试发现

似乎是有效果的,但还是有问题,

  1. 方法二 之前提到stackoverflow探讨的方法

https://github.com/piyank22/Blog/blob/master/TheJourneyOfNLP.ipynb

关于这个问题的解决在clever way to implement two seperate graphs with the same trainable variables

 

这位老兄的建议如下:

我去看了代码,做法也大概是上面这样的:

训练和推理构建两个不同的图,先对训练图进行训练然后保存,

恢复时使用推理图从训练图中恢复参数。

这里nlp_nmt和nlp_nmt-2应该是一样的,估计是他写的时候忘记改了。

 

       按照这个方法,这个问题被完美解决了。需要注意一个问题就是构建不同子图时需要保证他们的相同部分的name_scope或者variable_scope一样,不然会出现找不到tensor的问题。

 

大概解决思路,训练和推理生成2个不同的子图,保存空的推理子图和训练好参数的训练图,加载模型时使用推理图但是恢复参数时从训练图中恢复。

    #加载模型
    def load_model(self):
        self.inference_graph = tf.Graph()
        self.sess = tf.InteractiveSession(graph=self.inference_graph)
        dir,file = os.path.split(self.model_path)
        # 导入的计算图是inference的
        loader = tf.train.import_meta_graph(dir+'/inference/'+file+".inference"+ '.meta')
        #导入的数据是训练图的
        loader.restore(self.sess, self.model_path)

我觉得这是一个很有用的方法可以实现任何参数共享的问题。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值