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

Sil*_*ash 5 machine-learning deep-learning tensorflow

我构建了一个普通的字符级 RNN,并在一些数据上对其进行了训练。直到那里一切正常。

但是现在我想使用模型来生成文本。问题是在这个文本生成阶段,batch_size为 1,每批的num_steps也不同。

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

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

cha*_*255 4

我已经处理过同样的问题。您需要处理两个问题。第一个是将批量大小和步长调整为 1。您可以通过将输入序列中的批量和长度维度设置为无来轻松完成此操作。即 [None, None, 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
Run Code Online (Sandbox Code Playgroud)

它使用以下两个函数,它们应该适用于任何类型的 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]
Run Code Online (Sandbox Code Playgroud)

最后在我的生成函数中看看我如何管理隐藏状态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) 
Run Code Online (Sandbox Code Playgroud)