¿Mezclar capas de avance y capas recurrentes en Tensorflow?

¿Alguien ha podido mezclar capas avanzadas y capas recurrentes en Tensorflow?

Por ejemplo: entrada-> conv-> GRU-> lineal-> salida

Puedo imaginar que uno puede definir su propia celda con capas de avance y sin estado que luego se pueden astackr usando la función MultiRNNCell, algo así como:

cell = tf.nn.rnn_cell.MultiRNNCell ([conv_cell, GRU_cell, linear_cell])

Esto haría la vida mucho más fácil …

no puedes hacer lo siguiente:

rnnouts, _ = rnn(grucell, inputs) linearout = [tf.matmul(rnnout, weights) + bias for rnnout in rnnouts] 

etc.

Este tutorial da un ejemplo de cómo usar capas convolucionales junto con las recurrentes. Por ejemplo, teniendo las últimas capas de convolución como esta:

 ... l_conv4_a = conv_pre(l_pool3, 16, (5, 5), scope="l_conv4_a") l_pool4 = pool(l_conv3_a, scope="l_pool4") l_flatten = flatten(l_pool4, scope="flatten") 

y habiendo definido la célula RNN:

 _, shape_state = tf.nn.dynamic_rnn(cell=shape_cell, inputs=tf.expand_dims(batch_norm(x_shape_pl), 2), dtype=tf.float32, scope="shape_rnn") 

Puede concatenar ambas salidas y usarlas como entrada para la siguiente capa:

 features = tf.concat(concat_dim=1, values=[x_margin_pl, shape_state, x_texture_pl, l_flatten], name="features") 

O simplemente puede usar la salida de la capa CNN como entrada a la celda RNN:

 _, shape_state = tf.nn.dynamic_rnn(cell=shape_cell, inputs=l_flatten, dtype=tf.float32, scope="shape_rnn") 

Esto es lo que tengo hasta ahora; mejoras bienvenidas:

 class LayerCell(rnn_cell_impl.RNNCell): def __init__(self, tf_layer, **kwargs): ''' :param tf_layer: a tensorflow layer, eg tf.layers.Conv2D or tf.keras.layers.Conv2D. NOT tf.layers.conv2d ! Can pass all other layer params as well, just need to give the parameter name: paramname=param''' self.layer_fn = tf_layer(**kwargs) def __call__(self, inputs, state, scope=None): ''' Every `RNNCell` must implement `call` with the signature `(output, next_state) = call(input, state)`. The optional third input argument, `scope`, is allowed for backwards compatibility purposes; but should be left off for new subclasses.''' return (self.layer_fn(inputs), state) def __str__(self): return "Cell wrapper of " + str(self.layer_fn) def __getattr__(self, attr): '''credits to https://stackoverflow.com/questions/1382871/dynamically-attaching-a-method-to-an-existing-python-object-generated-with-swig/1383646#1383646''' return getattr(self.layer_fn, attr) @property def state_size(self): """size(s) of state(s) used by this cell. It can be represented by an Integer, a TensorShape or a tuple of Integers or TensorShapes. """ return (0,) @property def output_size(self): """Integer or TensorShape: size of outputs produced by this cell.""" # use with caution; could be uninitialized return self.layer_fn.output_shape 

(Naturalmente, no use con capas recurrentes porque se destruirá el mantenimiento del estado).

Parece funcionar con: tf.layers.Conv2D, tf.keras.layers.Conv2D, tf.keras.layers.Activation, tf.layers.BatchNormalization

NO funciona con: tf.keras.layers.BatchNormalization. Al menos me falló cuando lo uso en un bucle tf.while; quejándose de la combinación de variables de diferentes marcos, similar a aquí . Tal vez keras usa tf.Variable () en lugar de tf.get_variable () …?


Uso:

 cell0 = tf.contrib.rnn.ConvLSTMCell(conv_ndims=2, input_shape=[40, 40, 3], output_channels=16, kernel_shape=[5, 5]) cell1 = LayerCell(tf.keras.layers.Conv2D, filters=8, kernel_size=[5, 5], strides=(1, 1), padding='same') cell2 = LayerCell(tf.layers.BatchNormalization, axis=-1) inputs = np.random.rand(10, 40, 40, 3).astype(np.float32) multicell = tf.contrib.rnn.MultiRNNCell([cell0, cell1, cell2]) state = multicell.zero_state(batch_size=10, dtype=tf.float32) output = multicell(inputs, state)