Skip to content

Commit e7c4d09

Browse files
nkovela1tensorflower-gardener
authored andcommitted
Expands Keras internal testing coverage for the new v3 saving format for common tests.
PiperOrigin-RevId: 527732364
1 parent d72829a commit e7c4d09

File tree

7 files changed

+208
-67
lines changed

7 files changed

+208
-67
lines changed

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/layers/core/core_test.py

Lines changed: 47 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ def test_dropout_partial_noise_shape(self):
8989
# Test that dropout mask is shared across second dim.
9090
self.assertAllClose(out_np[:, 0, :], out_np[:, 1, :])
9191

92-
def test_dropout_with_savemodel(self):
92+
def test_dropout_with_saving(self):
9393
inputs = keras.Input(shape=(5, 10))
9494
layer = keras.layers.Dropout(0.5, force_generator=True)
9595
outputs = layer(inputs)
@@ -105,32 +105,52 @@ def test_dropout_with_savemodel(self):
105105
# Make sure the layer does dropout value when training
106106
self.assertNotAllClose(train, predict)
107107

108-
model.save(
109-
os.path.join(self.get_temp_dir(), "savedmodel"), save_format="tf"
110-
)
111-
loaded_model = keras.models.load_model(
112-
os.path.join(self.get_temp_dir(), "savedmodel")
113-
)
114-
predict2 = loaded_model(np.ones((20, 5, 10)))
115-
116-
self.assertAllClose(predict, predict2)
117-
# Make sure the model dropout different value after loading
118-
train2 = loaded_model(np.ones((20, 5, 10)), training=True)
119-
self.assertNotAllClose(train, train2)
120-
self.assertIsNotNone(loaded_model.layers[1]._random_generator)
121-
122-
# Also make sure the checkpoint doesn't contain any variable from the
123-
# dropout layer, to keep the backward compatibility.
124-
checkpoint = tf.train.Checkpoint(model)
125-
save_path = checkpoint.save(
126-
os.path.join(self.get_temp_dir(), "checkpoint")
127-
)
128-
checkpoint_var_names = [
129-
name_value_tuple[0]
130-
for name_value_tuple in tf.train.list_variables(save_path)
131-
]
132-
for name in checkpoint_var_names:
133-
self.assertNotIn("dropout", name)
108+
with self.subTest("savedmodel"):
109+
model.save(
110+
os.path.join(self.get_temp_dir(), "savedmodel"),
111+
save_format="tf",
112+
)
113+
loaded_model = keras.models.load_model(
114+
os.path.join(self.get_temp_dir(), "savedmodel")
115+
)
116+
predict2 = loaded_model(np.ones((20, 5, 10)))
117+
118+
self.assertAllClose(predict, predict2)
119+
# Make sure the model dropout different value after loading
120+
train2 = loaded_model(np.ones((20, 5, 10)), training=True)
121+
self.assertNotAllClose(train, train2)
122+
self.assertIsNotNone(loaded_model.layers[1]._random_generator)
123+
124+
with self.subTest("keras_v3"):
125+
if not tf.__internal__.tf2.enabled():
126+
self.skipTest(
127+
"TF2 must be enabled to use the new `.keras` saving."
128+
)
129+
model.save(os.path.join(self.get_temp_dir(), "model.keras"))
130+
loaded_model = keras.models.load_model(
131+
os.path.join(self.get_temp_dir(), "model.keras")
132+
)
133+
predict2 = loaded_model(np.ones((20, 5, 10)))
134+
135+
self.assertAllClose(predict, predict2)
136+
# Make sure the model dropout different value after loading
137+
train2 = loaded_model(np.ones((20, 5, 10)), training=True)
138+
self.assertNotAllClose(train, train2)
139+
self.assertIsNotNone(loaded_model.layers[1]._random_generator)
140+
141+
with self.subTest("checkpoint"):
142+
# Also make sure the checkpoint doesn't contain any variable from
143+
# the dropout layer, to keep the backward compatibility.
144+
checkpoint = tf.train.Checkpoint(model)
145+
save_path = checkpoint.save(
146+
os.path.join(self.get_temp_dir(), "checkpoint")
147+
)
148+
checkpoint_var_names = [
149+
name_value_tuple[0]
150+
for name_value_tuple in tf.train.list_variables(save_path)
151+
]
152+
for name in checkpoint_var_names:
153+
self.assertNotIn("dropout", name)
134154

135155

136156
@test_combinations.run_all_keras_modes

keras/models/sharpness_aware_minimization_test.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -109,12 +109,21 @@ def test_save_sam(self):
109109

110110
sam_model.fit(data, label)
111111

112-
path = os.path.join(self.get_temp_dir(), "model")
113-
sam_model.save(path)
114-
loaded_sam_model = keras.models.load_model(path)
115-
loaded_sam_model.load_weights(path)
112+
with self.subTest("savedmodel"):
113+
path = os.path.join(self.get_temp_dir(), "model")
114+
sam_model.save(path)
115+
loaded_sam_model = keras.models.load_model(path)
116+
loaded_sam_model.load_weights(path)
116117

117-
self.assertAllClose(sam_model(data), loaded_sam_model(data))
118+
self.assertAllClose(sam_model(data), loaded_sam_model(data))
119+
120+
with self.subTest("keras_v3"):
121+
path = os.path.join(self.get_temp_dir(), "model.keras")
122+
sam_model.save(path)
123+
loaded_sam_model = keras.models.load_model(path)
124+
loaded_sam_model.load_weights(path)
125+
126+
self.assertAllClose(sam_model(data), loaded_sam_model(data))
118127

119128
def test_checkpoint_sam(self):
120129
model = keras.Sequential(

keras/optimizers/optimizer_test.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,17 @@ def testSaveAndLoadOptimizerWithModel(self, optimizer_fn):
527527
loaded_optimizer.build(loaded_model.trainable_variables)
528528
self.assertAllClose(optimizer.variables, loaded_optimizer.variables)
529529

530+
# Save in `.keras` format.
531+
path = os.path.join(self.get_temp_dir(), "model.keras")
532+
model.save(path)
533+
loaded_model = keras.models.load_model(path)
534+
loaded_model.load_weights(path)
535+
loaded_optimizer = loaded_model.optimizer
536+
self.assertEqual(type(optimizer), type(loaded_optimizer))
537+
self.assertEqual(loaded_optimizer.learning_rate, 0.002)
538+
self.assertEqual(loaded_optimizer.clipnorm, 0.1)
539+
self.assertAllClose(optimizer.variables, loaded_optimizer.variables)
540+
530541
@parameterized.product(optimizer_fn=OPTIMIZER_FN)
531542
def testSparseGradientsWorkAsExpected(self, optimizer_fn):
532543
optimizer_1 = optimizer_fn()

0 commit comments

Comments
 (0)