From 432c6e6a8cf6f5f09630fc53fe2d3ff161d4324d Mon Sep 17 00:00:00 2001 From: Samuel Marks <807580+SamuelMarks@users.noreply.github.com> Date: Tue, 17 Oct 2023 14:22:16 -0400 Subject: [PATCH 1/8] Fixes for reference before assignment found through static analysis tooling --- examples/demo_jax_distributed.py | 1 + guides/custom_train_step_in_jax.py | 4 ++-- guides/distributed_training_with_jax.py | 1 + keras/activations/activations.py | 2 ++ keras/applications/densenet.py | 4 ++++ keras/layers/attention/attention.py | 2 ++ keras/layers/preprocessing/category_encoding.py | 2 ++ keras/layers/preprocessing/feature_space.py | 3 +-- keras/legacy/backend.py | 2 ++ keras/ops/nn.py | 4 ++-- keras/optimizers/base_optimizer.py | 2 ++ keras/trainers/compile_utils.py | 4 ++-- keras/trainers/epoch_iterator.py | 2 ++ keras/utils/file_utils.py | 4 +++- 14 files changed, 28 insertions(+), 9 deletions(-) diff --git a/examples/demo_jax_distributed.py b/examples/demo_jax_distributed.py index 88ebe478f2f..3361588f91b 100644 --- a/examples/demo_jax_distributed.py +++ b/examples/demo_jax_distributed.py @@ -287,6 +287,7 @@ def train_step(train_state, x, y): print("\nTrainig:") data_iter = iter(train_data) for epoch in range(EPOCHS): + loss_value = None # default for i in tqdm(range(STEPS_PER_EPOCH)): x, y = next(data_iter) sharded_x = jax.device_put(x.numpy(), data_sharding) diff --git a/guides/custom_train_step_in_jax.py b/guides/custom_train_step_in_jax.py index a8cd1f8bfce..96c551a2e84 100644 --- a/guides/custom_train_step_in_jax.py +++ b/guides/custom_train_step_in_jax.py @@ -124,7 +124,7 @@ def train_step(self, state, data): ) # Update metrics. - new_metrics_vars = [] + new_metrics_vars, logs = [], [] for metric in self.metrics: this_metric_vars = metrics_variables[ len(new_metrics_vars) : len(new_metrics_vars) @@ -314,7 +314,7 @@ def test_step(self, state, data): loss = self.compute_loss(x, y, y_pred) # Update metrics. - new_metrics_vars = [] + new_metrics_vars, logs = [], [] for metric in self.metrics: this_metric_vars = metrics_variables[ len(new_metrics_vars) : len(new_metrics_vars) diff --git a/guides/distributed_training_with_jax.py b/guides/distributed_training_with_jax.py index 3b8cddb348a..4936b37fd4c 100644 --- a/guides/distributed_training_with_jax.py +++ b/guides/distributed_training_with_jax.py @@ -251,6 +251,7 @@ def get_replicated_train_state(devices): # Custom training loop for epoch in range(num_epochs): data_iter = iter(train_data) + loss_value = None # default for data in data_iter: x, y = data sharded_x = jax.device_put(x.numpy(), data_sharding) diff --git a/keras/activations/activations.py b/keras/activations/activations.py index 043c6090b94..30d06c0052e 100644 --- a/keras/activations/activations.py +++ b/keras/activations/activations.py @@ -83,6 +83,8 @@ def static_call(x, negative_slope=0.0, max_value=None, threshold=0.0): negative_part = backend.nn.relu(-x + threshold) else: negative_part = backend.nn.relu(-x) + else: + negative_part = 1 clip_max = max_value is not None if threshold != 0: diff --git a/keras/applications/densenet.py b/keras/applications/densenet.py index d9bca939c98..6e9cde95935 100644 --- a/keras/applications/densenet.py +++ b/keras/applications/densenet.py @@ -294,6 +294,8 @@ def DenseNet( cache_subdir="models", file_hash="1ceb130c1ea1b78c3bf6114dbdfd8807", ) + else: + raise ValueError("weights_path undefined") else: if blocks == [6, 12, 24, 16]: weights_path = file_utils.get_file( @@ -316,6 +318,8 @@ def DenseNet( cache_subdir="models", file_hash="c13680b51ded0fb44dff2d8f86ac8bb1", ) + else: + raise ValueError("weights_path undefined") model.load_weights(weights_path) elif weights is not None: model.load_weights(weights) diff --git a/keras/layers/attention/attention.py b/keras/layers/attention/attention.py index 30027a3ff7a..261b49f9b4b 100644 --- a/keras/layers/attention/attention.py +++ b/keras/layers/attention/attention.py @@ -129,6 +129,8 @@ def _calculate_scores(self, query, key): scores = self.concat_score_weight * ops.sum( ops.tanh(q_reshaped + k_reshaped), axis=-1 ) + else: + raise ValueError("scores not computed") return scores diff --git a/keras/layers/preprocessing/category_encoding.py b/keras/layers/preprocessing/category_encoding.py index 6e89fc9afdd..5d5cdf81979 100644 --- a/keras/layers/preprocessing/category_encoding.py +++ b/keras/layers/preprocessing/category_encoding.py @@ -129,6 +129,8 @@ def _encode(self, inputs): ) elif self.output_mode == "count": outputs = self._count(inputs) + else: + raise ValueError("_encode outputs not calculated") return outputs diff --git a/keras/layers/preprocessing/feature_space.py b/keras/layers/preprocessing/feature_space.py index 214f7ffda95..f077c66f8a1 100644 --- a/keras/layers/preprocessing/feature_space.py +++ b/keras/layers/preprocessing/feature_space.py @@ -517,8 +517,7 @@ def adapt(self, dataset): preprocessor = self.preprocessors[name] # TODO: consider adding an adapt progress bar. # Sample 1 element to check the rank - for x in feature_dataset.take(1): - pass + x = next(feature_dataset.take(1)) if len(x.shape) == 0: # The dataset yields unbatched scalars; batch it. feature_dataset = feature_dataset.batch(32) diff --git a/keras/legacy/backend.py b/keras/legacy/backend.py index 5cfd2208ab8..e156f95c789 100644 --- a/keras/legacy/backend.py +++ b/keras/legacy/backend.py @@ -1279,6 +1279,8 @@ def relu(x, alpha=0.0, max_value=None, threshold=0.0): negative_part = tf.nn.relu(-x + threshold) else: negative_part = tf.nn.relu(-x) + else: + negative_part = 1 clip_max = max_value is not None diff --git a/keras/ops/nn.py b/keras/ops/nn.py index 6e3083004f8..48b853cb75c 100644 --- a/keras/ops/nn.py +++ b/keras/ops/nn.py @@ -1212,7 +1212,7 @@ def compute_output_spec(self, x): x_shape = list(getattr(x, "shape", [])) if self.axis == -1: x_shape.append(self.num_classes) - elif self.axis >= 0 and self.axis < len(x_shape): + elif 0 <= self.axis < len(x_shape): x_shape.insert(self.axis, self.num_classes) else: raise ValueError( @@ -1517,7 +1517,7 @@ def compute_output_spec(self, inputs): x_shape = list(getattr(inputs, "shape", [])) if self.axis == -1: x_shape.append(self.num_tokens) - elif self.axis >= 0 and self.axis < len(x_shape): + elif 0 <= self.axis < len(x_shape): x_shape.insert(self.axis, self.num_tokens) else: raise ValueError( diff --git a/keras/optimizers/base_optimizer.py b/keras/optimizers/base_optimizer.py index 9b2323e4984..7a3facfec6c 100644 --- a/keras/optimizers/base_optimizer.py +++ b/keras/optimizers/base_optimizer.py @@ -602,6 +602,8 @@ def get_config(self): learning_rate = serialization_lib.serialize_keras_object( self._learning_rate ) + else: + learning_rate = 0.5 config = { "name": self.name, diff --git a/keras/trainers/compile_utils.py b/keras/trainers/compile_utils.py index 9dbf20eb6b8..a11b6b57a25 100644 --- a/keras/trainers/compile_utils.py +++ b/keras/trainers/compile_utils.py @@ -159,6 +159,7 @@ def variables(self): return vars def build(self, y_true, y_pred): + num_outputs = 1 # default if self.output_names: output_names = self.output_names elif isinstance(y_pred, dict): @@ -171,7 +172,6 @@ def build(self, y_true, y_pred): output_names = None else: output_names = None - num_outputs = 1 if output_names: num_outputs = len(output_names) @@ -415,6 +415,7 @@ def __init__( super().__init__(name="compile_loss", reduction=reduction) def build(self, y_true, y_pred): + num_outputs = 1 # default if self.output_names: output_names = self.output_names elif isinstance(y_pred, dict): @@ -427,7 +428,6 @@ def build(self, y_true, y_pred): output_names = None else: output_names = None - num_outputs = 1 if output_names: num_outputs = len(output_names) diff --git a/keras/trainers/epoch_iterator.py b/keras/trainers/epoch_iterator.py index b5d7606c077..fd3155b4545 100644 --- a/keras/trainers/epoch_iterator.py +++ b/keras/trainers/epoch_iterator.py @@ -90,6 +90,7 @@ def enumerate_epoch(self, return_type="auto"): self._current_iterator = self._get_iterator(return_type) self._insufficient_data = False + step = 0 for step in range(self.steps_per_epoch): if self._insufficient_data: break @@ -114,6 +115,7 @@ def enumerate_epoch(self, return_type="auto"): if buffer: yield step - len(buffer) + 1, buffer else: + step = 0 for step, data in enumerate(self._get_iterator(return_type)): buffer.append(data) if len(buffer) == self.steps_per_execution: diff --git a/keras/utils/file_utils.py b/keras/utils/file_utils.py index e6963dea2db..b3c69aa7db3 100644 --- a/keras/utils/file_utils.py +++ b/keras/utils/file_utils.py @@ -101,9 +101,11 @@ def extract_archive(file_path, path=".", archive_format="auto"): if archive_type == "tar": open_fn = tarfile.open is_match_fn = tarfile.is_tarfile - if archive_type == "zip": + elif archive_type == "zip": open_fn = zipfile.ZipFile is_match_fn = zipfile.is_zipfile + else: + raise NotImplementedError(archive_type) if is_match_fn(file_path): with open_fn(file_path) as archive: From cdaa4a90cc63e3e46b2092852b617238558797ac Mon Sep 17 00:00:00 2001 From: Samuel Marks <807580+SamuelMarks@users.noreply.github.com> Date: Tue, 17 Oct 2023 16:19:33 -0400 Subject: [PATCH 2/8] Fixes for reference before assignment found through static analysis tooling --- .../timeseries_weather_forecasting.py | 3 +- keras/backend/jax/trainer.py | 1 + keras/backend/torch/trainer.py | 1 + .../preprocessing/category_encoding_test.py | 3 +- .../layers/preprocessing/center_crop_test.py | 3 +- .../preprocessing/discretization_test.py | 7 ++- keras/layers/preprocessing/feature_space.py | 2 +- .../preprocessing/hashed_crossing_test.py | 3 +- keras/layers/preprocessing/hashing_test.py | 5 +- .../preprocessing/integer_lookup_test.py | 3 +- keras/layers/preprocessing/normalization.py | 2 + .../preprocessing/normalization_test.py | 2 + .../preprocessing/random_brightness_test.py | 4 +- .../preprocessing/random_contrast_test.py | 3 +- .../layers/preprocessing/random_crop_test.py | 3 +- .../layers/preprocessing/random_flip_test.py | 6 +-- .../preprocessing/random_rotation_test.py | 3 +- .../preprocessing/random_translation_test.py | 3 +- .../layers/preprocessing/random_zoom_test.py | 3 +- keras/layers/preprocessing/rescaling_test.py | 3 +- keras/layers/preprocessing/resizing_test.py | 6 +-- .../preprocessing/string_lookup_test.py | 3 +- .../preprocessing/text_vectorization_test.py | 6 +-- keras/models/model.py | 5 +- keras/models/sequential.py | 1 + keras/ops/function.py | 2 +- keras/ops/numpy.py | 46 +++++++++---------- keras/ops/operation_utils.py | 4 +- keras/saving/saving_lib.py | 5 ++ keras/utils/nest.py | 1 + keras/utils/numerical_utils.py | 2 + 31 files changed, 72 insertions(+), 72 deletions(-) diff --git a/examples/keras_io/tensorflow/timeseries/timeseries_weather_forecasting.py b/examples/keras_io/tensorflow/timeseries/timeseries_weather_forecasting.py index b2dd13955dc..a22fd62cab4 100644 --- a/examples/keras_io/tensorflow/timeseries/timeseries_weather_forecasting.py +++ b/examples/keras_io/tensorflow/timeseries/timeseries_weather_forecasting.py @@ -281,8 +281,7 @@ def normalize(data, train_split): ) -for batch in dataset_train.take(1): - inputs, targets = batch +inputs, targets = next(iter(dataset_train)) print("Input shape:", inputs.numpy().shape) print("Target shape:", targets.numpy().shape) diff --git a/keras/backend/jax/trainer.py b/keras/backend/jax/trainer.py index cc76d3a06e2..c8c5d5133fb 100644 --- a/keras/backend/jax/trainer.py +++ b/keras/backend/jax/trainer.py @@ -412,6 +412,7 @@ def fit( self.make_train_function() self.stop_training = False + training_logs = {} callbacks.on_train_begin() for epoch in range(initial_epoch, epochs): diff --git a/keras/backend/torch/trainer.py b/keras/backend/torch/trainer.py index 56f7eb6cf81..de10601ce1e 100644 --- a/keras/backend/torch/trainer.py +++ b/keras/backend/torch/trainer.py @@ -280,6 +280,7 @@ def fit( ) self.stop_training = False + training_logs = {} self.make_train_function() callbacks.on_train_begin() diff --git a/keras/layers/preprocessing/category_encoding_test.py b/keras/layers/preprocessing/category_encoding_test.py index bd110995f46..cc9c018ebdd 100644 --- a/keras/layers/preprocessing/category_encoding_test.py +++ b/keras/layers/preprocessing/category_encoding_test.py @@ -164,6 +164,5 @@ def test_tf_data_compatibility(self): ] ) ds = tf_data.Dataset.from_tensor_slices(input_data).batch(4).map(layer) - for output in ds.take(1): - output = output.numpy() + output = next(iter(ds)).numpy() self.assertAllClose(output, expected_output) diff --git a/keras/layers/preprocessing/center_crop_test.py b/keras/layers/preprocessing/center_crop_test.py index 888737ec947..3d42d36b8fc 100644 --- a/keras/layers/preprocessing/center_crop_test.py +++ b/keras/layers/preprocessing/center_crop_test.py @@ -151,8 +151,7 @@ def test_tf_data_compatibility(self): layer = layers.CenterCrop(8, 9) input_data = np.random.random((2, 10, 12, 3)) ds = tf_data.Dataset.from_tensor_slices(input_data).batch(2).map(layer) - for output in ds.take(1): - output = output.numpy() + output = next(iter(ds)).numpy() self.assertEqual(list(output.shape), [2, 8, 9, 3]) def test_list_compatibility(self): diff --git a/keras/layers/preprocessing/discretization_test.py b/keras/layers/preprocessing/discretization_test.py index 874b2de23ab..db6fcd8ce47 100644 --- a/keras/layers/preprocessing/discretization_test.py +++ b/keras/layers/preprocessing/discretization_test.py @@ -76,8 +76,7 @@ def test_tf_data_compatibility(self): x = np.array([[-1.0, 0.0, 0.1, 0.2, 0.4, 0.5, 1.0, 1.2, 0.98]]) self.assertAllClose(layer(x), np.array([[0, 1, 1, 1, 2, 3, 4, 4, 3]])) ds = tf_data.Dataset.from_tensor_slices(x).batch(1).map(layer) - for output in ds.take(1): - output = output.numpy() + output = next(iter(ds)).numpy() self.assertAllClose(output, np.array([[0, 1, 1, 1, 2, 3, 4, 4, 3]])) # With adapt flow @@ -87,8 +86,8 @@ def test_tf_data_compatibility(self): ) x = np.array([[0.0, 0.1, 0.3]]) ds = tf_data.Dataset.from_tensor_slices(x).batch(1).map(layer) - for output in ds.take(1): - output.numpy() + output = next(iter(ds)).numpy() + self.assertAllClose(output, x) def test_saving(self): # With fixed bins diff --git a/keras/layers/preprocessing/feature_space.py b/keras/layers/preprocessing/feature_space.py index f077c66f8a1..8cb82516761 100644 --- a/keras/layers/preprocessing/feature_space.py +++ b/keras/layers/preprocessing/feature_space.py @@ -517,7 +517,7 @@ def adapt(self, dataset): preprocessor = self.preprocessors[name] # TODO: consider adding an adapt progress bar. # Sample 1 element to check the rank - x = next(feature_dataset.take(1)) + x = next(iter(feature_dataset)) if len(x.shape) == 0: # The dataset yields unbatched scalars; batch it. feature_dataset = feature_dataset.batch(32) diff --git a/keras/layers/preprocessing/hashed_crossing_test.py b/keras/layers/preprocessing/hashed_crossing_test.py index 53a4c0390c2..8a0434ee055 100644 --- a/keras/layers/preprocessing/hashed_crossing_test.py +++ b/keras/layers/preprocessing/hashed_crossing_test.py @@ -74,8 +74,7 @@ def test_tf_data_compatibility(self): .batch(5) .map(lambda x1, x2: layer((x1, x2))) ) - for output in ds.take(1): - output = output.numpy() + output = next(iter(ds)).numpy() self.assertAllClose(np.array([1, 4, 1, 1, 3]), output) def test_upsupported_shape_input_fails(self): diff --git a/keras/layers/preprocessing/hashing_test.py b/keras/layers/preprocessing/hashing_test.py index 4cd5f71667e..d23de762142 100644 --- a/keras/layers/preprocessing/hashing_test.py +++ b/keras/layers/preprocessing/hashing_test.py @@ -60,8 +60,7 @@ def test_tf_data_compatibility(self): layer = layers.Hashing(num_bins=3) inp = [["A"], ["B"], ["C"], ["D"], ["E"]] ds = tf.data.Dataset.from_tensor_slices(inp).batch(5).map(layer) - for output in ds.take(1): - output = output.numpy() + output = next(iter(ds)).numpy() self.assertAllClose(output, np.array([[1], [0], [1], [1], [2]])) @parameterized.named_parameters( @@ -306,6 +305,8 @@ def test_count_output(self, input_value, expected_output, output_shape): symbolic_sample_shape = () elif input_array.ndim == 2: symbolic_sample_shape = (None,) + else: + raise TypeError("Unknown `symbolic_sample_shape`") inputs = layers.Input(shape=symbolic_sample_shape, dtype="int32") layer = layers.Hashing(num_bins=3, output_mode="count") outputs = layer(inputs) diff --git a/keras/layers/preprocessing/integer_lookup_test.py b/keras/layers/preprocessing/integer_lookup_test.py index ede05bedf29..24e8919777c 100644 --- a/keras/layers/preprocessing/integer_lookup_test.py +++ b/keras/layers/preprocessing/integer_lookup_test.py @@ -102,6 +102,5 @@ def test_tf_data_compatibility(self): ) input_data = [2, 3, 4, 5] ds = tf_data.Dataset.from_tensor_slices(input_data).batch(4).map(layer) - for output in ds.take(1): - output = output.numpy() + output = next(iter(ds)).numpy() self.assertAllClose(output, np.array([2, 3, 4, 0])) diff --git a/keras/layers/preprocessing/normalization.py b/keras/layers/preprocessing/normalization.py index 76a4c37972d..6fa11cce93a 100644 --- a/keras/layers/preprocessing/normalization.py +++ b/keras/layers/preprocessing/normalization.py @@ -275,6 +275,8 @@ def adapt(self, data): batch_var + (batch_mean - new_total_mean) ** 2 ) * batch_weight total_mean = new_total_mean + else: + raise NotImplementedError(type(data)) self.adapt_mean.assign(total_mean) self.adapt_variance.assign(total_var) diff --git a/keras/layers/preprocessing/normalization_test.py b/keras/layers/preprocessing/normalization_test.py index c0c402709bc..c6d2ba6b5b4 100644 --- a/keras/layers/preprocessing/normalization_test.py +++ b/keras/layers/preprocessing/normalization_test.py @@ -65,6 +65,8 @@ def test_normalization_adapt(self, input_type): data = backend.convert_to_tensor(x) elif input_type == "tf.data": data = tf_data.Dataset.from_tensor_slices(x).batch(8) + else: + raise NotImplementedError(input_type) layer = layers.Normalization() layer.adapt(data) diff --git a/keras/layers/preprocessing/random_brightness_test.py b/keras/layers/preprocessing/random_brightness_test.py index 1044d28eace..4fc2c5686cf 100644 --- a/keras/layers/preprocessing/random_brightness_test.py +++ b/keras/layers/preprocessing/random_brightness_test.py @@ -56,5 +56,5 @@ def test_tf_data_compatibility(self): layer = layers.RandomBrightness(factor=0.5, seed=1337) input_data = np.random.random((2, 8, 8, 3)) ds = tf_data.Dataset.from_tensor_slices(input_data).batch(2).map(layer) - for output in ds.take(1): - output.numpy() + output = next(iter(ds)).numpy() + self.assertAllClose(output, input_data) diff --git a/keras/layers/preprocessing/random_contrast_test.py b/keras/layers/preprocessing/random_contrast_test.py index 95a9d1d85ce..386da49de9a 100644 --- a/keras/layers/preprocessing/random_contrast_test.py +++ b/keras/layers/preprocessing/random_contrast_test.py @@ -43,5 +43,4 @@ def test_tf_data_compatibility(self): layer = layers.RandomContrast(factor=0.5, seed=1337) input_data = np.random.random((2, 8, 8, 3)) ds = tf_data.Dataset.from_tensor_slices(input_data).batch(2).map(layer) - for output in ds.take(1): - output.numpy() + next(iter(ds)).numpy() diff --git a/keras/layers/preprocessing/random_crop_test.py b/keras/layers/preprocessing/random_crop_test.py index a8982e8dce5..5b90b59230b 100644 --- a/keras/layers/preprocessing/random_crop_test.py +++ b/keras/layers/preprocessing/random_crop_test.py @@ -69,6 +69,5 @@ def test_tf_data_compatibility(self): layer = layers.RandomCrop(8, 9) input_data = np.random.random((2, 10, 12, 3)) ds = tf_data.Dataset.from_tensor_slices(input_data).batch(2).map(layer) - for output in ds.take(1): - output = output.numpy() + output = next(iter(ds)).numpy() self.assertEqual(list(output.shape), [2, 8, 9, 3]) diff --git a/keras/layers/preprocessing/random_flip_test.py b/keras/layers/preprocessing/random_flip_test.py index b143979aa30..b00538695f4 100644 --- a/keras/layers/preprocessing/random_flip_test.py +++ b/keras/layers/preprocessing/random_flip_test.py @@ -135,8 +135,7 @@ def test_tf_data_compatibility(self): input_data = np.array([[[2, 3, 4]], [[5, 6, 7]]]) expected_output = np.array([[[5, 6, 7]], [[2, 3, 4]]]) ds = tf_data.Dataset.from_tensor_slices(input_data).batch(2).map(layer) - for output in ds.take(1): - output = output.numpy() + output = next(iter(ds)).numpy() self.assertAllClose(output, expected_output) # Test 4D input: shape (2, 2, 1, 3) layer = layers.RandomFlip("vertical", seed=42) @@ -159,6 +158,5 @@ def test_tf_data_compatibility(self): ] ) ds = tf_data.Dataset.from_tensor_slices(input_data).batch(2).map(layer) - for output in ds.take(1): - output = output.numpy() + output = next(iter(ds)).numpy() self.assertAllClose(output, expected_output) diff --git a/keras/layers/preprocessing/random_rotation_test.py b/keras/layers/preprocessing/random_rotation_test.py index 8dc4a104285..ea6dddc64c1 100644 --- a/keras/layers/preprocessing/random_rotation_test.py +++ b/keras/layers/preprocessing/random_rotation_test.py @@ -65,6 +65,5 @@ def test_tf_data_compatibility(self): [4, 3, 2, 1, 0], ] ).reshape((5, 5, 1)) - for output in ds.take(1): - output = output.numpy() + output = next(iter(ds)).numpy() self.assertAllClose(expected_output, output) diff --git a/keras/layers/preprocessing/random_translation_test.py b/keras/layers/preprocessing/random_translation_test.py index 7545dd96ff3..08dd9c4875d 100644 --- a/keras/layers/preprocessing/random_translation_test.py +++ b/keras/layers/preprocessing/random_translation_test.py @@ -327,5 +327,4 @@ def test_tf_data_compatibility(self): layer = layers.RandomTranslation(0.2, 0.1) input_data = np.random.random((1, 4, 4, 3)) ds = tf_data.Dataset.from_tensor_slices(input_data).batch(1).map(layer) - for output in ds.take(1): - output.numpy() + next(iter(ds)).numpy() diff --git a/keras/layers/preprocessing/random_zoom_test.py b/keras/layers/preprocessing/random_zoom_test.py index 8781572d780..55c42c9fcee 100644 --- a/keras/layers/preprocessing/random_zoom_test.py +++ b/keras/layers/preprocessing/random_zoom_test.py @@ -106,8 +106,7 @@ def test_tf_data_compatibility(self): [0, 0, 0, 0, 0], ] ).reshape((1, 5, 5, 1)) - for output in ds.take(1): - output = output.numpy() + output = next(iter(ds)).numpy() self.assertAllClose(expected_output, output) def test_dynamic_shape(self): diff --git a/keras/layers/preprocessing/rescaling_test.py b/keras/layers/preprocessing/rescaling_test.py index 34ae5171403..87f08eb3eee 100644 --- a/keras/layers/preprocessing/rescaling_test.py +++ b/keras/layers/preprocessing/rescaling_test.py @@ -72,8 +72,7 @@ def test_tf_data_compatibility(self): layer = layers.Rescaling(scale=1.0 / 255, offset=0.5) x = np.random.random((3, 10, 10, 3)) * 255 ds = tf_data.Dataset.from_tensor_slices(x).batch(3).map(layer) - for output in ds.take(1): - output.numpy() + next(iter(ds)).numpy() def test_rescaling_with_channels_first_and_vector_scale(self): config = backend.image_data_format() diff --git a/keras/layers/preprocessing/resizing_test.py b/keras/layers/preprocessing/resizing_test.py index 31cc19b0593..137db2c5d42 100644 --- a/keras/layers/preprocessing/resizing_test.py +++ b/keras/layers/preprocessing/resizing_test.py @@ -180,8 +180,7 @@ def test_tf_data_compatibility(self): layer = layers.Resizing(8, 9) input_data = np.random.random((2, 10, 12, 3)) ds = tf_data.Dataset.from_tensor_slices(input_data).batch(2).map(layer) - for output in ds.take(1): - output = output.numpy() + output = next(iter(ds)).numpy() self.assertEqual(list(output.shape), [2, 8, 9, 3]) @pytest.mark.skipif( @@ -198,6 +197,5 @@ def test_tf_data_compatibility_sequential(self): .batch(2) .map(Sequential([layer])) ) - for output in ds.take(1): - output = output.numpy() + output = next(iter(ds)).numpy() self.assertEqual(list(output.shape), [2, 8, 9, 3]) diff --git a/keras/layers/preprocessing/string_lookup_test.py b/keras/layers/preprocessing/string_lookup_test.py index 0f33f3d3b1c..777a16be66c 100644 --- a/keras/layers/preprocessing/string_lookup_test.py +++ b/keras/layers/preprocessing/string_lookup_test.py @@ -55,6 +55,5 @@ def test_tf_data_compatibility(self): ) input_data = ["b", "c", "d"] ds = tf_data.Dataset.from_tensor_slices(input_data).batch(3).map(layer) - for output in ds.take(1): - output = output.numpy() + output = next(iter(ds)).numpy() self.assertAllClose(output, np.array([2, 3, 0])) diff --git a/keras/layers/preprocessing/text_vectorization_test.py b/keras/layers/preprocessing/text_vectorization_test.py index 11078a8d1db..7bf09c35d35 100644 --- a/keras/layers/preprocessing/text_vectorization_test.py +++ b/keras/layers/preprocessing/text_vectorization_test.py @@ -72,8 +72,7 @@ def test_tf_data_compatibility(self): ) input_data = [["foo qux bar"], ["qux baz"]] ds = tf_data.Dataset.from_tensor_slices(input_data).batch(2).map(layer) - for output in ds.take(1): - output = output.numpy() + output = next(iter(ds)).numpy() self.assertAllClose(output, np.array([[4, 1, 3, 0], [1, 2, 0, 0]])) # Test adapt flow @@ -84,8 +83,7 @@ def test_tf_data_compatibility(self): ) layer.adapt(input_data) ds = tf_data.Dataset.from_tensor_slices(input_data).batch(2).map(layer) - for output in ds.take(1): - output.numpy() + next(iter(ds)).numpy() @pytest.mark.skipif( backend.backend() != "tensorflow", reason="Requires string tensors." diff --git a/keras/models/model.py b/keras/models/model.py index 74386b8bafe..505e9a4d0ab 100644 --- a/keras/models/model.py +++ b/keras/models/model.py @@ -418,6 +418,7 @@ def load_weights(self, filepath, skip_mismatch=False, **kwargs): def build_from_config(self, config): if not config: return + status = False if "input_shape" in config: # Case: all inputs are in the first arg (possibly nested). if utils.is_default(self.build): @@ -429,7 +430,7 @@ def build_from_config(self, config): self.build(config["input_shape"]) status = True except: - status = False + pass self._build_shapes_dict = config elif "shapes_dict" in config: @@ -441,7 +442,7 @@ def build_from_config(self, config): self.build(**config["shapes_dict"]) status = True except: - status = False + pass self._build_shapes_dict = config["shapes_dict"] if not status: diff --git a/keras/models/sequential.py b/keras/models/sequential.py index 349b7674fcf..58b876ae86f 100644 --- a/keras/models/sequential.py +++ b/keras/models/sequential.py @@ -320,6 +320,7 @@ def from_config(cls, config, custom_objects=None): model.add(layer) if ( not model._functional + and "build_input_shape" in locals() and build_input_shape and isinstance(build_input_shape, (tuple, list)) ): diff --git a/keras/ops/function.py b/keras/ops/function.py index e00ca5ce188..14a10ebf5e9 100644 --- a/keras/ops/function.py +++ b/keras/ops/function.py @@ -275,7 +275,7 @@ def map_graph(inputs, outputs): "The following previous operations were accessed " f"without issue: {operations_with_complete_input}" ) - operations_with_complete_input.append(operation.name) + operations_with_complete_input.append(node.operation.name) for x in tree.flatten(node.outputs): computable_tensors.add(x) diff --git a/keras/ops/numpy.py b/keras/ops/numpy.py index fea950ed923..e26d6a8c7cb 100644 --- a/keras/ops/numpy.py +++ b/keras/ops/numpy.py @@ -1140,39 +1140,39 @@ def call(self, x, weights=None): def compute_output_spec(self, x, weights=None): if weights is not None: - shape_match = shape_equal(x.shape, weights.shape, allow_none=True) - if self.axis is not None: - shape_match_on_axis = shape_equal( - [x.shape[self.axis]], weights.shape, allow_none=True - ) - if self.axis is None: - if weights is None or shape_match: + if shape_equal(x.shape, weights.shape, allow_none=True): return KerasTensor( - [], + reduce_shape(x.shape, axis=[self.axis]), dtype=x.dtype, ) - else: - raise ValueError( - "`weights` must have the same shape as `x` when " - f"`axis=None`, but received `weights.shape={weights.shape}`" - f" and `x.shape={x.shape}`." - ) + elif self.axis is not None and shape_equal( + [x.shape[self.axis]], weights.shape, allow_none=True): + return KerasTensor( + reduce_shape(x.shape, axis=[self.axis]), + dtype=x.dtype, + ) - if weights is None or shape_match_on_axis or shape_match: + if weights is None: return KerasTensor( - reduce_shape(x.shape, axis=[self.axis]), + [], dtype=x.dtype, ) - else: - # `weights` can either be a 1D array of length `x.shape[axis]` or - # of the same shape as `x`. + elif self.axis is None: raise ValueError( - "`weights` must have the same size as `x` at " - f"`axis={self.axis}` but received " - f"`weights.shape={weights.shape}` while x.shape at " - f"`{self.axis}` is `{x.shape[self.axis]}`." + "`weights` must have the same shape as `x` when " + f"`axis=None`, but received `weights.shape={weights.shape}`" + f" and `x.shape={x.shape}`." ) + # `weights` can either be a 1D array of length `x.shape[axis]` or + # of the same shape as `x`. + raise ValueError( + "`weights` must have the same size as `x` at " + f"`axis={self.axis}` but received " + f"`weights.shape={weights.shape}` while x.shape at " + f"`{self.axis}` is `{x.shape[self.axis]}`." + ) + @keras_export(["keras.ops.average", "keras.ops.numpy.average"]) def average(x, axis=None, weights=None): diff --git a/keras/ops/operation_utils.py b/keras/ops/operation_utils.py index b26de9857e2..d0582890d14 100644 --- a/keras/ops/operation_utils.py +++ b/keras/ops/operation_utils.py @@ -157,7 +157,9 @@ def compute_conv_output_shape( ) elif padding == "same" or padding == "causal": output_spatial_shape = np.floor((spatial_shape - 1) / strides) + 1 - output_spatial_shape = [int(i) for i in output_spatial_shape] + else: + raise ValueError("Unknown padding") + output_spatial_shape = list(map(int, output_spatial_shape)) for i in none_dims: output_spatial_shape[i] = None output_spatial_shape = tuple(output_spatial_shape) diff --git a/keras/saving/saving_lib.py b/keras/saving/saving_lib.py index 7342bcc8f22..21bb69ed74a 100644 --- a/keras/saving/saving_lib.py +++ b/keras/saving/saving_lib.py @@ -3,6 +3,7 @@ import datetime import io import json +import os.path import tempfile import warnings import zipfile @@ -224,6 +225,10 @@ def load_weights_only(model, filepath, skip_mismatch=False): weights_store = H5IOStore( _VARS_FNAME + ".h5", archive=archive, mode="r" ) + else: + raise NotImplementedError( + (lambda full_ext: full_ext[1] if full_ext[1] else filepath)( + os.path.splitext(filepath)[1])) _load_state( model, diff --git a/keras/utils/nest.py b/keras/utils/nest.py index 59aeee8de9d..3111f8e1f5d 100644 --- a/keras/utils/nest.py +++ b/keras/utils/nest.py @@ -33,6 +33,7 @@ def truncate(value, length): ) return flat_sequence[0] + packed = [] try: final_index, packed = packed_nest_with_indices( structure, flat_sequence, 0, is_nested_fn, sequence_fn diff --git a/keras/utils/numerical_utils.py b/keras/utils/numerical_utils.py index cf5266ffd2d..5063e1aadd5 100644 --- a/keras/utils/numerical_utils.py +++ b/keras/utils/numerical_utils.py @@ -143,6 +143,8 @@ def encode_categorical_inputs( bincounts = backend_module.numpy.where( backend_module.numpy.any(one_hot_input, axis=-2), 1, 0 ) + else: + raise NotImplementedError(output_mode) else: bincounts = backend_module.numpy.bincount( inputs, From 3a50584efce8ca94a6d9a03886572e7fbfe79be3 Mon Sep 17 00:00:00 2001 From: Samuel Marks <807580+SamuelMarks@users.noreply.github.com> Date: Wed, 25 Oct 2023 14:19:06 -0400 Subject: [PATCH 3/8] [keras/layers/preprocessing/discretization_test.py] Remove `assertAllClose` from `test_tf_data_compatibility` ; [keras/{ops/numpy.py,saving/saving_lib.py}] `black`en --- keras/layers/preprocessing/discretization_test.py | 4 ++-- keras/ops/numpy.py | 11 ++++++----- keras/saving/saving_lib.py | 4 +++- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/keras/layers/preprocessing/discretization_test.py b/keras/layers/preprocessing/discretization_test.py index db6fcd8ce47..58f3fca294c 100644 --- a/keras/layers/preprocessing/discretization_test.py +++ b/keras/layers/preprocessing/discretization_test.py @@ -86,8 +86,8 @@ def test_tf_data_compatibility(self): ) x = np.array([[0.0, 0.1, 0.3]]) ds = tf_data.Dataset.from_tensor_slices(x).batch(1).map(layer) - output = next(iter(ds)).numpy() - self.assertAllClose(output, x) + next(iter(ds)).numpy() + # self.assertAllClose(output, x) def test_saving(self): # With fixed bins diff --git a/keras/ops/numpy.py b/keras/ops/numpy.py index e26d6a8c7cb..20b1483015f 100644 --- a/keras/ops/numpy.py +++ b/keras/ops/numpy.py @@ -1146,11 +1146,12 @@ def compute_output_spec(self, x, weights=None): dtype=x.dtype, ) elif self.axis is not None and shape_equal( - [x.shape[self.axis]], weights.shape, allow_none=True): - return KerasTensor( - reduce_shape(x.shape, axis=[self.axis]), - dtype=x.dtype, - ) + [x.shape[self.axis]], weights.shape, allow_none=True + ): + return KerasTensor( + reduce_shape(x.shape, axis=[self.axis]), + dtype=x.dtype, + ) if weights is None: return KerasTensor( diff --git a/keras/saving/saving_lib.py b/keras/saving/saving_lib.py index 21bb69ed74a..af5a7710976 100644 --- a/keras/saving/saving_lib.py +++ b/keras/saving/saving_lib.py @@ -228,7 +228,9 @@ def load_weights_only(model, filepath, skip_mismatch=False): else: raise NotImplementedError( (lambda full_ext: full_ext[1] if full_ext[1] else filepath)( - os.path.splitext(filepath)[1])) + os.path.splitext(filepath)[1] + ) + ) _load_state( model, From 6c577456fc9303c0fd0665166eb4ffa62db1f50e Mon Sep 17 00:00:00 2001 From: Samuel Marks <807580+SamuelMarks@users.noreply.github.com> Date: Thu, 26 Oct 2023 19:50:26 -0400 Subject: [PATCH 4/8] [keras/ops/numpy.py] Revert to see if segfault resolves on CI --- keras/ops/numpy.py | 45 ++++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/keras/ops/numpy.py b/keras/ops/numpy.py index a47aeacada2..22b88b92763 100644 --- a/keras/ops/numpy.py +++ b/keras/ops/numpy.py @@ -1146,40 +1146,39 @@ def call(self, x, weights=None): def compute_output_spec(self, x, weights=None): if weights is not None: - if shape_equal(x.shape, weights.shape, allow_none=True): - return KerasTensor( - reduce_shape(x.shape, axis=[self.axis]), - dtype=x.dtype, + shape_match = shape_equal(x.shape, weights.shape, allow_none=True) + if self.axis is not None: + shape_match_on_axis = shape_equal( + [x.shape[self.axis]], weights.shape, allow_none=True ) - elif self.axis is not None and shape_equal( - [x.shape[self.axis]], weights.shape, allow_none=True - ): + if self.axis is None: + if weights is None or shape_match: return KerasTensor( - reduce_shape(x.shape, axis=[self.axis]), + [], dtype=x.dtype, ) + else: + raise ValueError( + "`weights` must have the same shape as `x` when " + f"`axis=None`, but received `weights.shape={weights.shape}`" + f" and `x.shape={x.shape}`." + ) - if weights is None: + if weights is None or shape_match_on_axis or shape_match: return KerasTensor( - [], + reduce_shape(x.shape, axis=[self.axis]), dtype=x.dtype, ) - elif self.axis is None: + else: + # `weights` can either be a 1D array of length `x.shape[axis]` or + # of the same shape as `x`. raise ValueError( - "`weights` must have the same shape as `x` when " - f"`axis=None`, but received `weights.shape={weights.shape}`" - f" and `x.shape={x.shape}`." + "`weights` must have the same size as `x` at " + f"`axis={self.axis}` but received " + f"`weights.shape={weights.shape}` while x.shape at " + f"`{self.axis}` is `{x.shape[self.axis]}`." ) - # `weights` can either be a 1D array of length `x.shape[axis]` or - # of the same shape as `x`. - raise ValueError( - "`weights` must have the same size as `x` at " - f"`axis={self.axis}` but received " - f"`weights.shape={weights.shape}` while x.shape at " - f"`{self.axis}` is `{x.shape[self.axis]}`." - ) - @keras_export(["keras.ops.average", "keras.ops.numpy.average"]) def average(x, axis=None, weights=None): From 7332a05931e2aa4fa7ec1e9d37421fdfe34f756c Mon Sep 17 00:00:00 2001 From: Samuel Marks <807580+SamuelMarks@users.noreply.github.com> Date: Wed, 6 Dec 2023 11:07:03 -0500 Subject: [PATCH 5/8] [keras/{callbacks/reduce_lr_on_plateau,layers/preprocessing/{center_crop_test,random_crop_test} ops/operation}.py] Resolve issues found through `flake8 --config setup.cfg .` --- keras/callbacks/reduce_lr_on_plateau.py | 2 +- keras/layers/preprocessing/center_crop_test.py | 2 -- keras/layers/preprocessing/random_crop_test.py | 2 -- keras/ops/operation.py | 4 ++-- 4 files changed, 3 insertions(+), 7 deletions(-) diff --git a/keras/callbacks/reduce_lr_on_plateau.py b/keras/callbacks/reduce_lr_on_plateau.py index c5a7f8eec75..2c8552bf61a 100644 --- a/keras/callbacks/reduce_lr_on_plateau.py +++ b/keras/callbacks/reduce_lr_on_plateau.py @@ -138,7 +138,7 @@ def on_epoch_end(self, epoch, logs=None): self.model.optimizer.learning_rate = new_lr if self.verbose > 0: io_utils.print_msg( - f"\nEpoch {epoch +1}: " + f"\nEpoch {epoch + 1}: " "ReduceLROnPlateau reducing " f"learning rate to {new_lr}." ) diff --git a/keras/layers/preprocessing/center_crop_test.py b/keras/layers/preprocessing/center_crop_test.py index cce45af13b1..aaafe4bb3d4 100644 --- a/keras/layers/preprocessing/center_crop_test.py +++ b/keras/layers/preprocessing/center_crop_test.py @@ -164,10 +164,8 @@ def test_input_smaller_than_crop_box(self, size, data_format): def test_tf_data_compatibility(self): if backend.config.image_data_format() == "channels_last": input_shape = (2, 10, 12, 3) - output_shape = (2, 8, 9, 3) else: input_shape = (2, 3, 10, 12) - output_shape = (2, 3, 8, 9) layer = layers.CenterCrop(8, 9) input_data = np.random.random(input_shape) ds = tf_data.Dataset.from_tensor_slices(input_data).batch(2).map(layer) diff --git a/keras/layers/preprocessing/random_crop_test.py b/keras/layers/preprocessing/random_crop_test.py index bac02355971..55388ae1f77 100644 --- a/keras/layers/preprocessing/random_crop_test.py +++ b/keras/layers/preprocessing/random_crop_test.py @@ -92,10 +92,8 @@ def test_tf_data_compatibility(self): layer = layers.RandomCrop(8, 9) if backend.config.image_data_format() == "channels_last": input_shape = (2, 10, 12, 3) - output_shape = (2, 8, 9, 3) else: input_shape = (2, 3, 10, 12) - output_shape = (2, 3, 8, 9) input_data = np.random.random(input_shape) ds = tf_data.Dataset.from_tensor_slices(input_data).batch(2).map(layer) output = next(iter(ds)).numpy() diff --git a/keras/ops/operation.py b/keras/ops/operation.py index c9ff620cb6e..4395d50ac1a 100644 --- a/keras/ops/operation.py +++ b/keras/ops/operation.py @@ -169,8 +169,8 @@ def __init__(self, arg1, arg2, **kwargs): def get_config(self): config = super().get_config() config.update({ - "arg1": self.arg1, - "arg2": self.arg2, + "arg1": self.arg1, + "arg2": self.arg2, }) return config""" ) From 5ee7555fa29e3d0262f67abf6accd175c088ea62 Mon Sep 17 00:00:00 2001 From: Samuel Marks <807580+SamuelMarks@users.noreply.github.com> Date: Fri, 11 Oct 2024 15:25:47 -0500 Subject: [PATCH 6/8] [keras/src/layers/preprocessing/image_preprocessing/center_crop_test.py] Return `output_shape` to being dependent on `image_data_format` on `test_tf_data_compatibility` --- .../preprocessing/image_preprocessing/center_crop_test.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/keras/src/layers/preprocessing/image_preprocessing/center_crop_test.py b/keras/src/layers/preprocessing/image_preprocessing/center_crop_test.py index 1867add8c70..d34ba7b0eb5 100644 --- a/keras/src/layers/preprocessing/image_preprocessing/center_crop_test.py +++ b/keras/src/layers/preprocessing/image_preprocessing/center_crop_test.py @@ -164,13 +164,15 @@ def test_input_smaller_than_crop_box(self, size, data_format): def test_tf_data_compatibility(self): if backend.config.image_data_format() == "channels_last": input_shape = (2, 10, 12, 3) + output_shape = (2, 8, 9, 3) else: input_shape = (2, 3, 10, 12) + output_shape = (2, 3, 8, 9) layer = layers.CenterCrop(8, 9) input_data = np.random.random(input_shape) ds = tf_data.Dataset.from_tensor_slices(input_data).batch(2).map(layer) output = next(iter(ds)).numpy() - self.assertEqual(list(output.shape), [2, 8, 9, 3]) + self.assertEqual(list(output.shape), output_shape) # TODO # def test_list_compatibility(self): From a3bd38d5bd5dede0f48c7421324854b362c0fd82 Mon Sep 17 00:00:00 2001 From: Samuel Marks <807580+SamuelMarks@users.noreply.github.com> Date: Fri, 11 Oct 2024 15:27:51 -0500 Subject: [PATCH 7/8] [keras/src/layers/preprocessing/image_preprocessing/random_crop_test.py] Return `output_shape` to being dependent on `image_data_format` on `test_tf_data_compatibility` --- .../preprocessing/image_preprocessing/random_crop_test.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/keras/src/layers/preprocessing/image_preprocessing/random_crop_test.py b/keras/src/layers/preprocessing/image_preprocessing/random_crop_test.py index a53d86cf22e..b38f41845dd 100644 --- a/keras/src/layers/preprocessing/image_preprocessing/random_crop_test.py +++ b/keras/src/layers/preprocessing/image_preprocessing/random_crop_test.py @@ -130,12 +130,14 @@ def test_tf_data_compatibility(self): layer = layers.RandomCrop(8, 9) if backend.config.image_data_format() == "channels_last": input_shape = (2, 10, 12, 3) + output_shape = (2, 8, 9, 3) else: input_shape = (2, 3, 10, 12) + output_shape = (2, 3, 8, 9) input_data = np.random.random(input_shape) ds = tf_data.Dataset.from_tensor_slices(input_data).batch(2).map(layer) output = next(iter(ds)).numpy() - self.assertEqual(list(output.shape), [2, 8, 9, 3]) + self.assertEqual(list(output.shape), output_shape) def test_dict_input(self): layer = layers.RandomCrop( From f2542e9339d6c0badd0ca220d4aa395d86c1c07b Mon Sep 17 00:00:00 2001 From: Samuel Marks <807580+SamuelMarks@users.noreply.github.com> Date: Fri, 11 Oct 2024 15:38:28 -0500 Subject: [PATCH 8/8] [keras/src/layers/preprocessing/image_preprocessing/{center,random}_crop_test.py] Make tuple `output.shape` for comparison against `output_shape` from `image_data_format` on `test_tf_data_compatibility` --- .../preprocessing/image_preprocessing/center_crop_test.py | 2 +- .../preprocessing/image_preprocessing/random_crop_test.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/keras/src/layers/preprocessing/image_preprocessing/center_crop_test.py b/keras/src/layers/preprocessing/image_preprocessing/center_crop_test.py index d34ba7b0eb5..7eaa32e08bd 100644 --- a/keras/src/layers/preprocessing/image_preprocessing/center_crop_test.py +++ b/keras/src/layers/preprocessing/image_preprocessing/center_crop_test.py @@ -172,7 +172,7 @@ def test_tf_data_compatibility(self): input_data = np.random.random(input_shape) ds = tf_data.Dataset.from_tensor_slices(input_data).batch(2).map(layer) output = next(iter(ds)).numpy() - self.assertEqual(list(output.shape), output_shape) + self.assertEqual(tuple(output.shape), output_shape) # TODO # def test_list_compatibility(self): diff --git a/keras/src/layers/preprocessing/image_preprocessing/random_crop_test.py b/keras/src/layers/preprocessing/image_preprocessing/random_crop_test.py index b38f41845dd..c4796a2b224 100644 --- a/keras/src/layers/preprocessing/image_preprocessing/random_crop_test.py +++ b/keras/src/layers/preprocessing/image_preprocessing/random_crop_test.py @@ -137,7 +137,7 @@ def test_tf_data_compatibility(self): input_data = np.random.random(input_shape) ds = tf_data.Dataset.from_tensor_slices(input_data).batch(2).map(layer) output = next(iter(ds)).numpy() - self.assertEqual(list(output.shape), output_shape) + self.assertEqual(tuple(output.shape), output_shape) def test_dict_input(self): layer = layers.RandomCrop(