Machine learning Tensorflow在文本生成期间更改RNN的批大小
我构建了一个普通的角色级RNN,并根据一些数据对其进行了训练。在那之前一切都很顺利 但是现在我想用这个模型来生成文本。问题是在这个文本生成阶段,batch\u size为1,每个批次的num\u步数也不同 这导致了几个错误,我尝试了一些黑客修复,但它们不起作用。通常的处理方法是什么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
编辑:更具体地说,我的输入占位符的形状为[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)