Skip to content

Commit

Permalink
Merge branch 'master' into keras.utils-defaults-to
Browse files Browse the repository at this point in the history
# Conflicts:
#	keras/utils/audio_dataset.py
#	keras/utils/image_dataset.py
  • Loading branch information
SamuelMarks committed May 4, 2023
2 parents 1a192af + 70a217d commit 4cbcdf8
Show file tree
Hide file tree
Showing 49 changed files with 1,013 additions and 529 deletions.
2 changes: 1 addition & 1 deletion .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM mcr.microsoft.com/vscode/devcontainers/python:3.8
FROM mcr.microsoft.com/vscode/devcontainers/python:3.9
COPY setup.sh /setup.sh

# Install Bazel
Expand Down
2 changes: 1 addition & 1 deletion keras/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,6 @@
from tensorflow.python import tf2
from tensorflow.python.util.tf_export import keras_export

__version__ = "2.13.0"
__version__ = "2.14.0"

keras_export("keras.__version__").export_constant(__name__, "__version__")
9 changes: 9 additions & 0 deletions keras/constraints.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

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

import warnings

import tensorflow.compat.v2 as tf

from keras import backend
Expand Down Expand Up @@ -357,6 +359,13 @@ def body_fn(i, array):

@keras_export("keras.constraints.serialize")
def serialize(constraint, use_legacy_format=False):
if not isinstance(constraint, Constraint):
warnings.warn(
"The `keras.constraints.serialize()` API should only be used for "
"objects of type `keras.constraints.Constraint`. Found an instance "
f"of type {type(constraint)}, which may lead to improper "
"serialization."
)
if use_legacy_format:
return legacy_serialization.serialize_keras_object(constraint)
return serialize_keras_object(constraint)
Expand Down
8 changes: 4 additions & 4 deletions keras/datasets/reuters.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,13 @@ def load_data(
skip_top: skip the top N most frequently occurring words
(which may not be informative). These words will appear as
`oov_char` value in the dataset. 0 means no words are
skipped. Defaults to 0
skipped. Defaults to `0`.
maxlen: int or None. Maximum sequence length.
Any longer sequence will be truncated. None means no truncation.
Defaults to `None`.
test_split: Float between 0 and 1. Fraction of the dataset to be used
as test data. 0.2 means that 20% of the dataset is used as
test data. Defaults to 0.2
test_split: Float between `0.` and `1.`. Fraction of the dataset to be
used as test data. `0.2` means that 20% of the dataset is used as
test data. Defaults to `0.2`.
seed: int. Seed for reproducible data shuffling.
start_char: int. The start of a sequence will be marked with this
character. 0 is usually the padding character. Defaults to `1`.
Expand Down
4 changes: 2 additions & 2 deletions keras/distribute/distributed_file_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def write_dirpath(dirpath, strategy):
The writing dir path that should be used to save with distribution.
"""
if strategy is None:
# Infer strategy from `distribution_strategy_context` if not given.
# Infer strategy from `tf.distribute` if not given.
strategy = tf.distribute.get_strategy()
if strategy is None:
# If strategy is still not available, this is not in distributed
Expand All @@ -107,7 +107,7 @@ def remove_temp_dirpath(dirpath, strategy):
strategy: The tf.distribute strategy object currently used.
"""
if strategy is None:
# Infer strategy from `distribution_strategy_context` if not given.
# Infer strategy from `tf.distribute` if not given.
strategy = tf.distribute.get_strategy()
if strategy is None:
# If strategy is still not available, this is not in distributed
Expand Down
17 changes: 17 additions & 0 deletions keras/engine/deferred_sequential_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,23 @@ def test_feature_extraction(self):
# Check that inputs and outputs are connected
_ = extractor(np.random.random((4, 6)))

@test_combinations.run_all_keras_modes(always_skip_v1=True)
def test_saving_keras_v3(self):
model = get_model()
model(np.random.random((3, 6))) # Build model

path = os.path.join(self.get_temp_dir(), "model_path.keras")
model.save(path)
new_model = keras.models.load_model(path)
model_layers = model._flatten_layers(include_self=True, recursive=False)
new_model_layers = new_model._flatten_layers(
include_self=True, recursive=False
)
for layer1, layer2 in zip(model_layers, new_model_layers):
self.assertEqual(layer1.name, layer2.name)
for w1, w2 in zip(layer1.weights, layer2.weights):
self.assertAllClose(w1, w2)

@test_combinations.run_all_keras_modes(always_skip_v1=True)
def test_saving_savedmodel(self):
model = get_model()
Expand Down
23 changes: 22 additions & 1 deletion keras/engine/functional_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from keras.engine import input_layer as input_layer_lib
from keras.engine import sequential
from keras.engine import training as training_lib
from keras.saving import object_registration
from keras.saving.legacy import save
from keras.testing_infra import test_combinations
from keras.testing_infra import test_utils
Expand Down Expand Up @@ -1875,7 +1876,7 @@ def test_external_keras_serialization_compat_input_layers(self):
test_combinations.combine(mode=["graph", "eager"])
)
@test_utils.run_v2_only
def test_save_load_with_single_elem_list_inputs(self):
def test_save_load_with_single_elem_list_inputs_saved_model(self):
class MyLayer(layers.Layer):
def __init__(self):
super().__init__()
Expand All @@ -1893,6 +1894,26 @@ def call(self, inputs):

