Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

keras.ops.map can't handle nested structures for TensorFlow backend #20048

Open
apage224 opened this issue Jul 26, 2024 · 1 comment · May be fixed by #20064
Open

keras.ops.map can't handle nested structures for TensorFlow backend #20048

apage224 opened this issue Jul 26, 2024 · 1 comment · May be fixed by #20064

Comments

@apage224
Copy link

Keras: 3.4.1
TensorFlow: 2.17.0

As background, I am looking to leverage both keras.ops.map as well as keras.ops.vectorize_map for custom preprocessing layers. Certain layers require sequential mapping hence I use keras.ops.map. If I pass a nested input, keras.ops.map will fail when using TensorFlow backend.

I believe this line is an issue as it assumes the input is not nested:

def map(f, xs):
    xs = tree.map_structure(convert_to_tensor, xs)

    def get_fn_output_signature(x):
        out = f(x)
        return tree.map_structure(tf.TensorSpec.from_tensor, out)

    fn_output_signature = get_fn_output_signature(xs[0])
    return tf.map_fn(f, xs, fn_output_signature=fn_output_signature)

From what I can tell, it is trying to determine the output signature (which might not match the input) by feeding the function a single element (e.g. xs[0]) which won't work on nested inputs. I was able to fix it by updating the function as follows (note: I've done only limited testing).

def map(f, xs):
    xs = tree.map_structure(convert_to_tensor, xs)

    def get_fn_output_signature(x):
        out = f(x)
        return tree.map_structure(tf.TensorSpec.from_tensor, out)
    # Grab single element unpacking and repacking single element        
    x = tf.nest.pack_sequence_as(xs, [x[0] for x in tf.nest.flatten(xs)])
    fn_output_signature = get_fn_output_signature(x)  
    return tf.map_fn(f, xs, fn_output_signature=fn_output_signature)

Test case:

import os
os.environ["KERAS_BACKEND"] = "tensorflow"
import keras
import tensorflow as tf

def my_fn(inputs):    
    outputs = dict(inputs)
    outputs['x'] = inputs['x'][:, 0]
    outputs['y'] = inputs['y']   1
    return outputs

xs = {
    'x': tf.convert_to_tensor(np.random.rand(4, 100, 3), dtype=tf.float32),
    'y': tf.convert_to_tensor(np.random.randint(0, 10, size=(4, 1)), dtype=tf.int32)
}

Calling keras.ops.map:

ys = keras.ops.map(my_fn, xs)

produces error:

    225     out = f(x)
    226     return tree.map_structure(tf.TensorSpec.from_tensor, out)
--> 228 fn_output_signature = get_fn_output_signature(xs[0])
    229 return tf.map_fn(f, xs, fn_output_signature=fn_output_signature)

KeyError: 0"

Calling custom map:

ys = map(my_fn, xs)
print(ys['x'].shape)

produces correct result:

    (4, 100)
@mehtamansi29
Copy link
Collaborator

mehtamansi29 commented Jul 30, 2024

Hi @apage224 -

Thanks for raising this issue. We have raised an internal fix for this issue. Soon it will be reflected in the documentation as well. This fix is working fine for nested structure(i.e dict,list,tuple).

Example:

import os
os.environ["KERAS_BACKEND"] = "tensorflow"
import keras
import tensorflow as tf
import numpy as np

def my_fn(inputs): 
    outputs = [x**2 for x in inputs]
    return outputs

xs = [tf.convert_to_tensor(np.random.rand(4, 100, 3), dtype=tf.float32),tf.convert_to_tensor(np.random.randint(0, 10, size=(4, 1)), dtype=tf.int32)]

ys = keras.ops.map(my_fn, xs)
print(ys)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants