我想使用tensorflow生成文本,并一直在修改LSTM教程(https://www.tensorflow.org/versions/master/tutorials/recurrent/index.html#recurrent-neural-networks)代码以这样做,但是我最初的解决方案似乎产生了废话,即使经过很长时间的训练,它也没有改善。我不明白为什么。这个想法是从零矩阵开始,然后一次生成一个单词。
这是代码,我在其中添加了以下两个函数 https://tensorflow.googlesource.com/tensorflow/+/master/tensorflow/models/rnn/ptb/ptb_word_lm.py
生成器如下所示
def generate_text(session,m,eval_op):
state = m.initial_state.eval()
x = np.zeros((m.batch_size,m.num_steps), dtype=np.int32)
output = str()
for i in xrange(m.batch_size):
for step in xrange(m.num_steps):
try:
# Run the batch
# targets have to bee set but m is the validation model, thus it should not train the neural network
cost, state, _, probabilities = session.run([m.cost, m.final_state, eval_op, m.probabilities],
{m.input_data: x, m.targets: x, m.initial_state: state})
# Sample a word-id and add it to the matrix and output
word_id = sample(probabilities[0,:])
output = output + " " + reader.word_from_id(word_id)
x[i][step] = word_id
except ValueError as e:
print("ValueError")
print(output)
我已将变量“概率”添加到 ptb_model 中,它只是 logits 上的 softmax。
self._probabilities = tf.nn.softmax(logits)
取样:
def sample(a, temperature=1.0):
# helper function to sample an index from a probability array
a = np.log(a) / temperature
a = np.exp(a) / np.sum(np.exp(a))
return np.argmax(np.random.multinomial(1, a, 1))
我一直在朝着完全相同的目标努力,并且刚刚让它发挥作用。您在这里进行了许多正确的修改,但我认为您错过了一些步骤。
首先,为了生成文本,您需要创建仅代表单个时间步的模型的不同版本。原因是我们需要对每个输出 y 进行采样,然后才能将其输入模型的下一步。我通过创建一个新配置来做到这一点,将
num_steps
和 batch_size
都设置为 1。
class SmallGenConfig(object):
"""Small config. for generation"""
init_scale = 0.1
learning_rate = 1.0
max_grad_norm = 5
num_layers = 2
num_steps = 1 # this is the main difference
hidden_size = 200
max_epoch = 4
max_max_epoch = 13
keep_prob = 1.0
lr_decay = 0.5
batch_size = 1
vocab_size = 10000
我还用这些行向模型添加了概率:
self._output_probs = tf.nn.softmax(logits)
和
@property
def output_probs(self):
return self._output_probs
然后,我的
generate_text()
功能有一些差异。第一个是我使用 tf.train.Saver()
对象从磁盘加载保存的模型参数。请注意,我们在使用上面的新配置实例化 PTBModel 后执行此操作。
def generate_text(train_path, model_path, num_sentences):
gen_config = SmallGenConfig()
with tf.Graph().as_default(), tf.Session() as session:
initializer = tf.random_uniform_initializer(-gen_config.init_scale,
gen_config.init_scale)
with tf.variable_scope("model", reuse=None, initializer=initializer):
m = PTBModel(is_training=False, config=gen_config)
# Restore variables from disk.
saver = tf.train.Saver()
saver.restore(session, model_path)
print("Model restored from file " + model_path)
第二个区别是我获取了从 ids 到单词字符串的查找表(我必须编写这个函数,请参阅下面的代码)。
words = reader.get_vocab(train_path)
我按照与您相同的方式设置了初始状态,但随后我以不同的方式设置了初始令牌。我想使用“句子结尾”标记,以便我可以用正确类型的单词开始句子。我查看了索引这个词,发现
<eos>
恰好有索引 2(确定性),所以我只是将其硬编码进去。最后,我将它包装在 1x1 Numpy 矩阵中,这样它就是模型输入的正确类型.
state = m.initial_state.eval()
x = 2 # the id for '<eos>' from the training set
input = np.matrix([[x]]) # a 2D numpy matrix
最后,这是我们生成句子的部分。请注意,我们告诉
session.run()
计算 output_probs
和 final_state
。我们给它输入和状态。在第一次迭代中,输入是 <eos>
,状态是 initial_state
,但在后续迭代中,我们将最后一次采样的输出作为输入,并从最后一次迭代传递状态。另请注意,我们使用 words
列表从输出索引中查找单词字符串。
text = ""
count = 0
while count < num_sentences:
output_probs, state = session.run([m.output_probs, m.final_state],
{m.input_data: input,
m.initial_state: state})
x = sample(output_probs[0], 0.9)
if words[x]=="<eos>":
text += ".\n\n"
count += 1
else:
text += " " + words[x]
# now feed this new word as input into the next iteration
input = np.matrix([[x]])
然后我们要做的就是打印出我们积累的文本。
print(text)
return
这就是
generate_text()
函数。
最后,让我向您展示
get_vocab()
的函数定义,我将其放入 reader.py 中。
def get_vocab(filename):
data = _read_words(filename)
counter = collections.Counter(data)
count_pairs = sorted(counter.items(), key=lambda x: (-x[1], x[0]))
words, _ = list(zip(*count_pairs))
return words
您需要做的最后一件事是能够在训练后保存模型,如下所示
save_path = saver.save(session, "/tmp/model.ckpt")
这就是您稍后在生成文本时将从磁盘加载的模型。
还有一个问题:我发现有时 Tensorflow softmax 函数生成的概率分布之和并不精确为 1.0。当总和大于 1.0 时,
np.random.multinomial()
会抛出错误。所以我必须编写自己的采样函数,如下所示
def sample(a, temperature=1.0):
a = np.log(a) / temperature
a = np.exp(a) / np.sum(np.exp(a))
r = random.random() # range: [0,1)
total = 0.0
for i in range(len(a)):
total += a[i]
if total>r:
return i
return len(a)-1
当你把所有这些放在一起时,这个小模型能够生成一些很酷的句子。
我用了你的代码,好像不对。所以我稍微修改一下,看起来可行。 这是我的代码,我不确定它是否正确:
def generate_text(session,m,eval_op, word_list):
output = []
for i in xrange(20):
state = m.initial_state.eval()
x = np.zeros((1,1), dtype=np.int32)
y = np.zeros((1,1), dtype=np.int32)
output_str = ""
for step in xrange(100):
if True:
# Run the batch
# targets have to bee set but m is the validation model, thus it should not train the neural network
cost, state, _, probabilities = session.run([m.cost, m.final_state, eval_op, m.probabilities],
{m.input_data: x, m.targets: y, m.initial_state: state})
# Sample a word-id and add it to the matrix and output
word_id = sample(probabilities[0,:])
if (word_id<0) or (word_id > len(word_list)):
continue
#print(word_id)
output_str = output_str + " " + word_list[word_id]
x[0][0] = word_id
print(output_str)
output.append(output_str)
return output