Skip to content

Commit 198512f

Browse files
authored
Merge branch 'keras-team:master' into master
2 parents 73314e2 + 4d30a7f commit 198512f

File tree

6 files changed

+138
-7
lines changed

6 files changed

+138
-7
lines changed

keras/src/layers/regularization/dropout.py

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,55 @@ def __init__(self, rate, noise_shape=None, seed=None, **kwargs):
4848
)
4949
self.rate = rate
5050
self.seed = seed
51-
self.noise_shape = noise_shape
51+
self.noise_shape = self._validate_noise_shape(noise_shape)
5252
if rate > 0:
5353
self.seed_generator = backend.random.SeedGenerator(seed)
5454
self.supports_masking = True
5555

5656
self._build_at_init()
5757

58+
def _validate_noise_shape(self, noise_shape):
59+
if noise_shape is None:
60+
return None
61+
62+
if isinstance(noise_shape, str):
63+
raise ValueError(
64+
f"Invalid value received for argument `noise_shape`. "
65+
f"Expected a tuple or list of integers. "
66+
f"Received: noise_shape={noise_shape}"
67+
)
68+
69+
if not isinstance(noise_shape, tuple):
70+
try:
71+
noise_shape = tuple(noise_shape)
72+
except TypeError:
73+
raise ValueError(
74+
f"Invalid value received for argument `noise_shape`. "
75+
f"Expected an iterable of integers "
76+
f"(e.g., a tuple or list). "
77+
f"Received: noise_shape={noise_shape}"
78+
)
79+
80+
for i, dim in enumerate(noise_shape):
81+
if dim is not None:
82+
if not isinstance(dim, int):
83+
raise ValueError(
84+
f"Invalid value received for argument `noise_shape`. "
85+
f"Expected all elements to be integers or None. "
86+
f"Received element at index {i}: {dim} "
87+
f"(type: {type(dim).__name__})"
88+
)
89+
90+
if dim <= 0:
91+
raise ValueError(
92+
f"Invalid value received for argument `noise_shape`. "
93+
f"Expected all dimensions to be positive integers "
94+
f"or None. "
95+
f"Received negative or zero value at index {i}: {dim}"
96+
)
97+
98+
return noise_shape
99+
58100
def call(self, inputs, training=False):
59101
if training and self.rate > 0:
60102
return backend.random.dropout(

keras/src/layers/regularization/dropout_test.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,52 @@ def test_dropout_rate_greater_than_one(self):
6060
"Expected a float value between 0 and 1.",
6161
):
6262
_ = layers.Dropout(rate=1.5)
63+
64+
def test_validate_noise_shape_none(self):
65+
layer = layers.Dropout(0.5, noise_shape=None)
66+
self.assertIsNone(layer.noise_shape)
67+
68+
def test_validate_noise_shape_integer_tuple(self):
69+
layer = layers.Dropout(0.5, noise_shape=(20, 1, 10))
70+
self.assertEqual(layer.noise_shape, (20, 1, 10))
71+
72+
def test_validate_noise_shape_none_values(self):
73+
layer = layers.Dropout(0.5, noise_shape=(None, 1, None))
74+
self.assertEqual(layer.noise_shape, (None, 1, None))
75+
76+
def test_validate_noise_shape_cast_to_a_tuple(self):
77+
layer = layers.Dropout(0.5, noise_shape=[20, 1, 10])
78+
self.assertEqual(layer.noise_shape, (20, 1, 10))
79+
self.assertIsInstance(layer.noise_shape, tuple)
80+
81+
def test_validate_noise_shape_non_iterable(self):
82+
with self.assertRaisesRegex(
83+
ValueError,
84+
"Invalid value received for argument `noise_shape`. "
85+
"Expected a tuple or list of integers.",
86+
):
87+
layers.Dropout(0.5, noise_shape="Invalid")
88+
89+
def test_validate_noise_shape_invalid_type(self):
90+
with self.assertRaisesRegex(
91+
ValueError,
92+
"Invalid value received for argument `noise_shape`. "
93+
"Expected all elements to be integers or None.",
94+
):
95+
layers.Dropout(0.5, noise_shape=(20, 1.5, 10))
96+
97+
def test_validate_noise_shape_negative_value(self):
98+
with self.assertRaisesRegex(
99+
ValueError,
100+
"Invalid value received for argument `noise_shape`. "
101+
"Expected all dimensions to be positive integers or None.",
102+
):
103+
layers.Dropout(0.5, noise_shape=(20, -1, 10))
104+
105+
def test_validate_noise_shape_zero_value(self):
106+
with self.assertRaisesRegex(
107+
ValueError,
108+
"Invalid value received for argument `noise_shape`. "
109+
"Expected all dimensions to be positive integers or None.",
110+
):
111+
layers.Dropout(0.5, noise_shape=(20, 0, 10))

