Repository: keras-team/keras (python)

Issue:
Issue loading model containing Dense layer with Identity initializer
Hi, I've been migrating some model code from Keras 2 to Keras 3, and I think I stumbled upon a bug.

I've noticed that if I save a model containing a dense layer with an identity kernel initializer in Keras 3+, I cannot reload the model. I get an exception that says Keras cannot interpret the initializer identifier in config.json in the Keras model file.
A snippet from the exception (rest at the bottom of this post):

```Exception encountered: Could not interpret initializer identifier: {'module': 'keras.initializers', 'class_name': 'IdentityInitializer', 'config': {}, 'registered_name': None}```

I have tried this out on Windows using Keras 3.6 + Jax 0.4.35 and Ubuntu using Keras 3.6 + Tensorflow 2.18.0, and I get the same exception.

Currently, I am using a hacky workaround to replace the faulty 'IdentityInitializer' string in the config.json in the Keras model file with 'Identity'

Here are pytest tests for replicating this issue (see test_save_read_dense_layer_model_with_identity_initializer for replicating this exception, see attached
[testoutput.txt](https://github.com/user-attachments/files/17710057/testoutput.txt)
 file for test output which includes the full exception traceback):

```python
from pathlib import Path
from tempfile import TemporaryDirectory
from zipfile import ZipFile

import keras


def load_model_with_workaround(model_path: Path) -> keras.Model:
    with (
        TemporaryDirectory() as tmp_dir,
        ZipFile(model_path, "r") as original_model_file,
    ):
        new_model_path = Path(tmp_dir) / "new.keras"
        with ZipFile(new_model_path, "w") as new_model_file:
            for file_name in original_model_file.namelist():
                original_data = original_model_file.read(file_name)

                if file_name == "config.json":
                    original_data = (
                        original_data.decode("utf-8")
                        .replace(
                            'class_name": "IdentityInitializer"',
                            'class_name": "Identity"',
                        )
                        .encode("utf-8")
                    )

                with new_model_file.open(file_name, "w") as f:
                    f.write(original_data)

        return keras.models.load_model(new_model_path)


# This test will fail
def test_save_read_dense_layer_model_with_identity_initializer() -> None:
    model = keras.Sequential(
        [
            keras.layers.Input((5,)),
            keras.layers.Dense(5, kernel_initializer=keras.initializers.Identity()),
        ]
    )
    with TemporaryDirectory() as tmp_dir:
        save_path = Path(tmp_dir) / "mymodel.keras"
        model.save(save_path)
        model_from_file = keras.models.load_model(save_path)


# This test will pass
def test_save_read_dense_layer_model_with_identity_initializer_using_workaround() -> None:
    model = keras.Sequential(
        [
            keras.layers.Input((5,)),
            keras.layers.Dense(5, kernel_initializer=keras.initializers.Identity()),
        ]
    )
    with TemporaryDirectory() as tmp_dir:
        save_path = Path(tmp_dir) / "mymodel.keras"
        model.save(save_path)
        model_from_file = load_model_with_workaround(save_path)

```


Pull request description:
Improve the consistency of the names of initializers
Fix #20483 

This PR renames `OrthogonalInitializer` to `Orthogonal` and `STFTInitializer` to `STFT` for name consistency and adds serialization tests to prevent future breakages.

This PR also adds a default value `side="real"` to support calls to `initializers.get("STFT")`

Patch:
diff --git a/keras/api/_tf_keras/keras/initializers/__init__.py b/keras/api/_tf_keras/keras/initializers/__init__.py
index 405fa1142..9b49190c4 100644
--- a/keras/api/_tf_keras/keras/initializers/__init__.py
+++ b/keras/api/_tf_keras/keras/initializers/__init__.py
@@ -7,6 +7,9 @@ since your modifications would be overwritten.
 from keras.src.initializers import deserialize
 from keras.src.initializers import get
 from keras.src.initializers import serialize
+from keras.src.initializers.constant_initializers import STFT
+from keras.src.initializers.constant_initializers import STFT as STFTInitializer
+from keras.src.initializers.constant_initializers import STFT as stft
 from keras.src.initializers.constant_initializers import Constant
 from keras.src.initializers.constant_initializers import Constant as constant
 from keras.src.initializers.constant_initializers import Identity
@@ -16,7 +19,6 @@ from keras.src.initializers.constant_initializers import (
 from keras.src.initializers.constant_initializers import Identity as identity
 from keras.src.initializers.constant_initializers import Ones
 from keras.src.initializers.constant_initializers import Ones as ones
-from keras.src.initializers.constant_initializers import STFTInitializer
 from keras.src.initializers.constant_initializers import Zeros
 from keras.src.initializers.constant_initializers import Zeros as zeros
 from keras.src.initializers.initializer import Initializer
@@ -40,13 +42,11 @@ from keras.src.initializers.random_initializers import LecunUniform
 from keras.src.initializers.random_initializers import (
     LecunUniform as lecun_uniform,
 )
-from keras.src.initializers.random_initializers import OrthogonalInitializer
+from keras.src.initializers.random_initializers import Orthogonal
 from keras.src.initializers.random_initializers import (
-    OrthogonalInitializer as Orthogonal,
-)
-from keras.src.initializers.random_initializers import (
-    OrthogonalInitializer as orthogonal,
+    Orthogonal as OrthogonalInitializer,
 )
+from keras.src.initializers.random_initializers import Orthogonal as orthogonal
 from keras.src.initializers.random_initializers import RandomNormal
 from keras.src.initializers.random_initializers import (
     RandomNormal as random_normal,
diff --git a/keras/api/initializers/__init__.py b/keras/api/initializers/__init__.py
index 405fa1142..9b49190c4 100644
--- a/keras/api/initializers/__init__.py
+++ b/keras/api/initializers/__init__.py
@@ -7,6 +7,9 @@ since your modifications would be overwritten.
 from keras.src.initializers import deserialize
 from keras.src.initializers import get
 from keras.src.initializers import serialize
+from keras.src.initializers.constant_initializers import STFT
+from keras.src.initializers.constant_initializers import STFT as STFTInitializer
+from keras.src.initializers.constant_initializers import STFT as stft
 from keras.src.initializers.constant_initializers import Constant
 from keras.src.initializers.constant_initializers import Constant as constant
 from keras.src.initializers.constant_initializers import Identity
@@ -16,7 +19,6 @@ from keras.src.initializers.constant_initializers import (
 from keras.src.initializers.constant_initializers import Identity as identity
 from keras.src.initializers.constant_initializers import Ones
 from keras.src.initializers.constant_initializers import Ones as ones
-from keras.src.initializers.constant_initializers import STFTInitializer
 from keras.src.initializers.constant_initializers import Zeros
 from keras.src.initializers.constant_initializers import Zeros as zeros
 from keras.src.initializers.initializer import Initializer
@@ -40,13 +42,11 @@ from keras.src.initializers.random_initializers import LecunUniform
 from keras.src.initializers.random_initializers import (
     LecunUniform as lecun_uniform,
 )
-from keras.src.initializers.random_initializers import OrthogonalInitializer
+from keras.src.initializers.random_initializers import Orthogonal
 from keras.src.initializers.random_initializers import (
-    OrthogonalInitializer as Orthogonal,
-)
-from keras.src.initializers.random_initializers import (
-    OrthogonalInitializer as orthogonal,
+    Orthogonal as OrthogonalInitializer,
 )
+from keras.src.initializers.random_initializers import Orthogonal as orthogonal
 from keras.src.initializers.random_initializers import RandomNormal
 from keras.src.initializers.random_initializers import (
     RandomNormal as random_normal,
diff --git a/keras/src/initializers/__init__.py b/keras/src/initializers/__init__.py
index fc943cbee..7223f5029 100644
--- a/keras/src/initializers/__init__.py
+++ b/keras/src/initializers/__init__.py
@@ -5,10 +5,10 @@ import numpy as np
 from keras.src import backend
 from keras.src import ops
 from keras.src.api_export import keras_export
+from keras.src.initializers.constant_initializers import STFT
 from keras.src.initializers.constant_initializers import Constant
 from keras.src.initializers.constant_initializers import Identity
 from keras.src.initializers.constant_initializers import Ones
-from keras.src.initializers.constant_initializers import STFTInitializer
 from keras.src.initializers.constant_initializers import Zeros
 from keras.src.initializers.initializer import Initializer
 from keras.src.initializers.random_initializers import GlorotNormal
@@ -17,7 +17,7 @@ from keras.src.initializers.random_initializers import HeNormal
 from keras.src.initializers.random_initializers import HeUniform
 from keras.src.initializers.random_initializers import LecunNormal
 from keras.src.initializers.random_initializers import LecunUniform
-from keras.src.initializers.random_initializers import OrthogonalInitializer
+from keras.src.initializers.random_initializers import Orthogonal
 from keras.src.initializers.random_initializers import RandomNormal
 from keras.src.initializers.random_initializers import RandomUniform
 from keras.src.initializers.random_initializers import TruncatedNormal
@@ -30,7 +30,7 @@ ALL_OBJECTS = {
     Constant,
     Identity,
     Ones,
-    STFTInitializer,
+    STFT,
     Zeros,
     GlorotNormal,
     GlorotUniform,
@@ -38,11 +38,11 @@ ALL_OBJECTS = {
     HeUniform,
     LecunNormal,
     LecunUniform,
+    Orthogonal,
     RandomNormal,
-    TruncatedNormal,
     RandomUniform,
+    TruncatedNormal,
     VarianceScaling,
-    OrthogonalInitializer,
 }
 
 ALL_OBJECTS_DICT = {cls.__name__: cls for cls in ALL_OBJECTS}
@@ -52,11 +52,12 @@ ALL_OBJECTS_DICT.update(
 # Aliases
 ALL_OBJECTS_DICT.update(
     {
-        "uniform": RandomUniform,
+        "IdentityInitializer": Identity,  # For compatibility
         "normal": RandomNormal,
-        "orthogonal": OrthogonalInitializer,
-        "Orthogonal": OrthogonalInitializer,  # Legacy
         "one": Ones,
+        "STFTInitializer": STFT,  # For compatibility
+        "OrthogonalInitializer": Orthogonal,  # For compatibility
+        "uniform": RandomUniform,
         "zero": Zeros,
     }
 )
diff --git a/keras/src/initializers/constant_initializers.py b/keras/src/initializers/constant_initializers.py
index ebdc93ca1..8f096232d 100644
--- a/keras/src/initializers/constant_initializers.py
+++ b/keras/src/initializers/constant_initializers.py
@@ -108,9 +108,9 @@ class Ones(Initializer):
 
 @keras_export(
     [
-        "keras.initializers.IdentityInitializer",
         "keras.initializers.Identity",
         "keras.initializers.identity",
+        "keras.initializers.IdentityInitializer",
     ]
 )
 class Identity(Initializer):
@@ -154,8 +154,14 @@ class Identity(Initializer):
         return self.gain * ops.eye(*shape, dtype=dtype)
 
 
-@keras_export(["keras.initializers.STFTInitializer"])
-class STFTInitializer(Initializer):
+@keras_export(
+    [
+        "keras.initializers.STFT",
+        "keras.initializers.stft",
+        "keras.initializers.STFTInitializer",
+    ]
+)
+class STFT(Initializer):
     """Initializer of Conv kernels for Short-term Fourier Transformation (STFT).
 
     Since the formula involves complex numbers, this class compute either the
@@ -177,7 +183,8 @@ class STFTInitializer(Initializer):
 
     Args:
         side: String, `"real"` or `"imag"` deciding if the kernel will compute
-            the real side or the imaginary side of the output.
+            the real side or the imaginary side of the output. Defaults to
+            `"real"`.
         window: String for the name of the windowing function in the
             `scipy.signal.windows` module, or array_like for the window values,
             or `None` for no windowing.
@@ -188,7 +195,9 @@ class STFTInitializer(Initializer):
             periodic. Defaults to `False`.
     """
 
-    def __init__(self, side, window="hann", scaling="density", periodic=False):
+    def __init__(
+        self, side="real", window="hann", scaling="density", periodic=False
+    ):
         if side not in ["real", "imag"]:
             raise ValueError(f"side should be 'real' or 'imag', not {side}")
         if isinstance(window, str):
diff --git a/keras/src/initializers/random_initializers.py b/keras/src/initializers/random_initializers.py
index e8bf5d106..ad1123e2a 100644
--- a/keras/src/initializers/random_initializers.py
+++ b/keras/src/initializers/random_initializers.py
@@ -639,12 +639,12 @@ def compute_fans(shape):
 
 @keras_export(
     [
-        "keras.initializers.OrthogonalInitializer",
         "keras.initializers.Orthogonal",
         "keras.initializers.orthogonal",
+        "keras.initializers.OrthogonalInitializer",
     ]
 )
-class OrthogonalInitializer(RandomInitializer):
+class Orthogonal(RandomInitializer):
     """Initializer that generates an orthogonal matrix.
 
     If the shape of the tensor to initialize is two-dimensional, it is
diff --git a/keras/src/layers/preprocessing/stft_spectrogram.py b/keras/src/layers/preprocessing/stft_spectrogram.py
index 8031133a2..6834bc356 100644
--- a/keras/src/layers/preprocessing/stft_spectrogram.py
+++ b/keras/src/layers/preprocessing/stft_spectrogram.py
@@ -217,7 +217,7 @@ class STFTSpectrogram(layers.Layer):
             self.real_kernel = self.add_weight(
                 name="real_kernel",
                 shape=shape,
-                initializer=initializers.STFTInitializer(
+                initializer=initializers.STFT(
                     "real", self.window, self.scaling, self.periodic
                 ),
             )
@@ -225,7 +225,7 @@ class STFTSpectrogram(layers.Layer):
             self.imag_kernel = self.add_weight(
                 name="imag_kernel",
                 shape=shape,
-                initializer=initializers.STFTInitializer(
+                initializer=initializers.STFT(
                     "imag", self.window, self.scaling, self.periodic
                 ),
             )


Test patch:
diff --git a/keras/src/initializers/constant_initializers_test.py b/keras/src/initializers/constant_initializers_test.py
index 055ff1e7d..28e199b43 100644
--- a/keras/src/initializers/constant_initializers_test.py
+++ b/keras/src/initializers/constant_initializers_test.py
@@ -69,6 +69,10 @@ class ConstantInitializersTest(testing.TestCase):
 
         self.run_class_serialization_test(initializer)
 
+        # Test compatible class_name
+        initializer = initializers.get("IdentityInitializer")
+        self.assertIsInstance(initializer, initializers.Identity)
+
     def test_stft_initializer(self):
         shape = (256, 1, 513)
         time_range = np.arange(256).reshape((-1, 1, 1))
@@ -82,12 +86,12 @@ class ConstantInitializersTest(testing.TestCase):
             # of non-small error in jax and torch
             tol_kwargs = {"atol": 1e-4, "rtol": 1e-6}
 
-        initializer = initializers.STFTInitializer("real", None)
+        initializer = initializers.STFT("real", None)
         values = backend.convert_to_numpy(initializer(shape))
         self.assertAllClose(np.cos(args), values, atol=1e-4)
         self.run_class_serialization_test(initializer)
 
-        initializer = initializers.STFTInitializer(
+        initializer = initializers.STFT(
             "real",
             "hamming",
             None,
@@ -99,7 +103,7 @@ class ConstantInitializersTest(testing.TestCase):
         self.assertAllClose(np.cos(args) * window, values, **tol_kwargs)
         self.run_class_serialization_test(initializer)
 
-        initializer = initializers.STFTInitializer(
+        initializer = initializers.STFT(
             "imag",
             "tukey",
             "density",
@@ -112,7 +116,7 @@ class ConstantInitializersTest(testing.TestCase):
         self.assertAllClose(np.sin(args) * window, values, **tol_kwargs)
         self.run_class_serialization_test(initializer)
 
-        initializer = initializers.STFTInitializer(
+        initializer = initializers.STFT(
             "imag",
             list(range(1, 257)),
             "spectrum",
@@ -125,8 +129,12 @@ class ConstantInitializersTest(testing.TestCase):
         self.run_class_serialization_test(initializer)
 
         with self.assertRaises(ValueError):
-            initializers.STFTInitializer("imaginary")
+            initializers.STFT("imaginary")
         with self.assertRaises(ValueError):
-            initializers.STFTInitializer("real", scaling="l2")
+            initializers.STFT("real", scaling="l2")
         with self.assertRaises(ValueError):
-            initializers.STFTInitializer("real", window="unknown")
+            initializers.STFT("real", window="unknown")
+
+        # Test compatible class_name
+        initializer = initializers.get("STFTInitializer")
+        self.assertIsInstance(initializer, initializers.STFT)
diff --git a/keras/src/initializers/random_initializers_test.py b/keras/src/initializers/random_initializers_test.py
index a8c33a7c4..15b13efff 100644
--- a/keras/src/initializers/random_initializers_test.py
+++ b/keras/src/initializers/random_initializers_test.py
@@ -7,7 +7,7 @@ from keras.src import testing
 from keras.src import utils
 
 
-class InitializersTest(testing.TestCase):
+class RandomInitializersTest(testing.TestCase):
     def test_random_normal(self):
         utils.set_random_seed(1337)
         shape = (25, 20)
@@ -124,11 +124,11 @@ class InitializersTest(testing.TestCase):
         )
         self.run_class_serialization_test(initializer)
 
-    def test_orthogonal_initializer(self):
+    def test_orthogonal(self):
         shape = (5, 5)
         gain = 2.0
         seed = 1234
-        initializer = initializers.OrthogonalInitializer(gain=gain, seed=seed)
+        initializer = initializers.Orthogonal(gain=gain, seed=seed)
         values = initializer(shape=shape)
         self.assertEqual(initializer.seed, seed)
         self.assertEqual(initializer.gain, gain)
@@ -148,9 +148,9 @@ class InitializersTest(testing.TestCase):
 
         self.run_class_serialization_test(initializer)
 
-        # Test legacy class_name
-        initializer = initializers.get("Orthogonal")
-        self.assertIsInstance(initializer, initializers.OrthogonalInitializer)
+        # Test compatible class_name
+        initializer = initializers.get("OrthogonalInitializer")
+        self.assertIsInstance(initializer, initializers.Orthogonal)
 
     def test_get_method(self):
         obj = initializers.get("glorot_normal")
@@ -214,7 +214,7 @@ class InitializersTest(testing.TestCase):
 
     def test_serialization_with_seed_generator(self):
         seed = random.SeedGenerator()
-        initializer = initializers.OrthogonalInitializer(seed=seed)
+        initializer = initializers.Orthogonal(seed=seed)
         self.run_class_serialization_test(initializer)
 
         seed = random.SeedGenerator()