Repository: keras-team/keras

Issue:
Function _create_seed() in keras.backend causes DeprecationWarning in Python 3.10
In [line 2032 of keras.backend.py](https://github.com/keras-team/keras/blob/af1408d3255e3db9067522762e22a6c454c56654/keras/backend.py#L2032) we call random.randint(1, 1e9), which causes DeprecationWarning in Python 3.10 and will raise an error in some future version of Python. I suggest to replace it with `random.randint(1, int(1e9))` or `random.randint(1, 1000000000)`.


Discussion:
What would you think of adding support for `border_mode = same` instead?

I'd love to, but don't know how to do it. Can you help me figure it out?

I believe Lasagne supports it. Looking at the source, this seems to be the relevant bit: https://github.com/Lasagne/Lasagne/blob/master/lasagne/layers/conv.py#L441

Basically, setting border_mode to full and resizing the output after the fact. Very simple and straightforward.

Ok, thanks for the help. This is basically what the CropImage function did. Let me know what you think now and if you would do anything differently

Looks good, I updated the doc and the standardized the style. It's merged.

Thanks for the change!
Wouldn't it be less computationally intensive to pad with zeros before
calling conv2d and then use a _valid_ border_mode (perhaps by using the
newly ZeroPadding2D Layer?

On Thu, Jul 16, 2015 at 8:20 AM, François Chollet notifications@github.com
wrote:

> Looks good, I updated the doc and the standardized the style. It's merged.
> 
> —
> Reply to this email directly or view it on GitHub
> https://github.com/fchollet/keras/pull/396#issuecomment-121831833.


Resolving patch:
diff --git a/keras/backend.py b/keras/backend.py
--- a/keras/backend.py
+++ b/keras/backend.py
@@ -2029,7 +2029,7 @@ def _create_seed(self, user_specified_seed):
         elif getattr(_SEED_GENERATOR, "generator", None):
             return _SEED_GENERATOR.generator.randint(1, 1e9)
         else:
-            return random.randint(1, 1e9)
+            return random.randint(1, int(1e9))
 
     def random_normal(
         self, shape, mean=0.0, stddev=1.0, dtype=None, nonce=None
diff --git a/keras/engine/training_v1.py b/keras/engine/training_v1.py
--- a/keras/engine/training_v1.py
+++ b/keras/engine/training_v1.py
@@ -1756,10 +1756,15 @@ def _prepare_total_loss(self, masks):
                             ) = losses_utils.squeeze_or_expand_dimensions(
                                 mask, sample_weight=sample_weight
                             )
-                            sample_weight *= mask
 
                     if hasattr(loss_fn, "reduction"):
                         per_sample_losses = loss_fn.call(y_true, y_pred)
+                        sample_weight = losses_utils.apply_valid_mask(
+                            per_sample_losses,
+                            sample_weight,
+                            mask,
+                            loss_fn.reduction,
+                        )
                         weighted_losses = losses_utils.compute_weighted_loss(
                             per_sample_losses,
                             sample_weight=sample_weight,
diff --git a/keras/losses.py b/keras/losses.py
--- a/keras/losses.py
+++ b/keras/losses.py
@@ -148,8 +148,21 @@ def __call__(self, y_true, y_pred, sample_weight=None):
                 call_fn = tf.__internal__.autograph.tf_convert(
                     self.call, tf.__internal__.autograph.control_status_ctx()
                 )
+
             losses = call_fn(y_true, y_pred)
-            mask = losses_utils.get_mask(losses)
+
+            in_mask = losses_utils.get_mask(y_pred)
+            out_mask = losses_utils.get_mask(losses)
+
+            if in_mask is not None and out_mask is not None:
+                mask = in_mask & out_mask
+            elif in_mask is not None:
+                mask = in_mask
+            elif out_mask is not None:
+                mask = out_mask
+            else:
+                mask = None
+
             reduction = self._get_reduction()
             sample_weight = losses_utils.apply_valid_mask(
                 losses, sample_weight, mask, reduction


Test patch:
diff --git a/keras/engine/compile_utils_test.py b/keras/engine/compile_utils_test.py
--- a/keras/engine/compile_utils_test.py
+++ b/keras/engine/compile_utils_test.py
@@ -294,19 +294,74 @@ def my_mae(labels, preds):
         self.assertIsInstance(total_loss, tf.Tensor)
         self.assertEqual(total_loss.dtype, tf.float64)
 
+    @test_combinations.generate(
+        test_combinations.combine(
+            input_type=["dense", "masked", "ragged"],
+            reduction=["auto", "sum"],
+            use_sample_weights=[True, False],
+        ),
+    )
+    def test_loss_consistency(self, input_type, reduction, use_sample_weights):
+        y_p = tf.ragged.constant(
+            [[[1], [1], [1]], [[1], [1]]], dtype=tf.float32
+        )
+        y_t = tf.ragged.constant(
+            [[[1], [0], [0]], [[1], [1]]], dtype=tf.float32
+        )
+
+        if input_type == "masked":
+            mask = tf.ones_like(y_p).to_tensor()
+            y_p = y_p.to_tensor()
+            y_t = y_t.to_tensor()
+            y_p._keras_mask = mask
+        elif input_type == "dense":
+            y_p = y_p.to_tensor()
+            y_t = y_t.to_tensor()
+
+        if input_type == "dense":
+            count = 6
+        else:
+            count = 5
+
+        if use_sample_weights:
+            wrong = 4
+            maybe_sample_weight = {
+                "sample_weight": tf.constant([[2], [1]], dtype=tf.float32)
+            }
+        else:
+            wrong = 2
+            maybe_sample_weight = {}
+
+        expected = wrong
+        if reduction != "sum":
+            expected /= count
+
+        loss_obj = losses_mod.MeanAbsoluteError(reduction=reduction)
+
+        result = loss_obj(y_t, y_p, **maybe_sample_weight)
+        self.assertAlmostEqual(result.numpy(), expected)
+
+        container = compile_utils.LossesContainer(loss_obj)
+        container_result = container(y_t, y_p, **maybe_sample_weight)
+        self.assertAlmostEqual(container_result.numpy(), expected)
+
     def test_loss_masking(self):
         loss_container = compile_utils.LossesContainer("mae")
         y_p = tf.constant([[[1], [1]], [[0], [0]]], dtype=tf.float32)
         y_t = tf.constant([[[1], [1]], [[1], [1]]], dtype=tf.float32)
+        # Reduction is "sum_over_batch_size" that's not the literal batch size,
+        # but the number of elements being summed: The number of valid
+        # emlements. So since the mask has two valid items, the number of
+        # elements is 2.
         y_p._keras_mask = tf.constant([[1, 0], [1, 0]], dtype=tf.float32)
 
         total_loss = loss_container(y_t, y_p)
-        self.assertAlmostEqual(total_loss.numpy(), 0.25)  # sum over batch size
+        self.assertAlmostEqual(total_loss.numpy(), 0.5)  # sum over num valid
 
         self.assertLen(loss_container.metrics, 1)
         loss_metric = loss_container.metrics[0]
         self.assertEqual(loss_metric.name, "loss")
-        self.assertAlmostEqual(loss_metric.result().numpy(), 0.25)
+        self.assertAlmostEqual(loss_metric.result().numpy(), 0.5)
 
     def test_loss_sample_weight(self):
         loss_container = compile_utils.LossesContainer("mae")
@@ -331,13 +386,13 @@ def test_loss_masking_sample_weight(self):
         y_p._keras_mask = tf.constant([[1, 0], [1, 0]], dtype=tf.float32)
 
         total_loss = loss_container(y_t, y_p, sample_weight=sw)
-        # (0 * .2 + 1 * .5) / 4
-        self.assertAlmostEqual(total_loss.numpy(), 0.125)  # sum over batch size
+        # (0 * .2 + 1 * .5) / 2
+        self.assertAlmostEqual(total_loss.numpy(), 0.25)  # sum over num valid
 
         self.assertLen(loss_container.metrics, 1)
         loss_metric = loss_container.metrics[0]
         self.assertEqual(loss_metric.name, "loss")
-        self.assertAlmostEqual(loss_metric.result().numpy(), 0.125)
+        self.assertAlmostEqual(loss_metric.result().numpy(), 0.25)
 
     def test_custom_loss_callables(self):
         def custom_loss_fn(y_true, y_pred):
diff --git a/keras/engine/training_test.py b/keras/engine/training_test.py
--- a/keras/engine/training_test.py
+++ b/keras/engine/training_test.py
@@ -3732,7 +3732,9 @@ def test_metrics_masking(self):
         model.add(layers_module.Masking(mask_value=0, input_shape=(2, 1)))
         model.add(
             layers_module.TimeDistributed(
-                layers_module.Dense(1, kernel_initializer="ones")
+                layers_module.Dense(
+                    1, kernel_initializer="ones", trainable=False
+                )
             )
         )
         model.compile(
@@ -3743,7 +3745,10 @@ def test_metrics_masking(self):
         )
 
         # verify that masking is applied.
-        x = np.array([[[1], [1]], [[1], [1]], [[0], [0]]])
+        x = np.array(
+            # third row is masked
+            [[[1], [1]], [[1], [1]], [[0], [0]]]
+        )
         y = np.array([[[1], [1]], [[0], [1]], [[1], [1]]])
         scores = model.train_on_batch(x, y)
         self.assertArrayNear(scores, [0.25, 0.75], 0.1)
@@ -3751,7 +3756,7 @@ def test_metrics_masking(self):
         # verify that masking is combined with sample weights.
         w = np.array([3, 2, 4])
         scores = model.train_on_batch(x, y, sample_weight=w)
-        self.assertArrayNear(scores, [0.3328, 0.8], 0.001)
+        self.assertArrayNear(scores, [0.5, 0.8], 0.001)
 
     @test_combinations.run_all_keras_modes
     def test_add_metric_with_tensor_on_model(self):