Lambda model save/load silently changes numeric constants on Windows
Description
On Windows, saving and loading a model containing Lambda(lambda x: x + 92) changes its predictions. The restored function adds 47 instead of 92.
Verified on upstream commit a718e34b9f1fa4998dfba174712613ad0dc37cbc, Keras 3.16.0, Python 3.12.12, Windows, NumPy backend (NumPy 2.5.3, JAX 0.11.1).
Reproduction
import os
import tempfile
from pathlib import Path
os.environ["KERAS_BACKEND"] = "numpy"
import keras
import numpy as np
model = keras.Sequential([
keras.Input(shape=(1,)),
keras.layers.Lambda(lambda x: x + 92),
])
x = np.array([[0], [1], [2]], dtype="float32")
before = keras.ops.convert_to_numpy(model(x))
with tempfile.TemporaryDirectory() as directory:
path = Path(directory) / "model.keras"
model.save(path)
# Load only the model created immediately above.
restored = keras.models.load_model(path, safe_mode=False)
after = keras.ops.convert_to_numpy(restored(x))
print("Before:", before.ravel().tolist())
print("After: ", after.ravel().tolist())
np.testing.assert_array_equal(after, before)
Expected both outputs: [92.0, 93.0, 94.0].
Actual restored output: [47.0, 48.0, 49.0]. Loading succeeds, but the equality assertion fails.
Cause and proposed fix
The Windows branch in keras/src/utils/python_utils.py::func_dump applies .replace(b"\\", b"/") to the entire marshaled code object. It changes byte 0x5c (92) into 0x2f (47), including numeric constants and string/bytes constants.
The replacement was introduced in #6628 for the old raw_unicode_escape decoder. #8572 subsequently changed the format to Base64. Preserving the marshaled bytes and Base64-encoding them directly fixes the reproduced corruption.
This affects serialized Python functions; it does not imply that every Windows model is affected. The fix prevents new corruption and cannot recover constants already altered in saved artifacts.
Contribution request
I would like to contribute the fix. Could a maintainer assign this issue to @ujwalreddybattu04? A prepared fix and regression tests are in draft PR #23631, pending assignment and the contributor requirements.
Local validation of that fix: 50 tests passed and 3 skipped across the utility, Lambda layer, and object serialization test suites using the NumPy backend. The full model reproduction passes after the fix.
AI assistance was used for investigation, the patch, tests, and this report.
Source: keras-team/keras