ValueError: Unknown layer: <function <lambda> at 0x...> in Keras Lambda layer
0 reputation · 18 Feb 2024, 11:50 UTC
0 reputation · 18 Feb 2024, 11:50 UTC
The goal is to guarantee that a Keras model containing a Lambda layer can be saved and subsequently reloaded in a different process or environment without raising a ValueError about an unknown layer. In local interactive sessions the inline lambda function remains in memory, allowing the model to be used directly, but after model.save the layer’s configuration cannot reconstruct the function, leading to the error 'ValueError: Unknown layer: at 0x...>' when tf.keras.models.load_model is called. The current behavior places the responsibility on the user to define the lambda at module level or to supply a custom_objects mapping, yet it remains unclear whether the framework should handle this automatically.
Should Keras automatically wrap inline lambda functions in a serializable wrapper to make them loadable without user intervention? What are the potential performance, security, or compatibility trade‑offs of such automatic wrapping? Would strengthening the documentation to require module‑level definitions be enough to avoid production failures, or does the API need a change?
34775 reputation · 18 Feb 2024, 16:05 UTC
To resolve the ValueError: Unknown layer: <function <lambda>..., you must provide the function used in the Lambda layer to the custom_objects argument during model loading. Because lambda functions are anonymous and not serializable by Python's pickle module, Keras cannot reconstruct the function from the saved model file alone.
lambda with a standard Python function. This ensures the function has a consistent name that can be referenced across different sessions.Lambda layer.load_model.import tensorflow as tf
from tensorflow.keras.layers import Lambda
from tensorflow.keras.models import Sequential, load_model
# 1. Define a named function instead of a lambda
def multiply_by_two(x):
return x * 2
# 2. Use the named function in the model
model = Sequential([
Lambda(multiply_by_two, name="my_lambda_layer")
])
model.save("my_model.h5")
# 3. Provide the function in custom_objects during loading
reloaded_model = load_model("my_model.h5",
custom_objects={"multiply_by_two": multiply_by_two})
Keras saves the configuration of a layer, not the executable bytecode of the function. For a Lambda layer, it saves the name of the function. Since lambda functions are anonymous, they are assigned a generic name (like <lambda>) and a memory address that changes every time the Python interpreter starts. When load_model is called in a new process, the memory address is gone, and the name <lambda> is ambiguous, leading to the ValueError.
While Keras could theoretically attempt to serialize the bytecode of a lambda function, this introduces significant risks:
pickle) can lead to remote code execution (RCE) vulnerabilities if a model file is tampered with.Strengthening documentation is the preferred path over API changes, as requiring named functions enforces a disciplined approach to model versioning and deployment.
To verify the fix, attempt to load the model in a completely fresh Python shell or a separate script where the model was not originally defined. If the model loads without the custom_objects dictionary and fails, but succeeds with it, the resolution is confirmed.
Diagnostic Detail: Are you using the .h5 format or the newer SavedModel format? The SavedModel format (default in TF 2.x) attempts to save the computation graph, which can sometimes bypass this issue for simple operations, whereas .h5 relies more heavily on Python-side reconstruction.
Use comments to ask for clarification. Post a solution as an answer.
34,775 reputation · 18 Feb 2024, 16:20 UTC
The named-function route fixes the .h5 / .keras case, but two details tend to bite later.
First, custom_objects matches the callable's __name__ stored in the layer config, not the layer's name argument. A module-level function importable at the same dotted path in the loading process usually needs no mapping at all; the mapping is mainly for renamed or relocated callables.
Second, closures do not survive. A lambda or nested function that reads an outer variable reloads with that value missing, and re-registering the object via custom_objects does not restore it — the captured state was never serialized. SavedModel export is stricter still: the Lambda is traced to a generic op, so serving environments need the logic expressed with native TF ops or a registered custom object.
Practical check: save the model, then load it in a fresh interpreter with the defining module imported, and confirm the construction-time warning that recent TF 2.x releases emit for inline lambdas is absent.