Machine learning Tensorflow在文本生成期间更改RNN的批大小

Machine learning Tensorflow在文本生成期间更改RNN的批大小,machine-learning,tensorflow,deep-learning,Machine Learning,Tensorflow,Deep Learning,我构建了一个普通的角色级RNN,并根据一些数据对其进行了训练。在那之前一切都很顺利 但是现在我想用这个模型来生成文本。问题是在这个文本生成阶段,batch\u size为1,每个批次的num\u步数也不同 这导致了几个错误,我尝试了一些黑客修复,但它们不起作用。通常的处理方法是什么 编辑:更具体地说,我的输入占位符的形状为[None,num_steps],但问题在于初始状态不接受[None,hidden_size]的形状。我也处理过同样的问题。您需要处理两个问题。第一个是将批量大小和步长调整为1

我构建了一个普通的角色级RNN,并根据一些数据对其进行了训练。在那之前一切都很顺利

但是现在我想用这个模型来生成文本。问题是在这个文本生成阶段,batch\u size为1,每个批次的num\u步数也不同

这导致了几个错误,我尝试了一些黑客修复,但它们不起作用。通常的处理方法是什么


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

我也处理过同样的问题。您需要处理两个问题。第一个是将批量大小和步长调整为1。通过将输入序列中的“批次”和“长度”维度设置为“无”,可以轻松完成此操作。Ie[None,None,128],128代表128个ascii字符(尽管您可能使用更少的字符,因为您可能只需要字符的一个子集。)

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

我个人不喜欢将state_is_tupel设置为False,因为它已被弃用,所以我编写了自己的代码来展平state 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) 

如何使用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图中的define 2 branch,可能看起来不是很好。但如果您不想通过RNN单元的init_状态,这是一种方法

chasep255解决方案中所述,两个棘手的部分是:
初始状态
批量大小
&
序列长度

第一个棘手的部分:

如果我们将
batch\u size
sequence 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')
第二个棘手的部分:

下一步是定义动态
初始状态
。对于这一部分,如chasep255解决方案中所述,我们可以使用
占位符
,我们自己将
零状态
传递给RNN。为此,我使用API根据输入序列获得不同的批大小(在我的示例中:
self.inp
):

现在在培训中,我运行了两次
sess.run()
首先,用零值填充初始_状态。为此,我使用了一个大小为
[training\u batch\u size*hidden\lstm\u size]
的数组,并使用零值将其传递给
占位符Second,我使用
占位符再次将状态传递到下一个时间步,类似于:

  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) 
在推论中,我们可以做同样的事情。这一次,我们用大小为零的值
[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)
请参阅完整的代码

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)