Tensorflow与Keras中的RNN,tf.nn.动态折旧

Tensorflow与Keras中的RNN,tf.nn.动态折旧,tensorflow,keras,tf.keras,Tensorflow,Keras,Tf.keras,我的问题是:和keras.layers.RNN(cell)是否与文档中所述的完全相同 我正在计划建立一个RNN,然而,它似乎是有利于Keras的 它特别指出: 警告:此函数已弃用。它将在将来被移除 版本更新说明:请使用keras.layers.RNN(单元格), 这相当于这个API 但我不知道在可变序列长度的情况下,API是如何等价的 在原始TF中,我们可以指定形状(批次大小,序列长度)的张量。这样,如果我们的序列是[0,1,2,3,4],并且批次中最长的序列的大小是10,我们可以用0和[0,1

我的问题是:和
keras.layers.RNN(cell)
是否与文档中所述的完全相同

我正在计划建立一个RNN,然而,它似乎是有利于Keras的

它特别指出:

警告:此函数已弃用。它将在将来被移除 版本更新说明:请使用keras.layers.RNN(单元格), 这相当于这个API

但我不知道在可变序列长度的情况下,API是如何等价的

在原始TF中,我们可以指定形状
(批次大小,序列长度)
的张量。这样,如果我们的序列是
[0,1,2,3,4]
,并且批次中最长的序列的大小是10,我们可以用0和
[0,1,2,3,4,0,0,0,0,0,0,0,0]
,我们可以说
seq_length=5
来处理
[0,1,2,3,4]

然而,在Keras中,这不是它的工作方式!我们可以做的是,在以前的层(例如嵌入层)中指定
mask_zero=True
。这也将掩盖第一个零

我可以通过向整个向量中添加一个来解决这个问题,但是在使用
tft.compute\u词汇()
进行处理之后,我需要做额外的预处理,它将词汇映射到0索引向量。

不,但是它们(或者可以被设置成)也没有太大的不同

TL;博士
tf.nn.dynamic\u rnn
将序列结束后的元素替换为0。据我所知,这不能用
tf.keras.layers.*
复制,但你可以用
RNN(掩蔽(…)
方法得到类似的行为:它只是停止计算并向前携带最后的输出和状态。你将得到与
tf.nn.dynamic\u RNN
相同的(非填充)输出

实验 下面是一个简单的工作示例,演示了使用层和不使用层之间的差异

将numpy导入为np
导入tensorflow作为tf
测试_输入=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_权重=(np.one(shape=(3,2))*np.transpose([[0.37,1,2]]).astype(np.float32)
emb=tf.keras.layers.emb(
*emb_.shape,
权重=[emb_权重],
可训练=错误
)
掩模=tf.keras.layers.Masking(掩模_值=0.37)
rnn=tf.keras.layers.GRU(
1.
return_sequences=True,
激活=无,
反复激活=无,
kernel_初始值设定项='ones',
循环_初始值设定项='零',
使用_bias=True,
偏差\u初始值设定项='one'
)
def old_rnn(输入):
rnn_输出,rnn_状态=tf.nn.dynamic_(
rnn.cell,
投入,
dtype=tf.32,
序列长度=序列长度
)
返回rnn_输出
x=tf.keras.layers.Input(shape=test\u Input.shape[1:])
m0=tf.keras.Model(输入=x,输出=emb(x))
m1=tf.keras.Model(输入=x,输出=rnn(emb(x)))
m2=tf.keras.Model(输入=x,输出=rnn(掩码(emb(x)))
打印(m0.predict(test\u input.squence())
打印(m1.predict(test_输入).squence())
打印(m2.predict(test_input.squence())
sess=tf.keras.backend.get_session()
打印(sess.run(old_rnn(mask(emb(x))),feed_dict={x:test_input}).square())
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
旧体系结构的实际输出:

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\u rnn
    用于用零屏蔽填充元素
  • 没有屏蔽的新RNN层在填充元素上运行,就像它们是数据一样
  • 新的
    rnn(mask(…)
    方法只是停止计算并向前携带最后的输出和状态。请注意,我为此方法获得的(非填充)输出与
    tf.nn.dynamic\u rnn
    中的输出完全相同

无论如何,我不能涵盖所有可能的边缘情况,但我希望您可以使用此脚本进一步解决问题。

您是在谈论
keras
还是
tf.keras
?我想问,您是否应该真正关心这一点(即前面的
seq_长度
).从文档中…所以这更多是为了性能而不是正确性。@MPękalski我使用tf。keras@rst我实际上不理解正确性的问题。如果我输入0,矩阵乘法也将是0,但随后我将添加一个1偏差,该偏差通过一个激活函数与其权重一起传递。我很可能会得到一个非零输出由于偏差项。因此偏差权重将继续训练?或者我的理解不正确吗?@rst假设它们的意思是将剩余的“填充”0传递到RNN或屏蔽它们之间没有区别,例如不在它们上训练。我对此进行了扩展,以显示没有嵌入层的屏蔽。回答很好,这对我帮助很大。今晚我有了一个有趣的发现——如果将GRU单元封装在一个双向层中,它会将携带的输出转换为零,从而获得与旧实现相同的输出,而无需预定义序列长度。