save.load_model("/tmp/km2")

@test_utils.run_v2_only
def test_save_load_with_single_elem_list_inputs_keras_v3(self):
@object_registration.register_keras_serializable()
class MyLayer(layers.Layer):
def __init__(self):
super().__init__()
self._preserve_input_structure_in_config = True

def call(self, inputs):
return inputs[0]

inputs = input_layer_lib.Input(shape=(3,))
layer = MyLayer()
outputs = layer([inputs])

model = training_lib.Model(inputs=inputs, outputs=outputs)
model.save("/tmp/model.keras")

models.load_model("/tmp/model.keras")

@test_combinations.generate(
test_combinations.combine(mode=["graph", "eager"])
)
Expand Down
50 changes: 41 additions & 9 deletions keras/engine/functional_utils_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,11 +151,6 @@ def test_build_model_from_intermediate_tensor(self):
model.fit(
np.random.randn(batch_size, 32), np.random.randn(batch_size, 16)
)
# Test for model saving
output_path = os.path.join(self.get_temp_dir(), "tf_keras_saved_model")
model.save(output_path, save_format="tf")
loaded_model = models.load_model(output_path)
self.assertEqual(model.summary(), loaded_model.summary())

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

# Test for model saving
with self.subTest("savedmodel"):
output_path = os.path.join(
self.get_temp_dir(), "tf_keras_saved_model"
)
model.save(output_path, save_format="tf")
loaded_model = models.load_model(output_path)
self.assertEqual(model.summary(), loaded_model.summary())

with self.subTest("keras_v3"):
if not tf.__internal__.tf2.enabled():
self.skipTest(
"TF2 must be enabled to use the new `.keras` saving."
)
output_path = os.path.join(
self.get_temp_dir(), "tf_keras_v3_model.keras"
)
model.save(output_path, save_format="keras_v3")
loaded_model = models.load_model(output_path)
self.assertEqual(model.summary(), loaded_model.summary())

def test_build_model_from_intermediate_tensor_with_complicated_model(self):
# The topology is like below:
# input1 -> dense1 -> a
Expand Down Expand Up @@ -212,10 +228,6 @@ def test_build_model_from_intermediate_tensor_with_complicated_model(self):
],
np.random.randn(batch_size, 8),
)
output_path = os.path.join(self.get_temp_dir(), "tf_keras_saved_model")
model.save(output_path, save_format="tf")
loaded_model = models.load_model(output_path)
self.assertEqual(model.summary(), loaded_model.summary())

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

with self.subTest("savedmodel"):
output_path = os.path.join(
self.get_temp_dir(), "tf_keras_saved_model"
)
model.save(output_path, save_format="tf")
loaded_model = models.load_model(output_path)
self.assertEqual(model.summary(), loaded_model.summary())

with self.subTest("keras_v3"):
if not tf.__internal__.tf2.enabled():
self.skipTest(
"TF2 must be enabled to use the new `.keras` saving."
)
output_path = os.path.join(
self.get_temp_dir(), "tf_keras_v3_model.keras"
)
model.save(output_path, save_format="keras_v3")
loaded_model = models.load_model(output_path)
self.assertEqual(model.summary(), loaded_model.summary())


