Skip to content

Commit 4cbcdf8

Browse files
committed
Merge branch 'master' into keras.utils-defaults-to
# Conflicts: # keras/utils/audio_dataset.py # keras/utils/image_dataset.py
2 parents 1a192af + 70a217d commit 4cbcdf8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+1013
-529
lines changed

.devcontainer/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM mcr.microsoft.com/vscode/devcontainers/python:3.8
1+
FROM mcr.microsoft.com/vscode/devcontainers/python:3.9
22
COPY setup.sh /setup.sh
33

44
# Install Bazel

keras/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,6 @@
2828
from tensorflow.python import tf2
2929
from tensorflow.python.util.tf_export import keras_export
3030

31-
__version__ = "2.13.0"
31+
__version__ = "2.14.0"
3232

3333
keras_export("keras.__version__").export_constant(__name__, "__version__")

keras/constraints.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
"""Constraints: functions that impose constraints on weight values."""
1818

19+
import warnings
20+
1921
import tensorflow.compat.v2 as tf
2022

2123
from keras import backend
@@ -357,6 +359,13 @@ def body_fn(i, array):
357359

358360
@keras_export("keras.constraints.serialize")
359361
def serialize(constraint, use_legacy_format=False):
362+
if not isinstance(constraint, Constraint):
363+
warnings.warn(
364+
"The `keras.constraints.serialize()` API should only be used for "
365+
"objects of type `keras.constraints.Constraint`. Found an instance "
366+
f"of type {type(constraint)}, which may lead to improper "
367+
"serialization."
368+
)
360369
if use_legacy_format:
361370
return legacy_serialization.serialize_keras_object(constraint)
362371
return serialize_keras_object(constraint)

keras/datasets/reuters.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,13 @@ def load_data(
6969
skip_top: skip the top N most frequently occurring words
7070
(which may not be informative). These words will appear as
7171
`oov_char` value in the dataset. 0 means no words are
72-
skipped. Defaults to 0
72+
skipped. Defaults to `0`.
7373
maxlen: int or None. Maximum sequence length.
7474
Any longer sequence will be truncated. None means no truncation.
7575
Defaults to `None`.
76-
test_split: Float between 0 and 1. Fraction of the dataset to be used
77-
as test data. 0.2 means that 20% of the dataset is used as
78-
test data. Defaults to 0.2
76+
test_split: Float between `0.` and `1.`. Fraction of the dataset to be
77+
used as test data. `0.2` means that 20% of the dataset is used as
78+
test data. Defaults to `0.2`.
7979
seed: int. Seed for reproducible data shuffling.
8080
start_char: int. The start of a sequence will be marked with this
8181
character. 0 is usually the padding character. Defaults to `1`.

keras/distribute/distributed_file_utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def write_dirpath(dirpath, strategy):
8484
The writing dir path that should be used to save with distribution.
8585
"""
8686
if strategy is None:
87-
# Infer strategy from `distribution_strategy_context` if not given.
87+
# Infer strategy from `tf.distribute` if not given.
8888
strategy = tf.distribute.get_strategy()
8989
if strategy is None:
9090
# If strategy is still not available, this is not in distributed
@@ -107,7 +107,7 @@ def remove_temp_dirpath(dirpath, strategy):
107107
strategy: The tf.distribute strategy object currently used.
108108
"""
109109
if strategy is None:
110-
# Infer strategy from `distribution_strategy_context` if not given.
110+
# Infer strategy from `tf.distribute` if not given.
111111
strategy = tf.distribute.get_strategy()
112112
if strategy is None:
113113
# If strategy is still not available, this is not in distributed

keras/engine/deferred_sequential_test.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,23 @@ def test_feature_extraction(self):
120120
# Check that inputs and outputs are connected
121121
_ = extractor(np.random.random((4, 6)))
122122

123+
@test_combinations.run_all_keras_modes(always_skip_v1=True)
124+
def test_saving_keras_v3(self):
125+
model = get_model()
126+
model(np.random.random((3, 6))) # Build model
127+
128+
path = os.path.join(self.get_temp_dir(), "model_path.keras")
129+
model.save(path)
130+
new_model = keras.models.load_model(path)
131+
model_layers = model._flatten_layers(include_self=True, recursive=False)
132+
new_model_layers = new_model._flatten_layers(
133+
include_self=True, recursive=False
134+
)
135+
for layer1, layer2 in zip(model_layers, new_model_layers):
136+
self.assertEqual(layer1.name, layer2.name)
137+
for w1, w2 in zip(layer1.weights, layer2.weights):
138+
self.assertAllClose(w1, w2)
139+
123140
@test_combinations.run_all_keras_modes(always_skip_v1=True)
124141
def test_saving_savedmodel(self):
125142
model = get_model()

keras/engine/functional_test.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
from keras.engine import input_layer as input_layer_lib
2929
from keras.engine import sequential
3030
from keras.engine import training as training_lib
31+
from keras.saving import object_registration
3132
from keras.saving.legacy import save
3233
from keras.testing_infra import test_combinations
3334
from keras.testing_infra import test_utils
@@ -1875,7 +1876,7 @@ def test_external_keras_serialization_compat_input_layers(self):
18751876
test_combinations.combine(mode=["graph", "eager"])
18761877
)
18771878
@test_utils.run_v2_only
1878-
def test_save_load_with_single_elem_list_inputs(self):
1879+
def test_save_load_with_single_elem_list_inputs_saved_model(self):
18791880
class MyLayer(layers.Layer):
18801881
def __init__(self):
18811882
super().__init__()
@@ -1893,6 +1894,26 @@ def call(self, inputs):
18931894

18941895
save.load_model("/tmp/km2")
18951896

