带有数据集 API 的 Tensorflow 自定义估算器:嵌入查找 (feature_column) NMT 任务

Tensorflow custom Estimator with Dataset API: embedding lookup (feature_column) NMT task

我的问题在本质上与 Feature Columns Embedding lookup 很接近,但是我无法对那里给出的答案发表评论(没有足够的代表),我认为回答者要么没有完全理解问题,要么答案与要求的不完全一致。


目标

为使用数据集 API 提供数据的自定义 Estimator 提供服务。任务是NMT(seq2seq).


问题

Estimator 需要 feature_columns 作为投放的输入。我的 NMT 任务只有一个特征,即要翻译的输入句子(或者可能句子中的每个单词都是一个特征?)。所以我不确定如何使用我的输入句子作为可以输入 RNN 的特征(期望一个embedding_lookup [batch_size, max_seqence_len, embedding_dim]) 这最终将允许我为 Estimator 服务。


背景

我正在尝试利用自定义估算器来提供 seq2seq 样式的 NMT 实现。我需要能够通过 tf-serving 为模型提供服务,估计器似乎使它变得相对容易。

但是我遇到了 'how' 服务模型的障碍。据我所知,我需要 'feature_columns' 作为模型的输入。

https://github.com/MtDersvan/tf_playground/blob/master/wide_and_deep_tutorial/wide_and_deep_basic_serving.md

表明您需要一个 export_input_fn,它使用一个 feature_spec,它需要一个 feature_column(s) 作为输入。然而,这是有道理的,对于我的用例,我没有一堆(不同的)特征,而是我有输入句子(其中每个单词都是一个特征)需要通过嵌入查找并用作特征......

所以我知道我需要输入到我的模型中才能成为特征列。我对 NMT 的输入只是一个 [batch_size, max_sequence_len] 的张量,其中填充了句子中单词的索引(例如,对于 batch_size=1 [3, 17, 132, 2, 1, 0, ...] 其中每个索引应映射到一个嵌入向量)。通常我会通过

将其输入 embedding_lookup
    embs = tf.get_variable('embedding', [vocab_size, embedding_dim])
    tf.nn.embedding_lookup(embs, inputs)

我会很高兴,我可以将其作为输入提供给 RNN,剩下的就是历史,不是问题。

BUT,这是我遇到问题的地方,我需要使用 feature_columns(这样我才能为模型服务)。我在开头提到的问题的答案显示了如何使用 embedding_column,但他建议嵌入应该将整个句子查找为 一个单一特征 ,但传统上你会查找句子中的每个单词并获得它的嵌入。

同样,https://www.damienpontifex.com/2018/01/02/using-tensorflow-feature-columns-in-your-custom-estimator-model/

显示 'how to implement a feature-column in a custom estimator' 实际上他的 'Before' 代码完全正确(正如我写的那样),tf.get_variable 变成了 tf.nn.embedding_lookup,但是他的 'after' 代码,同样,只接受 1 个特征(整个句子?)。

我已经通过使用他们的代码并将我在 [batch_size、max_seq_len] 中的数据馈送到 tf.feature_column.categorical_column_with_identity 中验证了这一点,输出张量是 [batch_size , embedding_dim]

序列信息丢失?或者它只是被压扁了?当我打印输出时,它的大小 (?, embedding_dim) 在哪里?通常是我的 batch_size.

编辑: 我已经验证了形状是 [batch_size,embedding_dim],它不只是被压平了……所以序列信息是丢失

我猜它必须将输入视为 1 个单一输入特征(因此 batch_size=1 ex [3, 17, 132, 2, 1, 0, ...] 其中每个index maps to an embedding vector) 会映射到单个特征,这不是我们想要的,我们希望每个索引映射到一个嵌入,所需的输出是 [batch_size, max_seq_len, embedding_dim].

这听起来像是我需要的,不是一个 categorical_column_with_*,而是 max_seq_len 个数量(我的序列中每个单词 1 个),听起来对吗?每个词都是我模型的一个特征,所以我倾向于这是正确的方法,但这也有问题。我正在使用数据集 API,所以在我的 input_train_fn() 中,我从文件加载数据,然后使用 tf.data.Dataset.from_tensor_slices(data, labels) 将数据拆分为张量,我然后可以 dataset.batch(batch_size).make_one_shot_iterator().get_next() 输入我的 Estimator。我不能迭代每个批次(Tesors 不可迭代)所以我不能简单地为每个输入批次制作 100 feature_columns...

有人知道如何做到这一点吗? 这种嵌入查找是使用简单的占位符或变量(以及 NLP 任务中的常用方法)进行的非常简单的事情。但是当我冒险进入 Dataset API 和 Estimators 时,我 运行 进入了一个信息很少的墙(这不是一个基本的例子)。

我承认我的理解可能存在差距,自定义估算器和数据集 API 对我来说是新的,有时很难找到有关它们的信息。所以请随时向我发送信息。

