我一直试图通过https://www.tensorflow.org/tutorials/recurrent了解示例代码 您可以在https://github.com/tensorflow/models/blob/master/tutorials/rnn/ptb/ptb_word_lm.py
找到(使用tensorflow 1.3.0。)
我总结(我认为是)关键部分,对于我的问题,如下:
size = 200
vocab_size = 10000
layers = 2
# input_.input_data is a 2D tensor [batch_size, num_steps] of
# word ids, from 1 to 10000
cell = tf.contrib.rnn.MultiRNNCell(
[tf.contrib.rnn.BasicLSTMCell(size) for _ in range(2)]
)
embedding = tf.get_variable(
"embedding", [vocab_size, size], dtype=tf.float32)
inputs = tf.nn.embedding_lookup(embedding, input_.input_data)
inputs = tf.unstack(inputs, num=num_steps, axis=1)
outputs, state = tf.contrib.rnn.static_rnn(
cell, inputs, initial_state=self._initial_state)
output = tf.reshape(tf.stack(axis=1, values=outputs), [-1, size])
softmax_w = tf.get_variable(
"softmax_w", [size, vocab_size], dtype=data_type())
softmax_b = tf.get_variable("softmax_b", [vocab_size], dtype=data_type())
logits = tf.matmul(output, softmax_w) + softmax_b
# Then calculate loss, do gradient descent, etc.
我最大的问题是如果给出句子的前几个单词,我如何使用生成的模型实际生成下一个单词的建议?具体来说,我认为流程是这样的,但我无法理解评论行的代码是什么:
prefix = ["What", "is", "your"]
state = #Zeroes
# Call static_rnn(cell) once for each word in prefix to initialize state
# Use final output to set a string, next_word
print(next_word)
我的子问题是:
(我将他们全部问为一个问题,因为我怀疑他们都是连接在一起的,并且在我的理解中与一些差距有关。)
我期待在这里看到的是加载现有的word2vec字嵌入集(例如使用gensim' s KeyedVectors.load_word2vec_format()
),在每个句子加载时将输入语料库中的每个单词转换为该表示,然后LSTM会吐出一个相同维度的向量,我们会尝试找到最相似的单词(例如使用gensim' s similar_by_vector(y, topn=1)
)。
使用softmax可以从相对较慢的similar_by_vector(y, topn=1)
电话中拯救我们吗?
顺便说一下,对于我问题Using pre-trained word2vec with LSTM for word generation中已有的word2vec部分是类似的。然而,目前那里的答案并不是我想要的。我所希望的是一个简单的英语解释,可以为我打开灯光,并插入我理解中的任何差距。 Use pre-trained word2vec in lstm language model?是另一个类似的问题。
更新: Predicting next word using the language model tensorflow example和Predicting the next word using the LSTM ptb model tensorflow example是类似的问题。但是,两者都没有显示实际获取句子前几个单词的代码,并打印出对下一个单词的预测。我尝试从第二个问题和https://stackoverflow.com/a/39282697/841830(带有github分支)中的代码粘贴,但无法运行而没有错误。我认为它们可能是早期版本的TensorFlow?
另一个更新:另一个问题基本上是同一个问题:Predicting Next Word of LSTM Model from Tensorflow Example 它链接到 Predicting next word using the language model tensorflow example(而且,那里的答案并不完全符合我的要求)。
如果它仍然不清楚,我正在尝试编写一个名为getNextWord(model, sentencePrefix)
的高级函数,其中model
是我之前构建的LSTM,我已经加载了来自磁盘,sentencePrefix
是一个字符串,例如"打开",它可能会返回" pod"。然后我可以用&#34打开它;打开容器"它将返回" bay"等等。
一个示例(带有字符RNN,并使用mxnet)是https://github.com/zackchase/mxnet-the-straight-dope/blob/master/chapter05_recurrent-neural-networks/simple-rnn.ipynb结尾附近显示的sample()
函数
您可以在培训期间致电sample()
,但您也可以在培训后和任何您想要的句子中拨打电话。
答案 0 :(得分:7)
加载自定义数据而不是使用测试集:
reader.py@ptb_raw_data
test_path = os.path.join(data_path, "ptb.test.txt")
test_data = _file_to_word_ids(test_path, word_to_id) # change this line
test_data
应包含单词ID(打印word_to_id
以进行映射)。例如,它应该看起来像:[1,52,562,246] ......
我们需要在调用logits
sess.run
)
ptb_word_lm.py@PTBModel.__init__
logits = tf.reshape(logits, [self.batch_size, self.num_steps, vocab_size])
self.top_word_id = tf.argmax(logits, axis=2) # add this line
ptb_word_lm.py@run_epoch
fetches = {
"cost": model.cost,
"final_state": model.final_state,
"top_word_id": model.top_word_id # add this line
}
稍后在函数中,vals['top_word_id']
将有一个整数数组,其顶部字的ID。在word_to_id
中查找以确定预测的单词。我之前用小型模型做过这一点,前1的准确度相当低(20-30%iirc),尽管困惑是标题中的预测。
为什么要使用随机(未初始化,未经训练)的字嵌入?
您必须询问作者,但在我看来,培训嵌入使这更像是一个独立的教程:它不是将嵌入视为黑盒子,而是显示它是如何工作的。
为什么要使用softmax?
最终预测不由与隐藏层输出的余弦相似性确定。在LSTM之后有一个FC层,它将嵌入状态转换为最终字的单热编码。
这里是神经网络中操作和维度的草图:
word -> one hot code (1 x vocab_size) -> embedding (1 x hidden_size) -> LSTM -> FC layer (1 x vocab_size) -> softmax (1 x vocab_size)
隐藏层是否必须与输入的维度相匹配(即word2vec嵌入的维度)
从技术上讲,没有。如果你看一下LSTM方程,你会注意到x(输入)可以是任何大小,只要适当调整权重矩阵。
如何/我可以引入预先训练过的word2vec模型,而不是未初始化的模型吗?
我不知道,抱歉。
答案 1 :(得分:6)
我最大的问题是,如果给出句子的前几个单词,我如何使用生成的模型实际生成下一个单词建议?
即。我试图用签名编写一个函数:getNextWord(model,sentencePrefix)
在我解释我的答案之前,先向# Call static_rnn(cell) once for each word in prefix to initialize state
提一下你的建议:请记住,static_rnn
不会返回像numpy数组那样的值,而是一个张量。您可以在会话中运行(1)会话时将张量评估为值(会话保持计算图的状态,包括模型参数的值)和(2)计算所需的输入张量值。输入可以使用输入阅读器(教程中的方法)或使用占位符(我将在下面使用)提供。
现在遵循实际答案:
本教程中的模型旨在从文件中读取输入数据。 @ user3080953的答案已经展示了如何使用您自己的文本文件,但据我所知,您需要更多地控制数据如何馈送到模型。为此,您需要定义自己的占位符,并在调用session.run()
时将数据提供给这些占位符。
在下面的代码中,我将PTBModel
子类化,并使其负责将数据显式提供给模型。我介绍了一个特殊的PTBInteractiveInput
,其界面类似于PTBInput
,因此您可以重用PTBModel
中的功能。要训练您的模型,您仍然需要PTBModel
。
class PTBInteractiveInput(object):
def __init__(self, config):
self.batch_size = 1
self.num_steps = config.num_steps
self.input_data = tf.placeholder(dtype=tf.int32, shape=[self.batch_size, self.num_steps])
self.sequence_len = tf.placeholder(dtype=tf.int32, shape=[])
self.targets = tf.placeholder(dtype=tf.int32, shape=[self.batch_size, self.num_steps])
class InteractivePTBModel(PTBModel):
def __init__(self, config):
input = PTBInteractiveInput(config)
PTBModel.__init__(self, is_training=False, config=config, input_=input)
output = self.logits[:, self._input.sequence_len - 1, :]
self.top_word_id = tf.argmax(output, axis=2)
def get_next(self, session, prefix):
prefix_array, sequence_len = self._preprocess(prefix)
feeds = {
self._input.sequence_len: sequence_len,
self._input.input_data: prefix_array,
}
fetches = [self.top_word_id]
result = session.run(fetches, feeds)
self._postprocess(result)
def _preprocess(self, prefix):
num_steps = self._input.num_steps
seq_len = len(prefix)
if seq_len > num_steps:
raise ValueError("Prefix to large for model.")
prefix_ids = self._prefix_to_ids(prefix)
num_items_to_pad = num_steps - seq_len
prefix_ids.extend([0] * num_items_to_pad)
prefix_array = np.array([prefix_ids], dtype=np.float32)
return prefix_array, seq_len
def _prefix_to_ids(self, prefix):
# should convert your prefix to a list of ids
pass
def _postprocess(self, result):
# convert ids back to strings
pass
在__init__
的{{1}}函数中,您需要添加以下行:
PTBModel
为什么要使用随机(未初始化,未经训练)的字嵌入?
首先请注意,尽管嵌入在开始时是随机的,但它们将与网络的其余部分一起训练。训练后获得的嵌入将具有与使用word2vec模型获得的嵌入相似的属性,例如,通过向量操作回答类比问题的能力(王者+女人=女王等)在任务中你有相当多的数量训练数据如语言建模(不需要注释的训练数据)或神经机器翻译,从头开始训练嵌入是比较常见的。
为什么要使用softmax?
Softmax是将相似性得分(logits)的向量归一化为概率分布的函数。您需要一个概率分布来训练您具有交叉熵损失的模型,并能够从模型中进行采样。请注意,如果您只对训练模型中最可能的单词感兴趣,则不需要softmax,您可以直接使用logits。
隐藏层是否必须与输入的维度相匹配(即word2vec嵌入的维度)
不,原则上它可以是任何价值。但是,使用尺寸低于嵌入维度的隐藏状态并没有多大意义。
如何/我可以引入预先训练过的word2vec模型,而不是未初始化的模型吗?
这是一个使用给定numpy数组初始化嵌入的自包含示例。如果您希望在培训期间嵌入保持固定/不变,请将self.logits = logits
设置为trainable
。
False
答案 2 :(得分:4)
有很多问题,我会尝试澄清一些问题。
如果给出句子的前几个单词,如何使用生成的模型实际生成下一个单词建议?
这里的关键点是,下一个单词生成实际上是词汇表中的单词分类。所以你需要一个分类器,这就是输出中有softmax的原因。
原则是,在每个时间步,模型将基于最后一个字嵌入和前一个字的内部存储器输出下一个字。 tf.contrib.rnn.static_rnn
自动将输入组合到内存中,但我们需要提供嵌入的最后一个单词并对下一个单词进行分类。
我们可以使用预先训练过的word2vec模型,只需使用预先训练过的embedding
矩阵初始化function updateArray(array $data)
{
$result = array();
foreach ($data as $key => $value) {
if (is_integer($key) && is_string($value)) { // key is integer and value is string
$result[$value] = $value; // update the key
} elseif (is_array($value)) { // value is array
$result[$key] = updateArray($value); // recurse
} else {
$result[$key] = $value; // leave key/value alone
}
}
return $result;
}
print_r(updateArray($arr));
矩阵。我认为本教程使用随机矩阵是为了简单起见。内存大小与嵌入大小无关,您可以使用更大的内存大小来保留更多信息。
这些教程是高级的。如果你想深入了解细节,我建议你看看普通的python / numpy中的源代码。
答案 3 :(得分:2)
您可以在答案的最后找到所有代码。
我估计你的大多数问题(为什么使用Softmax,如何使用预训练嵌入层等等)都得到了回答。然而,当你还在等待一个简洁的代码从种子生成生成的文本时,我试着报告我最终是如何自己做的。
我从官方的Tensorflow教程开始,为了能够轻松地从生产的模型中生成单词,我一直在努力。幸运的是,在您在问题中提到的几乎所有答案中得到一些答案之后,我对问题(和解决方案)有了更好的了解。这可能包含错误,但至少它会运行并生成一些文本......
如果给出句子的前几个单词,如何使用生成的模型实际生成下一个单词建议?
我将在循环中包含下一个单词建议,以生成一个完整的句子,但您只需将其简化为一个单词。
假设您按照tensorflow(编写时的v1.4)here给出的当前教程,这将在训练后保存模型。
那么我们要做的就是从磁盘加载它,并编写一个函数,它接受这个模型和一些种子输入并返回生成的文本。
我假设我们在一个新的python脚本中编写了所有这些代码。在底部的整个脚本作为回顾,在这里我解释主要步骤。
FLAGS = tf.flags.FLAGS
FLAGS.model = "medium" # or whatever size you used
现在,非常重要的是,我们创建字典以将id映射到单词,反之亦然(因此我们不必读取整数列表......)。
word_to_id = reader._build_vocab('../data/ptb.train.txt') # here we load the word -> id dictionnary ()
id_to_word = dict(zip(word_to_id.values(), word_to_id.keys())) # and transform it into id -> word dictionnary
_, _, test_data, _ = reader.ptb_raw_data('../data')
然后我们加载配置类,同时将num_steps
和batch_size
设置为1,因为我们希望一次采样1个字,而LSTM也将处理1一次一个字。还可以动态创建输入实例:
eval_config = get_config()
eval_config.num_steps = 1
eval_config.batch_size = 1
model_input = PTBInput(eval_config, test_data)
要加载已保存的模型(由教程中的Supervisor.saver
模块保存),我们首先需要重建图表(很容易使用PTBModel
类)必须使用与训练时相同的相同配置:
sess = tf.Session()
initializer = tf.random_uniform_initializer(-eval_config.init_scale, eval_config.init_scale)
# not sure but seems to need the same name for variable scope as when saved ....!!
with tf.variable_scope("Model", reuse=None, initializer=initializer):
tf.global_variables_initializer()
mtest = PTBModel(is_training=False, config=eval_config, input=model_input)
sess.run(tf.global_variables_initializer())
saver = tf.train.Saver()
saver.restore(sess, tf.train.latest_checkpoint('../Whatever_folder_you_saved_in')) # the path must point to the hierarchy where your 'checkpoint' file is
首先,我们需要模型包含对logits输出的访问,或者更准确地说是整个词汇表中的概率分布。
所以在ptb_lstm.py
文件中添加以下行:
# the line goes somewhere below the reshaping "logits = tf.reshape(logits, [self.batch_size, ..."
self.probas = tf.nn.softmax(logits, name="probas")
然后我们可以设计一些采样函数(你可以自由地使用你喜欢的任何东西,最好的方法是使用温度进行采样,这会使分布变平或锐化),这里是一个基本的随机抽样方法:
def sample_from_pmf(probas):
t = np.cumsum(probas)
s = np.sum(probas)
return int(np.searchsorted(t, np.random.rand(1) * s))
最后一个函数将种子,你的模型,将单词映射到id的字典,反之亦然,作为输入和输出生成的文本字符串:
def generate_text(session, model, word_to_index, index_to_word,
seed='</s>', n_sentences=10):
sentence_cnt = 0
input_seeds_id = [word_to_index[w] for w in seed.split()]
state = session.run(model.initial_state)
# Initiate network with seeds up to the before last word:
for x in input_seeds_id[:-1]:
feed_dict = {model.initial_state: state,
model.input.input_data: [[x]]}
state = session.run([model.final_state], feed_dict)
text = seed
# Generate a new sample from previous, starting at last word in seed
input_id = [[input_seeds_id[-1]]]
while sentence_cnt < n_sentences:
feed_dict = {model.input.input_data: input_id,
model.initial_state: state}
probas, state = session.run([model.probas, model.final_state],
feed_dict=feed_dict)
sampled_word = sample_from_pmf(probas[0])
if sampled_word == word_to_index['</s>']:
text += '.\n'
sentence_cnt += 1
else:
text += ' ' + index_to_word[sampled_word]
input_wordid = [[sampled_word]]
return text
不要忘记添加以下行:
self.probas = tf.nn.softmax(logits, name='probas')
在ptb_lstm.py
文件的__init__
类的PTBModel
定义中,位于logits = tf.reshape(logits, [self.batch_size, self.num_steps, vocab_size])
行之后的任何位置。
整个脚本,只需从您拥有reader.py
,ptb_lstm.py
的同一目录运行:
import reader
import numpy as np
import tensorflow as tf
from ptb_lstm import PTBModel, get_config, PTBInput
FLAGS = tf.flags.FLAGS
FLAGS.model = "medium"
def sample_from_pmf(probas):
t = np.cumsum(probas)
s = np.sum(probas)
return int(np.searchsorted(t, np.random.rand(1) * s))
def generate_text(session, model, word_to_index, index_to_word,
seed='</s>', n_sentences=10):
sentence_cnt = 0
input_seeds_id = [word_to_index[w] for w in seed.split()]
state = session.run(model.initial_state)
# Initiate network with seeds up to the before last word:
for x in input_seeds_id[:-1]:
feed_dict = {model.initial_state: state,
model.input.input_data: [[x]]}
state = session.run([model.final_state], feed_dict)
text = seed
# Generate a new sample from previous, starting at last word in seed
input_id = [[input_seeds_id[-1]]]
while sentence_cnt < n_sentences:
feed_dict = {model.input.input_data: input_id,
model.initial_state: state}
probas, state = sess.run([model.probas, model.final_state],
feed_dict=feed_dict)
sampled_word = sample_from_pmf(probas[0])
if sampled_word == word_to_index['</s>']:
text += '.\n'
sentence_cnt += 1
else:
text += ' ' + index_to_word[sampled_word]
input_wordid = [[sampled_word]]
print(text)
if __name__ == '__main__':
word_to_id = reader._build_vocab('../data/ptb.train.txt') # here we load the word -> id dictionnary ()
id_to_word = dict(zip(word_to_id.values(), word_to_id.keys())) # and transform it into id -> word dictionnary
_, _, test_data, _ = reader.ptb_raw_data('../data')
eval_config = get_config()
eval_config.batch_size = 1
eval_config.num_steps = 1
model_input = PTBInput(eval_config, test_data, name=None)
sess = tf.Session()
initializer = tf.random_uniform_initializer(-eval_config.init_scale,
eval_config.init_scale)
with tf.variable_scope("Model", reuse=None, initializer=initializer):
tf.global_variables_initializer()
mtest = PTBModel(is_training=False, config=eval_config,
input_=model_input)
sess.run(tf.global_variables_initializer())
saver = tf.train.Saver()
saver.restore(sess, tf.train.latest_checkpoint('../models'))
while True:
print(generate_text(sess, mtest, word_to_id, id_to_word, seed="this sentence is"))
try:
raw_input('press Enter to continue ...\n')
except KeyboardInterrupt:
print('\b\bQuiting now...')
break
至于恢复旧的检查点(对我来说,6个月之前保存的模型,不确定当时使用的确切TF版本)和最近的张量流(至少1.6),它可能会引发一些关于未找到的变量的错误(请参阅注释) )。 在这种情况下,您应该使用this script更新检查点。
另外,请注意,对于我来说,我必须进一步修改它,因为我注意到saver.restore
函数试图读取lstm_cell
变量,尽管我的变量已转换为basic_lstm_cell
还带领NotFound Error
。因此,只需对checkpoint_convert.py
脚本(第72-73行)进行少量更改即可轻松修复新名称中的basic_
。
检查检查点中包含的变量名称的便捷方法是(CKPT_FILE
是.index
之前的后缀,.data0000-1000
等。):
reader = tf.train.NewCheckpointReader(CKPT_FILE)
reader.get_variable_to_shape_map()
通过这种方式,您可以验证您确实拥有正确的名称(或旧检查点版本中的错误名称)。