philipperemy / cond_rnn

Conditional RNNs for Tensorflow / Keras.
MIT License
225 stars 32 forks source link

ConditionalRNN or ConditionalRecurrent? #34

Closed Simo-JDev closed 2 years ago

Simo-JDev commented 2 years ago

I am trying to import the function but it is now called ConditionalRNN, but there is no mention of this on the README. If I only change that in the example code, I get an error:

from tensorflow.keras import Input
from tensorflow.keras.layers import LSTM

from cond_rnn import ConditionalRNN

time_steps, input_dim, output_dim, batch_size, cond_size = 128, 6, 12, 32, 5
inputs = Input(batch_input_shape=(batch_size, time_steps, input_dim))
cond_inputs = Input(batch_input_shape=(batch_size, cond_size))

outputs = ConditionalRNN(LSTM(units=output_dim))([inputs, cond_inputs])
print(outputs.shape)  # (batch_size, output_dim)

TypeError: '<' not supported between instances of 'LSTM' and 'int'

Any idea on what is going on?

philipperemy commented 2 years ago

@JustDonuts ConditionalRecurrent! It acts as a wrapper and not a layer anymore.

Try to upgrade to the latest version (2.4):

pip install cond_rnn --upgrade

This code works fine for me:

from tensorflow.keras import Input
from tensorflow.keras.layers import LSTM

from cond_rnn import ConditionalRecurrent

time_steps, input_dim, output_dim, batch_size, cond_size = 128, 6, 12, 32, 5
inputs = Input(batch_input_shape=(batch_size, time_steps, input_dim))
cond_inputs = Input(batch_input_shape=(batch_size, cond_size))

outputs = ConditionalRecurrent(LSTM(units=output_dim))([inputs, cond_inputs])
print(outputs.shape)  # (batch_size, output_dim)
Simo-JDev commented 2 years ago

Thanks, I have tried it but it still says that ConditionalRecurrent cannot be imported. When I check the cond_rnn.py file, there is no ConditionalRecurrent defined, only ConditionalRNN, which does not work with the code example given.

Simo-JDev commented 2 years ago

If it can be of any help, the code I have in the cond_rnn.py file in my environment is this:

import tensorflow as tf

def _get_tensor_shape(t):
    return t.get_shape().as_list()

class ConditionalRNN:

    def __init__(self,
                 units,
                 cell=tf.keras.layers.LSTMCell,
                 cond=None,
                 *args, **kwargs):  # Arguments to the RNN like return_sequences, return_state...
        """
        Conditional RNN. Conditions time series on categorical data.
        :param units: int, The number of units in the RNN Cell
        :param cell: string, cell class or object (pre-instantiated). In the case of string, 'GRU',
        'LSTM' and 'RNN' are supported.
        :param cond (optional): Tensor or list of tensors with shape [batch_size, cond_dim].
        In the case of a list, the tensors can have a different cond_dim.
        :param args: Any parameters of the tf.keras.layers.RNN class, such as return_sequences,
        return_state, stateful, unroll...
        """
        self.final_states = None
        self.init_state = None
        if isinstance(cell, str):
            if cell.upper() == 'GRU':
                cell = tf.keras.layers.GRUCell
            elif cell.upper() == 'LSTM':
                cell = tf.keras.layers.LSTMCell
            elif cell.upper() == 'RNN':
                cell = tf.keras.layers.SimpleRNNCell
            else:
                raise Exception('Only GRU, LSTM and RNN are supported as cells.')
        self._cell = cell if hasattr(cell, 'units') else cell(units=units)
        if isinstance(cond, list):  # multiple conditions.
            cond = [self._standardize_condition(cond) for cond in cond]
            init_state_list = []
            for cond in cond:
                init_state_list.append(tf.keras.layers.Dense(units=units)(cond))
            multi_cond_projector = tf.layers.Dense(1, activation=None, use_bias=True)
            multi_cond_state = multi_cond_projector(tf.stack(init_state_list, axis=-1))
            multi_cond_state = tf.squeeze(multi_cond_state, axis=-1)
            self.init_state = tf.unstack(multi_cond_state, axis=0)
        else:
            cond = self._standardize_condition(cond)
            if cond is not None:
                self.init_state = tf.keras.layers.Dense(units=units)(cond)
                self.init_state = tf.unstack(self.init_state, axis=0)
        self.rnn = tf.keras.layers.RNN(cell=self._cell, *args, **kwargs)

    def _standardize_condition(self, initial_cond):
        initial_cond_shape = _get_tensor_shape(initial_cond)
        if len(initial_cond_shape) == 2:
            initial_cond = tf.expand_dims(initial_cond, axis=0)
        first_cond_dim = _get_tensor_shape(initial_cond)[0]
        if isinstance(self._cell, tf.keras.layers.LSTMCell):
            if first_cond_dim == 1:
                initial_cond = tf.tile(initial_cond, [2, 1, 1])
            elif first_cond_dim != 2:
                raise Exception('Initial cond should have shape: [2, batch_size, hidden_size]\n'
                                'or [batch_size, hidden_size]. Shapes do not match.', initial_cond_shape)
        elif isinstance(self._cell, tf.keras.layers.GRUCell) or isinstance(self._cell, tf.keras.layers.SimpleRNNCell):
            if first_cond_dim != 1:
                raise Exception('Initial cond should have shape: [1, batch_size, hidden_size]\n'
                                'or [batch_size, hidden_size]. Shapes do not match.', initial_cond_shape)
        else:
            raise Exception('Only GRU, LSTM and RNN are supported as cells.')
        return initial_cond

    def __call__(self, inputs, *args, **kwargs):
        """
        :param inputs: 3-D Tensor with shape [batch_size, time_steps, input_dim].
        :return: outputs, states or outputs (if return_state=False)
        """
        out = self.rnn(inputs, initial_state=self.init_state, *args, **kwargs)
        if self.rnn.return_state:
            outputs, h, c = out
            final_states = tf.stack([h, c])
            return outputs, final_states
        else:
            return out
philipperemy commented 2 years ago

Ohh I see what's happening. Are you running on Mac? pip is trying to find a version compatible with tensorflow>=2.3 but now it's called tensorflow-macos so it's fetching an older version. I'll fix that.

philipperemy commented 2 years ago

Run this and try again:

pip install cond_rnn --upgrade

I uploaded the version 3.1.1.

philipperemy commented 2 years ago

Or run that, it should work as well:

pip install --no-binary cond_rnn cond_rnn
Simo-JDev commented 2 years ago

Thanks so much, it's working now.

philipperemy commented 2 years ago

@JustDonuts you're welcome!