keras/src/optimizers/schedules/learning_rate_schedule.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -584,9 +584,10 @@ class CosineDecay(LearningRateSchedule):
584584
schedule applies a linear increase per optimizer step to our learning rate
585585
from `initial_learning_rate` to `warmup_target` for a duration of
586586
`warmup_steps`. Afterwards, it applies a cosine decay function taking our
587-
learning rate from `warmup_target` to `alpha` for a duration of
588-
`decay_steps`. If `warmup_target` is None we skip warmup and our decay
589-
will take our learning rate from `initial_learning_rate` to `alpha`.
587+
learning rate from `warmup_target` to `warmup_target * alpha` for a
588+
duration of `decay_steps`. If `warmup_target` is None we skip warmup and
589+
our decay will take our learning rate from `initial_learning_rate` to
590+
`initial_learning_rate * alpha`.
590591
It requires a `step` value to compute the learning rate. You can
591592
just pass a backend variable that you increment at each training step.
592593

keras/src/utils/progbar.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import sys
44
import time
55

6+
import numpy as np
7+
68
from keras.src.api_export import keras_export
79
from keras.src.utils import io_utils
810

@@ -161,7 +163,10 @@ def update(self, current, values=None, finalize=None):
161163
for k in self._values_order:
162164
info += f" - {k}:"
163165
if isinstance(self._values[k], list):
164-
avg = self._values[k][0] / max(1, self._values[k][1])
166+
values, count = self._values[k]
167+
if not isinstance(values, float):
168+
values = np.mean(values)
169+
avg = values / max(1, count)
165170
if abs(avg) > 1e-3:
166171
info += f" {avg:.4f}"
167172
else:
@@ -188,7 +193,10 @@ def update(self, current, values=None, finalize=None):
188193
info += f" -{self._format_time(time_per_unit, self.unit_name)}"
189194
for k in self._values_order:
190195
info += f" - {k}:"
191-
avg = self._values[k][0] / max(1, self._values[k][1])
196+
values, count = self._values[k]
197+
if not isinstance(values, float):
198+
values = np.mean(values)
199+
avg = values / max(1, count)
192200
if avg > 1e-3:
193201
info += f" {avg:.4f}"
194202
else:

keras/src/utils/progbar_test.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import numpy as np
2+
from absl.testing import parameterized
3+
4+
from keras.src import testing
5+
from keras.src.utils import progbar
6+
7+
8+
class ProgbarTest(testing.TestCase):
9+
@parameterized.named_parameters(
10+
[
11+
("float", "float"),
12+
("np", "np"),
13+
("list", "list"),
14+
]
15+
)
16+
def test_update(self, value_type):
17+
if value_type == "float":
18+
values = 1.0
19+
elif value_type == "np":
20+
values = np.array(1.0)
21+
elif value_type == "list":
22+
values = [0.0, 1.0, 2.0]
23+
else:
24+
raise ValueError("Unknown value_type")
25+
pb = progbar.Progbar(target=1, verbose=1)
26+
27+
pb.update(1, values=[("values", values)], finalize=True)

keras/src/wrappers/sklearn_test.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Tests using Scikit-Learn's bundled estimator_checks."""
22

3+
import unittest
34
from contextlib import contextmanager
45

56
import pytest
@@ -59,7 +60,7 @@ def patched_more_tags(self):
5960

6061
def dynamic_model(X, y, loss, layers=[10]):
6162
"""Creates a basic MLP classifier dynamically choosing binary/multiclass
62-
classification loss and ouput activations.
63+
classification loss and output activations.
6364
"""
6465
n_features_in = X.shape[1]
6566
inp = Input(shape=(n_features_in,))
@@ -156,5 +157,8 @@ def test_sklearn_estimator_checks(estimator, check):
156157
or "NotImplementedError" in str(exc)
157158
):
158159
pytest.xfail("Backend not implemented")
160+
elif isinstance(exc, unittest.SkipTest):
161+
# Workaround for https://github.com/pytest-dev/pytest/issues/13895
162+
pytest.skip(str(exc))
159163
else:
160164
raise

0 commit comments

Comments
 (0)