I'm trying to get a preprocessing function to work with Dataset map, but I get the following error (full stack trace at the bottom):
ValueError: Tensor-typed variable initializers must either be wrapped in an init_scope or callable (e.g., `tf.Variable(lambda : tf.truncated_normal([10, 40]))`) when building functions. Please file a feature request if this restriction inconveniences you.
Below is a full snippet that reproduces the issue. My question is, why in one use case (crop only) it works, and when RandomFlip is used it doesn't? How can this be fixed?
import functools
import numpy as np
import tensorflow as tf
def data_gen():
for i in range(10):
x = np.random.random(size=(80, 80, 3)) * 255 # rgb image
x = x.astype('uint8')
y = np.random.random(size=(40, 40, 1)) * 255 # downsized mono image
y = y.astype('uint8')
yield x, y
def preprocess(image, label, cropped_image_size, cropped_label_size, skip_augmentations=False):
x = image
y = label
x_size = cropped_image_size
y_size = cropped_label_size
if not skip_augmentations:
x = tf.keras.layers.RandomFlip(mode="horizontal")(x)
y = tf.keras.layers.RandomFlip(mode="horizontal")(y)
x = tf.keras.layers.RandomRotation(factor=1.0, fill_mode='constant')(x)
y = tf.keras.layers.RandomRotation(factor=1.0, fill_mode='constant')(y)
x = tf.keras.layers.CenterCrop(x_size, x_size)(x)
y = tf.keras.layers.CenterCrop(y_size, y_size)(y)
return x, y
print(tf.__version__) # 2.6.0
dataset = tf.data.Dataset.from_generator(data_gen, output_signature=(
tf.TensorSpec(shape=(80, 80, 3), dtype='uint8'),
tf.TensorSpec(shape=(40, 40, 1), dtype='uint8')
))
crop_only_fn = functools.partial(preprocess, cropped_image_size=50, cropped_label_size=25, skip_augmentations=True)
train_preprocess_fn = functools.partial(preprocess, cropped_image_size=50, cropped_label_size=25, skip_augmentations=False)
# This works
crop_dataset = dataset.map(crop_only_fn)
# This fails: ValueError: Tensor-typed variable initializers must either be wrapped in an init_scope or callable
train_dataset = dataset.map(train_preprocess_fn)
Full stack trace:
Traceback (most recent call last):
File "./issue_dataaug.py", line 50, in <module>
train_dataset = dataset.map(train_preprocess_fn)
File "/...//virtualenvs/cvi36/lib/python3.6/site-packages/tensorflow/python/data/ops/dataset_ops.py", line 1861, in map
return MapDataset(self, map_func, preserve_cardinality=True)
File "/...//virtualenvs/cvi36/lib/python3.6/site-packages/tensorflow/python/data/ops/dataset_ops.py", line 4985, in __init__
use_legacy_function=use_legacy_function)
File "/...//virtualenvs/cvi36/lib/python3.6/site-packages/tensorflow/python/data/ops/dataset_ops.py", line 4218, in __init__
self._function = fn_factory()
File "/...//virtualenvs/cvi36/lib/python3.6/site-packages/tensorflow/python/eager/function.py", line 3151, in get_concrete_function
*args, **kwargs)
File "/...//virtualenvs/cvi36/lib/python3.6/site-packages/tensorflow/python/eager/function.py", line 3116, in _get_concrete_function_garbage_collected
graph_function, _ = self._maybe_define_function(args, kwargs)
File "/...//virtualenvs/cvi36/lib/python3.6/site-packages/tensorflow/python/eager/function.py", line 3463, in _maybe_define_function
graph_function = self._create_graph_function(args, kwargs)
File "/...//virtualenvs/cvi36/lib/python3.6/site-packages/tensorflow/python/eager/function.py", line 3308, in _create_graph_function
capture_by_value=self._capture_by_value),
File "/...//virtualenvs/cvi36/lib/python3.6/site-packages/tensorflow/python/framework/func_graph.py", line 1007, in func_graph_from_py_func
func_outputs = python_func(*func_args, **func_kwargs)
File "/...//virtualenvs/cvi36/lib/python3.6/site-packages/tensorflow/python/data/ops/dataset_ops.py", line 4195, in wrapped_fn
ret = wrapper_helper(*args)
File "/...//virtualenvs/cvi36/lib/python3.6/site-packages/tensorflow/python/data/ops/dataset_ops.py", line 4125, in wrapper_helper
ret = autograph.tf_convert(self._func, ag_ctx)(*nested_args)
File "/...//virtualenvs/cvi36/lib/python3.6/site-packages/tensorflow/python/autograph/impl/api.py", line 695, in wrapper
raise e.ag_error_metadata.to_exception(e)
ValueError: in user code:
./issue_dataaug.py:25 preprocess *
x = tf.keras.layers.RandomFlip(mode="horizontal")(x)
/...//virtualenvs/cvi36/lib/python3.6/site-packages/keras/layers/preprocessing/image_preprocessing.py:414 __init__ **
self._rng = make_generator(self.seed)
/...//virtualenvs/cvi36/lib/python3.6/site-packages/keras/layers/preprocessing/image_preprocessing.py:1375 make_generator
return tf.random.Generator.from_non_deterministic_state()
/...//virtualenvs/cvi36/lib/python3.6/site-packages/tensorflow/python/ops/stateful_random_ops.py:396 from_non_deterministic_state
return cls(state=state, alg=alg)
/...//virtualenvs/cvi36/lib/python3.6/site-packages/tensorflow/python/ops/stateful_random_ops.py:476 __init__
trainable=False)
/...//virtualenvs/cvi36/lib/python3.6/site-packages/tensorflow/python/ops/stateful_random_ops.py:489 _create_variable
return variables.Variable(*args, **kwargs)
/...//virtualenvs/cvi36/lib/python3.6/site-packages/tensorflow/python/ops/variables.py:268 __call__
return cls._variable_v2_call(*args, **kwargs)
/...//virtualenvs/cvi36/lib/python3.6/site-packages/tensorflow/python/ops/variables.py:262 _variable_v2_call
shape=shape)
/...//virtualenvs/cvi36/lib/python3.6/site-packages/tensorflow/python/ops/variables.py:243 <lambda>
previous_getter = lambda **kws: default_variable_creator_v2(None, **kws)
/...//virtualenvs/cvi36/lib/python3.6/site-packages/tensorflow/python/ops/variable_scope.py:2675 default_variable_creator_v2
shape=shape)
/...//virtualenvs/cvi36/lib/python3.6/site-packages/tensorflow/python/ops/variables.py:270 __call__
return super(VariableMetaclass, cls).__call__(*args, **kwargs)
/...//virtualenvs/cvi36/lib/python3.6/site-packages/tensorflow/python/ops/resource_variable_ops.py:1613 __init__
distribute_strategy=distribute_strategy)
/...//virtualenvs/cvi36/lib/python3.6/site-packages/tensorflow/python/ops/resource_variable_ops.py:1695 _init_from_args
raise ValueError("Tensor-typed variable initializers must either be "
ValueError: Tensor-typed variable initializers must either be wrapped in an init_scope or callable (e.g., `tf.Variable(lambda : tf.truncated_normal([10, 40]))`) when building functions. Please file a feature request if this restriction inconveniences you.
CodePudding user response:
I am not too sure this is directly related to your issue, but on TF
2.7 your code is not working at all, because all the Keras
augmentation layers expect float
values and not uint8
. So, maybe try casting your data like this:
import functools
import numpy as np
import tensorflow as tf
def data_gen():
for i in range(10):
x = np.random.random(size=(80, 80, 3)) * 255 # rgb image
x = x.astype('uint8')
y = np.random.random(size=(40, 40, 1)) * 255 # downsized mono image
y = y.astype('uint8')
yield x, y
def preprocess(image, label, cropped_image_size, cropped_label_size, skip_augmentations=False):
x = tf.cast(image, dtype=tf.float32)
y = tf.cast(label, dtype=tf.float32)
x_size = cropped_image_size
y_size = cropped_label_size
if not skip_augmentations:
x = tf.keras.layers.RandomFlip(mode="horizontal")(x)
y = tf.keras.layers.RandomFlip(mode="horizontal")(y)
x = tf.keras.layers.RandomRotation(factor=1.0, fill_mode='constant')(x)
y = tf.keras.layers.RandomRotation(factor=1.0, fill_mode='constant')(y)
x = tf.keras.layers.CenterCrop(x_size, x_size)(x)
y = tf.keras.layers.CenterCrop(y_size, y_size)(y)
return tf.cast(x, dtype=tf.uint8), tf.cast(y, dtype=tf.uint8)
print(tf.__version__) # 2.6.0
dataset = tf.data.Dataset.from_generator(data_gen, output_signature=(
tf.TensorSpec(shape=(80, 80, 3), dtype=tf.uint8),
tf.TensorSpec(shape=(40, 40, 1), dtype=tf.uint8)
))
crop_only_fn = functools.partial(preprocess, cropped_image_size=50, cropped_label_size=25, skip_augmentations=True)
train_preprocess_fn = functools.partial(preprocess, cropped_image_size=50, cropped_label_size=25, skip_augmentations=False)
# This works
crop_dataset = dataset.map(crop_only_fn)
# This fails: ValueError: Tensor-typed variable initializers must either be wrapped in an init_scope or callable
train_dataset = dataset.map(train_preprocess_fn)
On a side note, the Keras
augmentation layers are usually used as part of a model that you plan to train. You could alternatively use the tf.image
functions for example tf.image.central_crop
, tf.image.random_flip_left_right
or even tfa.image.rotate
.
Update 1: You are getting the error mentioned in the comments, because as documented here, the layers tf.keras.layers.RandomFlip
and tf.keras.layers.RandomRotation
are only active during training. So try using other methods:
import functools
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt
import tensorflow_addons as tfa
def preprocess(image, label, cropped_image_size, cropped_label_size, skip_augmentations=False):
x = tf.cast(image, dtype=tf.float32)
y = tf.cast(label, dtype=tf.float32)
x_size = cropped_image_size
y_size = cropped_label_size
if not skip_augmentations:
x = tf.image.random_flip_left_right(x)
y = tf.image.random_flip_left_right(y)
x = tfa.image.rotate(x, 90, fill_mode='constant')
y = tfa.image.rotate(y, 90, fill_mode='constant')
x = tf.keras.layers.CenterCrop(x_size, x_size)(x)
y = tf.keras.layers.CenterCrop(y_size, y_size)(y)
return tf.cast(x, dtype=tf.uint8), tf.cast(y, dtype=tf.uint8)
dataset = tf.data.Dataset.from_generator(data_gen, output_signature=(
tf.TensorSpec(shape=(80, 80, 3), dtype=tf.uint8),
tf.TensorSpec(shape=(40, 40, 1), dtype=tf.uint8)
))
crop_only_fn = functools.partial(preprocess, cropped_image_size=50, cropped_label_size=25, skip_augmentations=True)
train_preprocess_fn = functools.partial(preprocess, cropped_image_size=50, cropped_label_size=25, skip_augmentations=False)
crop_dataset = dataset.map(crop_only_fn)
train_dataset = dataset.map(train_preprocess_fn)
image, _ = next(iter(train_dataset.take(1)))
plt.imshow(image.numpy())
I excluded tf.keras.preprocessing.image.random_rotation
, since it does not seem to be working with tensors right now.
CodePudding user response:
As I commented, the error that you mentioned I didn't find reproducible. However, it simply needs to initialize the augmentation layers within the __init___
method.
ValueError: Tensor-typed variable initializers must either be wrapped in an init_scope or callable (e.g.,
tf.Variable(lambda : tf.truncated_normal([10, 40]))
) when building functions. Please file a feature request if this restriction inconveniences you.
Here is the full working code.
def data_gen():
for i in range(10):
x = np.random.random(size=(80, 80, 3)) * 255 # rgb image
x = x.astype('uint8')
y = np.random.random(size=(40, 40, 1)) * 255 # downsized mono image
y = y.astype('uint8')
yield x, y
class Augment(tf.keras.layers.Layer):
def __init__(self, seed=42):
super().__init__()
self.flip_a = tf.keras.layers.RandomFlip(mode="horizontal", seed=seed)
self.flip_b = tf.keras.layers.RandomFlip(mode="horizontal", seed=seed)
self.rot_a = tf.keras.layers.RandomRotation(factor=1.0,
fill_mode='constant', seed=seed)
self.rot_b = tf.keras.layers.RandomRotation(factor=1.0,
fill_mode='constant', seed=seed)
def call(self, inputs, labels):
x = self.flip_a(inputs)
x = self.rot_a(x)
y = self.flip_b(labels)
y = self.rot_b(y)
return x, y
def preprocess(image, label, cropped_image_size, cropped_label_size):
x = image
y = label
x_size = cropped_image_size
y_size = cropped_label_size
x = tf.cast(x, dtype=tf.float32)
y = tf.cast(y, dtype=tf.float32)
x = tf.keras.layers.CenterCrop(x_size, x_size)(x)
y = tf.keras.layers.CenterCrop(y_size, y_size)(y)
x = tf.cast(x, dtype=tf.uint8)
y = tf.cast(y, dtype=tf.uint8)
return x, y
Data
dataset = tf.data.Dataset.from_generator(data_gen, output_signature=(
tf.TensorSpec(shape=(80, 80, 3), dtype='uint8'),
tf.TensorSpec(shape=(40, 40, 1), dtype='uint8')
))
Test 1
crop_only_fn = functools.partial(preprocess,
cropped_image_size=50,
cropped_label_size=25)
# This works
crop_dataset = dataset.map(crop_only_fn)
x, y = next(iter(crop_dataset))
x.shape, y.shape
(TensorShape([50, 50, 3]), TensorShape([25, 25, 1]))
Test 2
train_preprocess_fn = functools.partial(preprocess,
cropped_image_size=50,
cropped_label_size=25)
train_dataset = dataset.map(train_preprocess_fn)
train_dataset = train_dataset.map(Augment()) # < calling now.
x, y = next(iter(train_dataset))
x.shape, y.shape
(TensorShape([50, 50, 3]), TensorShape([25, 25, 1]))