我最近遇到了一个类似的场景,我想链接循环层和非循环层。
我是否在我的 LSTM 层之前使用这个简单的层并将两者都传递给
tf.nn.dynamic_rnn() 操作...
这行不通。函数dynamic_rnn 需要一个单元格作为它的第一个参数。单元格是从tf.nn.rnn_cell.RNNCell 继承的类。此外,dynamic_rnn 的第二个输入参数应该是至少具有 3 个维度的张量,其中前两个维度是批处理和时间 (time_major=False) 或时间和批处理 (time_major=True)。
我是否使用函数 tf.map_fn() 两次(一次用于解包批次,一次用于解包序列),如果理解得当,可以解包我的序列并在每个特征行上应用一层。
这可能有效,但在我看来不是一个有效且干净的解决方案。首先,没有必要“解包”,因为您可能希望对成批的特征和时间步执行一些操作,其中批次中的每个观察都独立于其他观察。
我对这个特定问题的解决方案是创建一个tf.nn.rnn_cell.RNNCell 的子类。在我的例子中,我想要一个简单的前馈层,它可以迭代所有的时间步,并且可以在dynamic_rnn 中使用:
import tensorflow as tf
class FeedforwardCell(tf.nn.rnn_cell.RNNCell):
"""A stateless feedforward cell that can be used with MultiRNNCell
"""
def __init__(self, num_units, activation=tf.tanh, dtype=tf.float32):
self._num_units = num_units
self._activation = activation
# Store a dummy state to make dynamic_rnn happy.
self.dummy = tf.constant([[0.0]], dtype=dtype)
@property
def state_size(self):
return 1
@property
def output_size(self):
return self._num_units
def zero_state(self, batch_size, dtype):
return self.dummy
def __call__(self, inputs, state, scope=None):
"""Basic feedforward: output = activation(W * input)."""
with tf.variable_scope(scope or type(self).__name__): # "FeedforwardCell"
output = self._activation(tf.nn.rnn_cell._linear(
[inputs], self._num_units, True))
return output, self.dummy
这个类的一个实例可以在一个带有“普通”RNN 单元的列表中传递给tf.nn.rnn_cell.MultiRNNCell 初始化器。生成的对象实例可以作为cell 输入参数传递给dynamic_rnn。
需要注意的重要一点:dynamic_rnn 期望循环单元在被调用时返回一个状态。因此,我在FeedforwardCell 中使用dummy 作为假状态变量。
我的解决方案可能不是将循环层和非循环层链接在一起的最顺畅或最佳方式。我很想听听其他 Tensorflow 用户的建议。
编辑
如果您选择使用dynamic_rnn 的sequence_length 输入参数,那么state_size 应该是self._num_units 并且dummy 状态应该具有[batch_size, self.state_size] 的形状。换句话说,状态不能是标量。注意bidirectional_dynamic_rnn 要求sequence_length 参数不是None,而dynamic_rnn 没有这个要求。 (这在 TF 文档中的记录很薄弱。)