感谢您阅读我的文字墙并希望对我有所帮助(以及我见过的其他人提出了与此类似的问题但没有得到答案 https://groups.google.com/a/tensorflow.org/forum/#!searchin/discuss/embeddings$20in$20custom$20estimator/discuss/U3vFQF_jeaY/EjgwRQ3RDQAJ 我为这个人感到难过,他的问题没有得到真正的回答(出于与此处概述的相同原因,他的帖子被劫持了......)。

如果我没理解错的话,您想使用 Estimator API 来构建 SeqSeq 模型。开始的好地方 here,查看 Problems-Solutions/text 文件夹。

为了回答你关于如何使用 emedding 查找的问题,这里有一个例子

vocab_table = lookup.index_table_from_file(vocabulary_file='data/vocab.csv', num_oov_buckets=1, default_value=-1)
text = features[commons.FEATURE_COL]
words = tf.string_split(text)
dense_words = tf.sparse_tensor_to_dense(words, default_value=commons.PAD_WORD)
word_ids = vocab_table.lookup(dense_words)

padding = tf.constant([[0, 0], [0, commons.MAX_DOCUMENT_LENGTH]])
# Pad all the word_ids entries to the maximum document length
word_ids_padded = tf.pad(word_ids, padding)
word_id_vector = tf.slice(word_ids_padded, [0, 0], [-1, commons.MAX_DOCUMENT_LENGTH])

word_id_vector = {commons.FEATURE_COL: word_id_vector}

bow_column = tf.feature_column.categorical_column_with_identity(commons.FEATURE_COL, num_buckets=params.N_WORDS)
bow_embedding_column = tf.feature_column.embedding_column(bow_column, dimension=50, combiner='sqrtn')
bow = tf.feature_column.input_layer(word_id_vector, feature_columns=[bow_embedding_column])
logits = tf.layers.dense(bow, 2, activation=None)

以上代码可以包装在 Estimator model_fn 中。上面的 repo 包含此代码。请看一下。

所以我最终完成这项工作的方法是让每个单词成为输入特征,然后我简单地进行 wrd_2_idx 转换,将其作为特征传入 numerical_column(s ,你有 max_seq_lens 个),然后将这些列传递给 input_layer。然后在我的图表中,我使用这些功能并正常查找嵌入。基本上绕过了 embedding_column 查找,因为我无法弄清楚如何让它按照我想要的方式运行。这可能不是最佳的,但它可以工作并训练...

我会把这个作为公认的答案,希望在未来的某个时候我能找到更好的方法,或者其他人可以启发我解决这个问题的最佳方法。

我设法让它工作了……也因为 RNN 没有使用嵌入这一事实而出轨了。

我做了什么让这个工作(在最简单的情况下):

#features[VALUE_FEATURE_NAME] is shape (?, 200), ie. 200 words per row
inputs = tf.contrib.layers.embed_sequence(
  features[VALUES_FEATURE_NAME], 3000, 5,
)

# create an LSTM cell of size 100
lstm_cell = tf.nn.rnn_cell.BasicLSTMCell(200)

# create the complete LSTM
_, final_states = tf.nn.dynamic_rnn(
    lstm_cell, inputs, dtype=tf.float32)
outputs = final_states.h 

所以我想答案就在动态 rnn 的 tensorflow 文档中

Creates a recurrent neural network specified by RNNCell cell.

Performs fully dynamic unrolling of inputs.

所以这里的展开意味着RNN消耗[batch,time_steps,values]作为输入。

最佳

可以用tf.contrib.feature_column.sequence_categorical_column_with_vocabulary_listtf.contrib.feature_column.sequence_input_layer来解决

演示代码如下:

import tensorflow as tf
if __name__ == "__main__":
  #tf.enable_eager_execution()
  feature = {
      'aa': [['1', '2', '3'], 
             ['-1', '4', '-1'], 
             ['2', '-1', '-1'],
             ['4', '5', '6']]
  }

  aa_id = tf.contrib.feature_column.sequence_categorical_column_with_vocabulary_list(
      'aa', ['1', '2', '3', '4', '5']
    )
  seq_emb_matrix = tf.feature_column.embedding_column(aa_id, 2)
  seq_tensor, seq_length = tf.contrib.feature_column.sequence_input_layer(feature, [seq_emb_matrix])
  seq_tensor1, seq_length1 = tf.contrib.feature_column.sequence_input_layer(feature1, [seq_emb_matrix])
  seq_tensor2 = tf.squeeze(seq_tensor1)
  # print(tensor)
  with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    sess.run(tf.tables_initializer())
    a, a_len = sess.run([seq_tensor, seq_length])
    b, b_len = sess.run([seq_tensor1, seq_length1])
    print(a)
    print('a_len', a_len)
    print(a.shape)
    print('-'*50)
    print(b)
    print('b_len', b_len)
    print(b.shape)
    print(sess.run([seq_tensor2]))

打印结果如下:

[[[ 0.5333682  -0.39895234]
  [ 0.5335079   0.64998794]
  [-1.0432893  -0.8391434 ]]

 [[ 0.          0.        ]
  [-0.29623085 -0.17570129]
  [ 0.          0.        ]]

 [[ 0.5335079   0.64998794]
  [ 0.          0.        ]
  [ 0.          0.        ]]

 [[-0.29623085 -0.17570129]
  [ 0.7100604   0.9935588 ]
  [ 0.          0.        ]]]
('a_len', array([3, 3, 3, 3]))
(4, 3, 2)
--------------------------------------------------
[[[-0.24147142 -0.37740025]]

 [[-0.6222648   1.3862932 ]]

 [[ 1.2226609  -0.68292266]]]
('b_len', array([1, 1, 1]))
(3, 1, 2)
[array([[-0.24147142, -0.37740025],
       [-0.6222648 ,  1.3862932 ],
       [ 1.2226609 , -0.68292266]], dtype=float32)]