Wednesday, 7 August 2019

How to create Keras model with optional inputs

I'm looking for a way to create a Keras model with optional inputs. In raw TensorFlow, you can create placeholders with optional inputs as follows:

import numpy as np
import tensorflow as tf


def main():
    required_input = tf.placeholder(
        tf.float32,
        shape=(None, 2),
        name='required_input')

    default_optional_input = tf.random_uniform(
        shape=(tf.shape(required_input)[0], 3))
    optional_input = tf.placeholder_with_default(
        default_optional_input,
        shape=(None, 3),
        name='optional_input')

    output = tf.concat((required_input, optional_input), axis=-1)

    with tf.Session() as session:
        with_optional_input_output_np = session.run(output, feed_dict={
            required_input: np.random.uniform(size=(4, 2)),
            optional_input: np.random.uniform(size=(4, 3)),
        })

        print(f"with optional input: {with_optional_input_output_np}")

        without_optional_input_output_np = session.run(output, feed_dict={
            required_input: np.random.uniform(size=(4, 2)),
        })

        print(f"without optional input: {without_optional_input_output_np}")


if __name__ == '__main__':
    main()

In a similar fashion, I would want to be able to have optional inputs for my Keras model. It seems like the tensor argument in the keras.layers.Input.__init__ might be what I'm looking for, but at least it doesn't work as I was expecting (i.e. the same way as tf.placeholder_with_default shown above). Here's an example that breaks:

import numpy as np
import tensorflow as tf
import tensorflow_probability as tfp


def create_model(output_size):
    required_input = tf.keras.layers.Input(
        shape=(13, ), dtype='float32', name='required_input')

    batch_size = tf.shape(required_input)[0]

    def sample_optional_input(inputs, batch_size=None):
        base_distribution = tfp.distributions.MultivariateNormalDiag(
            loc=tf.zeros(output_size),
            scale_diag=tf.ones(output_size),
            name='sample_optional_input')

        return base_distribution.sample(batch_size)

    default_optional_input = tf.keras.layers.Lambda(
        sample_optional_input,
        arguments={'batch_size': batch_size}
    )(None)

    optional_input = tf.keras.layers.Input(
        shape=(output_size, ),
        dtype='float32',
        name='optional_input',
        tensor=default_optional_input)

    concat = tf.keras.layers.Concatenate(axis=-1)(
        [required_input, optional_input])
    dense = tf.keras.layers.Dense(
        output_size, activation='relu')(concat)

    model = tf.keras.Model(
        inputs=[required_input, optional_input],
        outputs=[dense])

    return model


def main():
    model = create_model(output_size=3)

    required_input_np = np.random.normal(size=(4, 13))
    outputs_np = model.predict({'required_input': required_input_np})
    print(f"outputs_np: {outputs_np}")

    required_input = tf.random_normal(shape=(4, 13))
    outputs = model({'required_input': required_input})
    print(f"outputs: {outputs}")


if __name__ == '__main__':
    main()

The first call to the model.predict seems to give correct output, but for some reason, the direct call to model fails with the following error:

ValueError: Layer model expects 2 inputs, but it received 1 input tensors. Inputs received: []

Can the tensor argument in Input.__init__ be used to implement optional inputs for Keras model as in my example above? If yes, what should I change in my example to make it run correctly? If not, what is the expected way of creating optional inputs in Keras?



from How to create Keras model with optional inputs

No comments:

Post a Comment