首页 文章

在文本生成期间,Tensorflow更改RNN的批量大小

提问于
浏览
4

我构建了一个vanilla角色级别的RNN并对一些数据进行了训练 . 一切都很好,直到那里 .

但现在我想使用该模型生成文本 . 问题是在此文本生成阶段, batch_size 为1,每批次 num_steps 也不同 .

这导致了几个错误,我尝试了一些hacky修复,但它们无法正常工作 . 处理这个问题的常用方法是什么?

编辑:更具体地说,我的输入占位符的形状为[None,num_steps],但问题在于初始状态不接受[None,hidden_size]的形状 .

3 回答

  • 0

    我已经解决了同样的问题 . 您需要处理两个问题 . 第一种方法是将批量大小和步长调整为1.您可以通过将输入序列中的批次和长度尺寸设置为无来轻松完成此操作 . 即[无,无,128],128代表128个ascii字符(尽管你可能使用较少,因为你可能只需要一个字符的子集 . )

    处理初始状态是最棘手的 . 这是因为您需要在对session.run()的调用之间保存它 . 由于num_steps为1,因此在每个步骤开始时将其初始化为零 . 我建议做的是允许初始状态作为占位符传递并从session.run()返回 . 这样,模型的用户可以在批次之间继续当前状态 . 最简单的方法是确保对于您使用的每个RNN,state_is_tupel都设置为False,您只需从动态RNN函数中获取最终状态张量 .

    我个人不喜欢将state_is_tupel设置为False,因为它已被弃用,所以我编写了自己的代码来展平状态tupel . 以下代码来自我的项目以生成声音 .

    batch_size = tf.shape(self.input_sound)[0]
            rnn = tf.nn.rnn_cell.MultiRNNCell([tf.nn.rnn_cell.LSTMCell(self.hidden_size) for _ in range(self.n_hidden)])  
            zero_state = pack_state_tupel(rnn.zero_state(batch_size, tf.float32))
            self.input_state = tf.placeholder_with_default(zero_state, None)
            state = unpack_state_tupel(self.input_state, rnn.state_size)
    
            rnn_input_seq = tf.cond(self.is_training, lambda: self.input_sound[:, :-1], lambda: self.input_sound)
            output, final_state = tf.nn.dynamic_rnn(rnn, rnn_input_seq, initial_state = state)
    
            with tf.variable_scope('output_layer'):
                output = tf.reshape(output, (-1, self.hidden_size))
                W = tf.get_variable('W', (self.hidden_size, self.sample_length))
                b = tf.get_variable('b', (self.sample_length,))
                output = tf.matmul(output, W) + b
                output = tf.reshape(output, (batch_size, -1, self.sample_length))
    
    
            self.output_state = pack_state_tupel(final_state)
            self.output_sound = output
    

    它使用以下两个功能,这些功能应该适用于任何类型的RNN,尽管我只使用此模型进行了测试 .

    def pack_state_tupel(state_tupel):
        if isinstance(state_tupel, tf.Tensor) or not hasattr(state_tupel, '__iter__'):
            return state_tupel
        else:
            return tf.concat(1, [pack_state_tupel(item) for item in state_tupel])
    
    def unpack_state_tupel(state_tensor, sizes):
        def _unpack_state_tupel(state_tensor_, sizes_, offset_):
            if isinstance(sizes_, tf.Tensor) or not hasattr(sizes_, '__iter__'): 
                return tf.reshape(state_tensor_[:, offset_ : offset_ + sizes_], (-1, sizes_)), offset_ + sizes_
            else:
                result = []
                for size in sizes_:
                    s, offset_ = _unpack_state_tupel(state_tensor_, size, offset_)
                    result.append(s)
                if isinstance(sizes_, tf.nn.rnn_cell.LSTMStateTuple):
                    return tf.nn.rnn_cell.LSTMStateTuple(*result), offset_
                else:
                    return tuple(result), offset_
        return _unpack_state_tupel(state_tensor, sizes, 0)[0]
    

    最后在我的生成函数中,看看我如何管理隐藏状态 s .

    def generate(self, seed, steps):
        def _step(x, s = None):
            feed_dict = {self.input_sound: np.reshape(x, (1, -1, self.sample_length))}
            if s is not None:
                feed_dict[self.input_state] = s
            return self.session.run([self.output_sound, self.output_state], feed_dict)
    
        seed_pad = self.sample_length - len(seed) % self.sample_length
        if seed_pad: seed = np.pad(seed, (seed_pad, 0), 'constant')
    
        y, s = _step(seed)
        y = y[:, -1:]
    
        result = [seed, y.flatten()]
        for _ in range(steps):
            y, s = _step(y, s)
            result.append(y.flatten())
    
        return np.concatenate(result)
    
  • 4

    如何使用tf的重用 .

    class Model():
         def __init__(self,batch_size,reuse)
              self.batch_size = batch_size
              self.reuse = reuse
              self.input_x = tf.placeholder(.....)
              self.input_y = tf.placeholder(.....)
         def inference(self)
              with tf.variable_scope('xxx',reuse=self.reuse)
                   ...
                   cell = tf.contrib.rnn.LSTMCell(xxx,reuse=self.reuse)
                   init_state = cell.zero_state(self.batch_size, dtype=tf.float32)
                   ...
         def train_op(self):
             ....
    
    if __name__ == '__main__':
          train_model = model(batch=128,reuse=False)
          test_model = model(batch=1,reuse=True)
          with tf.Session() as sess:
               sess.run(train_model.train_op,feed_dict={...})
               sess.run(test_model.prediction,feed_dict={...})
    

    当然,它看起来像在tf图中定义2分支,并且可能看起来不是很好 . 但是如果你不想传递RNN Cell的init_state,那就是一种方式 .

  • 0

    chasep255 解决方案中所述,两个棘手的部分是: initial_statebatch_sizesequence lenght .

    First tricky part:

    如果我们将 batch_sizesequence len 设置为 None ,我们可以在推理期间更改它 . 我们的第一步是将输入形状定义为 [None, None]

    self.inputs = tf.placeholder(tf.int32, shape=(None, None), name='inputs')
    self.targets = tf.placeholder(tf.int32, shape=(None, None), name='targets')
    

    Second tricky part:

    下一步是定义动态 initial_state . 对于这部分,如 chasep255 解决方案中所述,我们可以使用 placeholder ,其中我们自己将 zero_state 传递给 RNN . 为此,我使用tf.shape API根据输入序列获取不同的 batch size (在我的情况下: self.inp ):

    cells = [tf.nn.rnn_cell.GRUCell(self.rnn_size)] * self.layer_size
     rnn_cell = tf.nn.rnn_cell.MultiRNNCell(cells)
     self.init_state = rnn_cell.zero_state(tf.shape(self.inp)[0], tf.float32)
     self.rnn_outputs, self.final_state = tf.nn.dynamic_rnn(rnn_cell, self.inp,
                                                                       initial_state=self.init_state,
                                                                       dtype=tf.float32)
    

    现在在训练中,我跑两次 sess.run() . First ,用零值填充initial_state . 为此,我使用了一个大小为 [training_batch_size * hidden_lstm_size] 的数组,其值为零,以将其传递给 placeholder . Second ,我用 placeholder 再次使用传递状态到下一个时间步:

    new_state = sess.run(self.initial_state,
                                     feed_dict={self.inputs: np.zeros([self.batch_size_in_train, lstm_hidden_size], dtype=np.int32)})
    
      for x, y in batch_gen:
    
          feed_dict = {
                        self.inputs: x,
                        self.targets: y,
                        self.initial_state: new_state
                    }
                    _, step, new_state, loss = sess.run([self.optimizer, 
                                                       self.global_step, 
                                                       self.final_state, 
                                                       self.loss],
                                                       feed_dict)
    

    在推理中,我们可以做同样的事情 . 这次我们填充 initial_state ,其值为零 [1 * 1] . 我们的推理部分是:

    new_state = sess.run(self.initial_state, feed_dict={self.inputs: np.zeros([1, 1], dtype=np.int32)})
            for i in range(400):
                x = np.zeros((1, 1))
                x[0, 0] = c
                feed_dict = {
                    self.inputs: x,
                    self.keep_prob: 1,
                    self.initial_state: new_state
                }
                preds, new_state = sess.run(
                    [self.prediction, self.final_state],
                    feed_dict=feed_dict)
    

    查看完整代码here .

相关问题