Tensorflow 与 Keras 中的 RNN,tf.nn.dynamic_rnn() 的贬值
Posted
技术标签:
【中文标题】Tensorflow 与 Keras 中的 RNN,tf.nn.dynamic_rnn() 的贬值【英文标题】:RNN in Tensorflow vs Keras, depreciation of tf.nn.dynamic_rnn() 【发布时间】:2019-07-26 03:09:22 【问题描述】:我的问题是:tf.nn.dynamic_rnn
和 keras.layers.RNN(cell)
真的与文档中所述相同吗?
我正计划构建一个 RNN,但是,tf.nn.dynamic_rnn
似乎被贬低为支持 Keras。
特别指出:
警告:此功能已弃用。将来会被移除 版本。更新说明:请使用 keras.layers.RNN(cell), 相当于这个API
但在可变序列长度的情况下,我看不出 API 是如何等效的!
在原始 TF 中,我们可以指定一个形状为 (batch_size, seq_lengths)
的张量。这样,如果我们的序列是[0, 1, 2, 3, 4]
,并且batch中最长的序列大小为10,我们可以用0和[0, 1, 2, 3, 4, 0, 0, 0, 0, 0]
填充它,我们可以说seq_length=5
来处理[0, 1, 2, 3, 4]
。
但是,在 Keras 中,这不是它的工作方式!我们可以做的是在前面的层中指定mask_zero=True
,例如嵌入层。这也将掩盖第一个零!
我可以通过在整个向量中添加一个来绕过它,但那是我在使用 tft.compute_vocabulary()
处理后需要做的额外预处理,它将词汇表单词映射到 0 索引向量。
【问题讨论】:
你说的是keras
还是tf.keras
?
我质疑,您是否应该真正关心这一点(即之前的seq_lengths
)。来自文档 ...因此,性能比正确性更重要。
@MPękalski 我使用 tf.keras
@rst 我实际上并不了解关于正确性的问题。如果我输入 0,矩阵乘法也将为 0,但随后我将添加一个 1 偏置,该偏置通过具有其权重的激活函数传递。由于偏置项,我很可能会得到非零输出。因此,偏差权重会继续训练吗?还是我的理解有误?
@rst 假设它们的意思是将剩余的“填充”0 传递到 RNN 或屏蔽它们之间没有区别,例如没有对他们进行培训。
【参考方案1】:
不,但它们(或可以被制成)也没有那么不同。
TL;DR
tf.nn.dynamic_rnn
将序列结束后的元素替换为 0。据我所知,这不能用tf.keras.layers.*
复制,但你可以用RNN(Masking(...)
方法获得类似的行为:它只是停止计算并携带最后的输出和状态。您将获得与从 tf.nn.dynamic_rnn
获得的相同(非填充)输出。
实验
这是一个最小的工作示例,展示了 tf.nn.dynamic_rnn
和 tf.keras.layers.GRU
在使用和不使用 tf.keras.layers.Masking
层时的区别。
import numpy as np
import tensorflow as tf
test_input = np.array([
[1, 2, 1, 0, 0],
[0, 1, 2, 1, 0]
], dtype=int)
seq_length = tf.constant(np.array([3, 4], dtype=int))
emb_weights = (np.ones(shape=(3, 2)) * np.transpose([[0.37, 1, 2]])).astype(np.float32)
emb = tf.keras.layers.Embedding(
*emb_weights.shape,
weights=[emb_weights],
trainable=False
)
mask = tf.keras.layers.Masking(mask_value=0.37)
rnn = tf.keras.layers.GRU(
1,
return_sequences=True,
activation=None,
recurrent_activation=None,
kernel_initializer='ones',
recurrent_initializer='zeros',
use_bias=True,
bias_initializer='ones'
)
def old_rnn(inputs):
rnn_outputs, rnn_states = tf.nn.dynamic_rnn(
rnn.cell,
inputs,
dtype=tf.float32,
sequence_length=seq_length
)
return rnn_outputs
x = tf.keras.layers.Input(shape=test_input.shape[1:])
m0 = tf.keras.Model(inputs=x, outputs=emb(x))
m1 = tf.keras.Model(inputs=x, outputs=rnn(emb(x)))
m2 = tf.keras.Model(inputs=x, outputs=rnn(mask(emb(x))))
print(m0.predict(test_input).squeeze())
print(m1.predict(test_input).squeeze())
print(m2.predict(test_input).squeeze())
sess = tf.keras.backend.get_session()
print(sess.run(old_rnn(mask(emb(x))), feed_dict=x: test_input).squeeze())
m0
的输出用于显示应用嵌入层的结果。
请注意,根本没有零条目:
[[[1. 1. ] [[0.37 0.37]
[2. 2. ] [1. 1. ]
[1. 1. ] [2. 2. ]
[0.37 0.37] [1. 1. ]
[0.37 0.37]] [0.37 0.37]]]
下面是 m1
、m2
和 old_rnn
架构的实际输出:
m1: [[ -6. -50. -156. -272.7276 -475.83362]
[ -1.2876 -9.862801 -69.314 -213.94202 -373.54672 ]]
m2: [[ -6. -50. -156. -156. -156.]
[ 0. -6. -50. -156. -156.]]
old [[ -6. -50. -156. 0. 0.]
[ 0. -6. -50. -156. 0.]]
总结
旧的tf.nn.dynamic_rnn
用于用零屏蔽填充元素。
新的 RNN 层没有屏蔽在填充元素上运行,就像它们是数据一样。
新的rnn(mask(...))
方法只是停止计算并将最后的输出和状态向前传递。请注意,我为此方法获得的(非填充)输出与 tf.nn.dynamic_rnn
的输出完全相同。
无论如何,我无法涵盖所有可能的边缘情况,但我希望您可以使用此脚本进一步解决问题。
【讨论】:
我扩展了这个in this answer 以显示没有嵌入层的掩码。很好的答案,这对我帮助很大。 今晚我做了一个有趣的发现——如果你将 GRU 单元包裹在一个双向层中,它会将携带的输出转换为零,因此无需预定义序列即可获得与旧实现相同的输出长度。以上是关于Tensorflow 与 Keras 中的 RNN,tf.nn.dynamic_rnn() 的贬值的主要内容,如果未能解决你的问题,请参考以下文章
使用Tensorflow后端的Keras LSTM RNN中令人费解的训练损失与纪元...行为的任何原因
Tensorflow.keras:RNN 对 Mnist 进行分类
使用 Keras、Tensorflow 进行具有多个时间序列维度的 RNN 时间序列预测
运行 model.fit() 时的 tf.keras (RNN) 层问题