Repository: keras-team/keras

Issue:
Reopen PRs from tensorflow/tensorflow
I had three (albeit small) PRs in tensorflow/tensorflow. All of them were reverted due to some internal checks, which were not available to the end user back then. For more info, see [#49201](https://github.com/tensorflow/tensorflow/issues/49201). Should I reopen these PRs here? 

Links to PRs:
[#48610](https://github.com/tensorflow/tensorflow/pull/48610), [#48000](https://github.com/tensorflow/tensorflow/pull/48000), [#48491](https://github.com/tensorflow/tensorflow/pull/48491)
Unable to reproduce build errors in local environments.
I am currently working on #15315 in this repo. However, I am not able to reproduce the build errors neither in my local Windows environment nor in a Colab notebook. I have followed the instructions from "Option 2" in [CONTRIBUTING.md](https://github.com/keras-team/keras/blob/master/CONTRIBUTING.md#option-2-setup-a-local-environment). Here is the [gist](https://colab.research.google.com/gist/AdityaKane2001/4497f4297b68ff142e995b5731e3215d/15315.ipynb).

Here is the main issue:
```python
!apt-get install python3.7-dev python3.7-venv
!mkdir keras_env
!python3 -m venv keras_env
!source keras_env/bin/activate

BAZEL_VERSION = '4.2.1'
!wget https://github.com/bazelbuild/bazel/releases/download/{BAZEL_VERSION}/bazel-{BAZEL_VERSION}-installer-linux-x86_64.sh

!chmod +x bazel-{BAZEL_VERSION}-installer-linux-x86_64.sh
!./bazel-{BAZEL_VERSION}-installer-linux-x86_64.sh
!bazel

!git clone -b reopening_PR https://github.com/AdityaKane2001/keras.git
%cd keras
!bazel test keras/layers/convolutional_test
```

The above code writes the output to some files in cache:
```python
!cat  /root/.cache/bazel/_bazel_root/26321c5950fe518aef514f9fb14403c6/execroot/org_keras/bazel-out/k8-opt/testlogs/keras/layers/convolutional_test/shard_5_of_8/test.log

# Output
# exec ${PAGER:-/usr/bin/less} "$0" || exit 1
# Executing tests from //keras/layers:convolutional_test
# -----------------------------------------------------------------------------
# 2021-09-09 03:05:12.281707: E tensorflow/core/lib/monitoring/collection_registry.cc:77] Cannot register 2 metrics with the same name: /tensorflow/api/keras/optimizers
# Traceback (most recent call last):
#
# (Long error trace)
#
#   File "/usr/local/lib/python3.7/dist-packages/tensorflow/python/eager/monitoring.py", line 135, in __init__
#     self._metric = self._metric_methods[self._label_length].create(*args)
# tensorflow.python.framework.errors_impl.AlreadyExistsError: Another metric with the same name already exists.
```

whereas the [invocation log](https://source.cloud.google.com/results/invocations/4aa12df0-e509-479b-97fa-5787e2101ad2/log) gives correct errors. 


Discussion:
@AdityaKane2001 Thanks for the issue. Yes. please reopen them here.

@chenmoneygithub Would you please help him merge the PRs? And let me know if anything I can help. Thanks.
@haifeng-jin 
Thanks for the response. I'll reopen all the PRs combined into one tomorrow (it's around midnight at my place :P).
This issue was resolved by reinstalling python and venv on Colab.

Resolving patch:
diff --git a/keras/layers/convolutional.py b/keras/layers/convolutional.py
--- a/keras/layers/convolutional.py
+++ b/keras/layers/convolutional.py
@@ -193,6 +193,8 @@ def build(self, input_shape):
     kernel_shape = self.kernel_size + (input_channel // self.groups,
                                        self.filters)
 
+    output_shape = self.compute_output_shape(input_shape)
+
     self.kernel = self.add_weight(
         name='kernel',
         shape=kernel_shape,
@@ -284,15 +286,24 @@ def _spatial_output_shape(self, spatial_input_shape):
   def compute_output_shape(self, input_shape):
     input_shape = tf.TensorShape(input_shape).as_list()
     batch_rank = len(input_shape) - self.rank - 1
-    if self.data_format == 'channels_last':
-      return tf.TensorShape(
-          input_shape[:batch_rank]
-          + self._spatial_output_shape(input_shape[batch_rank:-1])
-          + [self.filters])
-    else:
-      return tf.TensorShape(
-          input_shape[:batch_rank] + [self.filters] +
-          self._spatial_output_shape(input_shape[batch_rank + 1:]))
+    try:
+      if self.data_format == 'channels_last':
+        return tf.TensorShape(
+            input_shape[:batch_rank]
+            + self._spatial_output_shape(input_shape[batch_rank:-1])
+            + [self.filters])
+      else:
+        return tf.TensorShape(
+            input_shape[:batch_rank] + [self.filters] +
+            self._spatial_output_shape(input_shape[batch_rank + 1:]))
+
+    except ValueError:
+        raise ValueError(f'One of the dimensions in the output is <= 0 '
+                         f'due to downsampling in {self.name}. Consider '
+                         f'increasing the input size. '
+                         f'Received input shape {input_shape} which would produce '
+                         f'output shape with a zero or negative value in a '
+                         f'dimension.')
 
   def _recreate_conv_op(self, inputs):  # pylint: disable=unused-argument
     return False
diff --git a/keras/layers/local.py b/keras/layers/local.py
--- a/keras/layers/local.py
+++ b/keras/layers/local.py
@@ -172,6 +172,14 @@ def build(self, input_shape):
                                                        self.padding,
                                                        self.strides[0])
 
+    if self.output_length <= 0:
+      raise ValueError(f'One of the dimensions in the output is <= 0 '
+                       f'due to downsampling in {self.name}. Consider '
+                       f'increasing the input size. '
+                       f'Received input shape {input_shape} which would produce '
+                       f'output shape with a zero or negative value in a '
+                       f'dimension.')
+
     if self.implementation == 1:
       self.kernel_shape = (self.output_length, self.kernel_size[0] * input_dim,
                            self.filters)
@@ -483,6 +491,14 @@ def build(self, input_shape):
     self.output_row = output_row
     self.output_col = output_col
 
+    if self.output_row <= 0 or self.output_col <= 0:
+      raise ValueError(f'One of the dimensions in the output is <= 0 '
+                       f'due to downsampling in {self.name}. Consider '
+                       f'increasing the input size. '
+                       f'Received input shape {input_shape} which would produce '
+                       f'output shape with a zero or negative value in a '
+                       f'dimension.')
+
     if self.implementation == 1:
       self.kernel_shape = (output_row * output_col, self.kernel_size[0] *
                            self.kernel_size[1] * input_filter, self.filters)


Test patch:
diff --git a/keras/layers/convolutional_test.py b/keras/layers/convolutional_test.py
--- a/keras/layers/convolutional_test.py
+++ b/keras/layers/convolutional_test.py
@@ -161,6 +161,13 @@ def fn(inpt):
       fn(inpt2)
       self.assertEqual(outp1_shape, layer(inpt1).shape)
 
+  def test_conv1d_invalid_output_shapes(self):
+    kwargs = {'filters': 2, 'kernel_size': 20}
+    with self.assertRaisesRegex(ValueError,
+                                r"""One of the dimensions in the output is <= 0"""):
+      layer = keras.layers.convolutional.Conv1D(**kwargs)
+      layer.build((None, 5, 2))
+
 
 @keras_parameterized.run_all_keras_modes
 class Conv2DTest(keras_parameterized.TestCase):
@@ -292,6 +299,13 @@ def test_conv2d_zero_kernel_size(self):
     kwargs = {'filters': 2, 'kernel_size': 0}
     with self.assertRaises(ValueError):
       keras.layers.Conv2D(**kwargs)
+  
+  def test_conv2d_invalid_output_shapes(self):
+    kwargs = {'filters': 2, 'kernel_size': 20}
+    with self.assertRaisesRegex(ValueError,
+                                r"""One of the dimensions in the output is <= 0"""):
+      layer = keras.layers.convolutional.Conv2D(**kwargs)
+      layer.build((None, 5, 5, 2))
 
 
 @keras_parameterized.run_all_keras_modes
@@ -428,6 +442,13 @@ def test_conv3d_dynamic_shape(self):
             input_shape=(None, 3, None, None, None),
             input_data=input_data)
 
+  def test_conv3d_invalid_output_shapes(self):
+    kwargs = {'filters': 2, 'kernel_size': 20}
+    with self.assertRaisesRegex(ValueError,
+                                r"""One of the dimensions in the output is <= 0"""):
+      layer = keras.layers.convolutional.Conv3D(**kwargs)
+      layer.build((None, 5, 5, 5, 2))
+
 
 @keras_parameterized.run_all_keras_modes(always_skip_v1=True)
 class GroupedConvTest(keras_parameterized.TestCase):
diff --git a/keras/layers/local_test.py b/keras/layers/local_test.py
--- a/keras/layers/local_test.py
+++ b/keras/layers/local_test.py
@@ -157,6 +157,12 @@ def test_locallyconnected_1d_regularization(self, data_format, padding,
         self.assertEqual(layer.kernel.constraint, k_constraint)
         self.assertEqual(layer.bias.constraint, b_constraint)
 
+  def test_locallyconnected1d_invalid_output_shapes(self):
+    kwargs = {'filters': 2, 'kernel_size': 10}
+    with self.assertRaisesRegex(ValueError,
+                                r"""One of the dimensions in the output is <= 0 """):
+      layer = keras.layers.LocallyConnected1D(**kwargs)
+      layer.build((None, 5, 2))
 
 @combinations.generate(combinations.combine(mode=['graph', 'eager']))
 class LocallyConnected2DLayersTest(tf.test.TestCase, parameterized.TestCase):
@@ -264,6 +270,12 @@ def test_locallyconnected_2d_regularization(self, data_format, padding,
         self.assertEqual(layer.kernel.constraint, k_constraint)
         self.assertEqual(layer.bias.constraint, b_constraint)
 
+  def test_locallyconnected2d_invalid_output_shapes(self):
+    kwargs = {'filters': 2, 'kernel_size': 10}
+    with self.assertRaisesRegex(ValueError,
+                                r"""One of the dimensions in the output is <= 0 """):
+      layer = keras.layers.LocallyConnected2D(**kwargs)
+      layer.build((None, 5, 5, 2))
 
 @combinations.generate(combinations.combine(mode=['graph', 'eager']))
 class LocallyConnectedImplementationModeTest(tf.test.TestCase,
@@ -283,8 +295,19 @@ def test_locallyconnected_implementation(self, width, data_format):
 
       np.random.seed(1)
       tf_test_util.random_seed.set_seed(1)
-      targets = np.random.randint(0, num_classes, (num_samples,))
-
+      # Following code generates sparse targets and converts them
+      # to one-hot encoded vectors
+      # Create sparse targets eg. [0,1,2]
+      sparse_targets = np.random.randint(0, num_classes, (num_samples,))
+
+      # Convert to one-hot encoding
+      # Final targets:
+      # [[ 1. 0. 0. ]
+      #  [ 0. 1. 0. ]
+      #  [ 0. 0. 1. ]]
+
+      targets = np.zeros((sparse_targets.size, num_classes))
+      targets[np.arange(sparse_targets.size), sparse_targets] = 1
       height = 7
       filters = 2
       inputs = get_inputs(data_format, filters, height, num_samples, width)
@@ -387,7 +410,19 @@ def test_locallyconnected_save(self, width, data_format):
 
       np.random.seed(1)
       tf_test_util.random_seed.set_seed(1)
-      targets = np.random.randint(0, num_classes, (num_samples,))
+      # Following code generates sparse targets and converts them
+      # to one-hot encoded vectors
+      # Create sparse targets eg. [0,1,2]
+      sparse_targets = np.random.randint(0, num_classes, (num_samples,))
+
+      # Convert to one-hot encoding
+      # Final targets:
+      # [[ 1. 0. 0. ]
+      #  [ 0. 1. 0. ]
+      #  [ 0. 0. 1. ]]
+
+      targets = np.zeros((sparse_targets.size, num_classes))
+      targets[np.arange(sparse_targets.size), sparse_targets] = 1
 
       height = 7
       filters = 2
@@ -555,7 +590,7 @@ def get_model(implementation,
   model.compile(
       optimizer=RMSPropOptimizer(0.01),
       metrics=[keras.metrics.categorical_accuracy],
-      loss=xent
+      loss=keras.losses.CategoricalCrossentropy(from_logits=True)
   )
   return model
 
@@ -589,7 +624,7 @@ def get_model_saveable(implementation, filters, kernel_size, strides, layers,
   model.compile(
       optimizer=rmsprop.RMSProp(learning_rate=0.01),
       metrics=[keras.metrics.categorical_accuracy],
-      loss=xent)
+      loss=keras.losses.CategoricalCrossentropy(from_logits=True))
   return model