【问题标题】:How to build an embedding layer in Tensorflow RNN?如何在 Tensorflow RNN 中构建嵌入层?
【发布时间】:2019-02-09 14:27:48
【问题描述】:

我正在构建一个 RNN LSTM 网络,以根据作者的年龄(二元分类 - 年轻/成人)对文本进行分类。

似乎网络没有学习并突然开始过度拟合:


红色:火车
蓝色:验证

一种可能是数据表示不够好。我只是按频率对唯一单词进行排序并给它们索引。例如:

unknown -> 0
the     -> 1
a       -> 2
.       -> 3
to      -> 4

所以我试图用词嵌入来代替它。 我看到了几个例子,但我无法在我的代码中实现它。大多数示例如下所示:

embedding = tf.Variable(tf.random_uniform([vocab_size, hidden_size], -1, 1))
inputs = tf.nn.embedding_lookup(embedding, input_data)

这是否意味着我们正在构建一个学习嵌入的层?我认为应该下载一些 Word2Vec 或 Glove 并使用它。

无论如何,假设我想构建这个嵌入层...
如果我在我的代码中使用这两行,我会得到一个错误:

TypeError:传递给参数“indices”的值的 DataType float32 不在允许值列表中:int32、int64

所以我想我必须将input_data 类型更改为int32。所以我这样做了(毕竟都是索引),我得到了这个:

TypeError: 输入必须是一个序列

我尝试按照this answer 中的建议将inputstf.contrib.rnn.static_rnn 的参数)包装成一个列表:[inputs],但这又产生了另一个错误:

ValueError:输入大小(输入的维度 0)必须可以通过 形状推断,但看到值 None。


更新:

在将张量 x 传递给 embedding_lookup 之前,我将其拆开堆叠。我在嵌入后移动了拆垛。

更新代码:

MIN_TOKENS = 10
MAX_TOKENS = 30
x = tf.placeholder("int32", [None, MAX_TOKENS, 1])
y = tf.placeholder("float", [None, N_CLASSES]) # 0.0 / 1.0
...
seqlen = tf.placeholder(tf.int32, [None]) #list of each sequence length*
embedding = tf.Variable(tf.random_uniform([VOCAB_SIZE, HIDDEN_SIZE], -1, 1))
inputs = tf.nn.embedding_lookup(embedding, x) #x is the text after converting to indices
inputs = tf.unstack(inputs, MAX_POST_LENGTH, 1)
outputs, states = tf.contrib.rnn.static_rnn(lstm_cell, inputs, dtype=tf.float32, sequence_length=seqlen) #---> Produces error

*seqlen:我对序列进行了零填充,因此它们都具有相同的列表大小,但由于实际大小不同,我准备了一个列表来描述没有填充的长度。

新错误:

ValueError: 层 basic_lstm_cell_1 的输入 0 与 层:预期 ndim=2,发现 ndim=3。收到的完整形状:[无, 1, 64]

64 是每个隐藏层的大小。

很明显,我的尺寸有问题...嵌入后如何使输入适合网络?

【问题讨论】:

    标签: python tensorflow rnn word-embedding


    【解决方案1】:

    tf.nn.static_rnn ,我们可以看到 inputs 参数是:

    一个长度为 T 的输入列表,每个输入都是一个形状为 [batch_size, input_size] 的张量

    所以你的代码应该是这样的:

    x = tf.placeholder("int32", [None, MAX_TOKENS])
    ...
    inputs = tf.unstack(inputs, axis=1)
    

    【讨论】:

    • 这解决了我的ValueError 问题(+ 相应地调整输入 - 将批次从 (BATCH_SIZE, MAX_TOKENS, 1) 重塑为 (BATCH_SIZE, MAX_TOKENS) 以匹配新的x 形状)。然而,这并没有解决学习问题。图表现在看起来像:this。我想你应该得到赏金(和 +1)。
    • 能否分享整个代码,让我在本地试用一下?
    • 与其为单词分配唯一值,不如从gloveword2vec 分配一个预置的嵌入向量,而不是训练它们。
    • 我也试过这样做(手套)。这些图表看起来与我最初发布的相似。所以我肯定做错了什么。这是代码(使用手套):link
    • 从代码中,我看到您使用np.linalg.norm 将glove 的嵌入向量更改为单个值,单个值可能不足以表示单词。我建议您更改此设置。
    【解决方案2】:

    tf.squeeze 是一种从张量中删除大小为 1 的维度的方法。如果最终目标是将输入形状设为 [None,64],则输入类似于 inputs = tf.squeeze(inputs) 的行,这样可以解决您的问题。

    【讨论】:

    • 它没有用。我把它放在哪里?如果我把它放在调用static_rnn 之前,我会收到另一个错误:Inputs must be a sequence
    • 尝试将输入括在括号中以生成tf.contrib.rnn.static_rnn(lstm_cell, [inputs], dtype=tf.float32, sequence_length=seqlen)
    • 我写过我试过了。请阅读全文
    • 你是用tf.sqeeze做的吗?你能在 rnn 调用之前打印输入的 dtype 和 shape 吗?
    • 是的,正如我在第一条评论中所说,我用squeeze 尝试了您的建议,但没有奏效。类型为tensorflow.python.framework.ops.Tensor 对应xsqlenkeep_prob,以及dict 对应weightsbiases