if __name__ == "__main__":
tf.test.main()
9 changes: 9 additions & 0 deletions keras/initializers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"""Keras initializer serialization / deserialization."""

import threading
import warnings

import tensorflow.compat.v2 as tf

Expand Down Expand Up @@ -136,6 +137,14 @@ def populate_deserializable_objects():

@keras_export("keras.initializers.serialize")
def serialize(initializer, use_legacy_format=False):
populate_deserializable_objects()
if not isinstance(initializer, tuple(LOCAL.ALL_OBJECTS.values())):
warnings.warn(
"The `keras.initializers.serialize()` API should only be used for "
"objects of type `keras.initializers.Initializer`. Found an "
f"instance of type {type(initializer)}, which may lead to improper "
"serialization."
)
if use_legacy_format:
return legacy_serialization.serialize_keras_object(initializer)

Expand Down
2 changes: 1 addition & 1 deletion keras/layers/activation/leaky_relu.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class LeakyReLU(Layer):
Same shape as the input.
Args:
alpha: Float >= 0. Negative slope coefficient. Defaults to `0.3`.
alpha: Float >= `0.`. Negative slope coefficient. Defaults to `0.3`.
"""

Expand Down
21 changes: 18 additions & 3 deletions keras/layers/attention/multi_head_attention_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from absl.testing import parameterized

import keras
from keras.saving import object_registration
from keras.testing_infra import test_combinations
from keras.testing_infra import test_utils

Expand Down Expand Up @@ -515,6 +516,7 @@ def test_initializer(self):
self.assertEqual(output.shape.as_list(), [None, 40, 80])


@object_registration.register_keras_serializable()
class TestModel(keras.Model):
def __init__(self):
super().__init__()
Expand All @@ -540,12 +542,19 @@ def call(self, x, training=False):

@test_combinations.run_all_keras_modes(always_skip_v1=True)
class KerasModelSavingTest(test_combinations.TestCase):
def test_keras_saving_subclass(self):
@parameterized.parameters("tf", "keras_v3")
def test_keras_saving_subclass(self, save_format):
model = TestModel()
query = keras.Input(shape=(40, 80))
_ = model(query)
model_path = self.get_temp_dir() + "/tmp_model"
keras.models.save_model(model, model_path, save_format="tf")
if save_format == "keras_v3":
if not tf.__internal__.tf2.enabled():
self.skipTest(
"TF2 must be enabled to use the new `.keras` saving."
)
model_path += ".keras"
keras.models.save_model(model, model_path, save_format=save_format)
reloaded_model = keras.models.load_model(model_path)
self.assertEqual(
len(model.trainable_variables),
Expand All @@ -556,7 +565,7 @@ def test_keras_saving_subclass(self):
):
self.assertAllEqual(src_v, loaded_v)

@parameterized.parameters("h5", "tf")
@parameterized.parameters("h5", "tf", "keras_v3")
def test_keras_saving_functional(self, save_format):
model = TestModel()
query = keras.Input(shape=(40, 80))
Expand All @@ -565,6 +574,12 @@ def test_keras_saving_functional(self, save_format):
)(query, query)
model = keras.Model(inputs=query, outputs=output)
model_path = self.get_temp_dir() + "/tmp_model"
if save_format == "keras_v3":
if not tf.__internal__.tf2.enabled():
self.skipTest(
"TF2 must be enabled to use the new `.keras` saving."
)
model_path += ".keras"
keras.models.save_model(model, model_path, save_format=save_format)
reloaded_model = keras.models.load_model(model_path)
self.assertEqual(
Expand Down
4 changes: 2 additions & 2 deletions keras/layers/convolutional/separable_conv2d.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,13 +185,13 @@ def call(self, inputs):
strides = (1,) + self.strides + (1,)
else:
strides = (1, 1) + self.strides
outputs = tf.compat.v1.nn.separable_conv2d(
outputs = tf.nn.separable_conv2d(
inputs,
self.depthwise_kernel,
self.pointwise_kernel,
strides=strides,
padding=self.padding.upper(),
rate=self.dilation_rate,
dilations=self.dilation_rate,
data_format=conv_utils.convert_data_format(
self.data_format, ndim=4
),
Expand Down
Loading

0 comments on commit 4cbcdf8

Please sign in to comment.