1897+
@test_utils.run_v2_only
1898+
def test_save_load_with_single_elem_list_inputs_keras_v3(self):
1899+
@object_registration.register_keras_serializable()
1900+
class MyLayer(layers.Layer):
1901+
def __init__(self):
1902+
super().__init__()
1903+
self._preserve_input_structure_in_config = True
1904+
1905+
def call(self, inputs):
1906+
return inputs[0]
1907+
1908+
inputs = input_layer_lib.Input(shape=(3,))
1909+
layer = MyLayer()
1910+
outputs = layer([inputs])
1911+
1912+
model = training_lib.Model(inputs=inputs, outputs=outputs)
1913+
model.save("/tmp/model.keras")
1914+
1915+
models.load_model("/tmp/model.keras")
1916+
18961917
@test_combinations.generate(
18971918
test_combinations.combine(mode=["graph", "eager"])
18981919
)

keras/engine/functional_utils_test.py

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -151,11 +151,6 @@ def test_build_model_from_intermediate_tensor(self):
151151
model.fit(
152152
np.random.randn(batch_size, 32), np.random.randn(batch_size, 16)
153153
)
154-
# Test for model saving
155-
output_path = os.path.join(self.get_temp_dir(), "tf_keras_saved_model")
156-
model.save(output_path, save_format="tf")
157-
loaded_model = models.load_model(output_path)
158-
self.assertEqual(model.summary(), loaded_model.summary())
159154

160155
# Also make sure the original inputs and y can still be used to build
161156
# model
@@ -167,6 +162,27 @@ def test_build_model_from_intermediate_tensor(self):
167162
self.assertIs(new_model.layers[1], layer1)
168163
self.assertIs(new_model.layers[2], layer2)
169164

165+
# Test for model saving
166+
with self.subTest("savedmodel"):
167+
output_path = os.path.join(
168+
self.get_temp_dir(), "tf_keras_saved_model"
169+
)
170+
model.save(output_path, save_format="tf")
171+
loaded_model = models.load_model(output_path)
172+
self.assertEqual(model.summary(), loaded_model.summary())
173+
174+
with self.subTest("keras_v3"):
175+
if not tf.__internal__.tf2.enabled():
176+
self.skipTest(
177+
"TF2 must be enabled to use the new `.keras` saving."
178+
)
179+
output_path = os.path.join(
180+
self.get_temp_dir(), "tf_keras_v3_model.keras"
181+
)
182+
model.save(output_path, save_format="keras_v3")
183+
loaded_model = models.load_model(output_path)
184+
self.assertEqual(model.summary(), loaded_model.summary())
185+
170186
def test_build_model_from_intermediate_tensor_with_complicated_model(self):
171187
# The topology is like below:
172188
# input1 -> dense1 -> a
@@ -212,10 +228,6 @@ def test_build_model_from_intermediate_tensor_with_complicated_model(self):
212228
],
213229
np.random.randn(batch_size, 8),
214230
)
215-
output_path = os.path.join(self.get_temp_dir(), "tf_keras_saved_model")
216-
model.save(output_path, save_format="tf")
217-
loaded_model = models.load_model(output_path)
218-
self.assertEqual(model.summary(), loaded_model.summary())
219231

220232
model2 = models.Model([a, b], d)
221233
# 2 input layers and 2 Add layer.
@@ -230,6 +242,26 @@ def test_build_model_from_intermediate_tensor_with_complicated_model(self):
230242
np.random.randn(batch_size, 8),
231243
)
232244

245+
with self.subTest("savedmodel"):
246+
output_path = os.path.join(
247+
self.get_temp_dir(), "tf_keras_saved_model"
248+
)
249+
model.save(output_path, save_format="tf")
250+
loaded_model = models.load_model(output_path)
251+
self.assertEqual(model.summary(), loaded_model.summary())
252+
253+
with self.subTest("keras_v3"):
254+
if not tf.__internal__.tf2.enabled():
255+
self.skipTest(
256+
"TF2 must be enabled to use the new `.keras` saving."
257+
)
258+
output_path = os.path.join(
259+
self.get_temp_dir(), "tf_keras_v3_model.keras"
260+
)
261+
model.save(output_path, save_format="keras_v3")
262+
loaded_model = models.load_model(output_path)
263+
self.assertEqual(model.summary(), loaded_model.summary())
264+
233265

234266
if __name__ == "__main__":
235267
tf.test.main()

keras/initializers/__init__.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"""Keras initializer serialization / deserialization."""
1616

1717
import threading
18+
import warnings
1819

1920
import tensorflow.compat.v2 as tf
2021

@@ -136,6 +137,14 @@ def populate_deserializable_objects():
136137

137138
@keras_export("keras.initializers.serialize")
138139
def serialize(initializer, use_legacy_format=False):
140+
populate_deserializable_objects()
141+
if not isinstance(initializer, tuple(LOCAL.ALL_OBJECTS.values())):
142+
warnings.warn(
143+
"The `keras.initializers.serialize()` API should only be used for "
144+
"objects of type `keras.initializers.Initializer`. Found an "
145+
f"instance of type {type(initializer)}, which may lead to improper "
146+
"serialization."
147+
)
139148
if use_legacy_format:
140149
return legacy_serialization.serialize_keras_object(initializer)
141150

keras/layers/activation/leaky_relu.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ class LeakyReLU(Layer):
5454
Same shape as the input.
5555
5656
Args:
57-
alpha: Float >= 0. Negative slope coefficient. Defaults to `0.3`.
57+
alpha: Float >= `0.`. Negative slope coefficient. Defaults to `0.3`.
5858
5959
"""
6060

0 commit comments

Comments
 (0)