必须用 custom_objects 参数显式注册自定义类和函数,否则 load_model() 因找不到类名或方法名而报错;推荐改用 SavedModel 格式并手动 call 触发构建以规避反序列化问题。

不能直接用 tf.keras.models.load_model() 加载旧版自定义模型,除非你显式提供所有自定义类和函数。 否则会报 ValueError: Unknown layer 或 NameError —— 因为 Keras 反序列化时找不到你定义的类名或方法名。
为什么 load_model() 会找不到自定义对象
Keras 在保存模型(尤其是 HDF5 格式)时,只存了类名字符串(如 "MyCustomLayer"),不存源码。加载时它尝试用 globals() 或传入的 custom_objects 字典去查这个名。如果你没把类定义好、没导入、或名字对不上,就直接失败。
常见现象包括:
- 训练代码里定义了
class ResBlock(tf.keras.layers.Layer),但加载脚本里没重定义它 - 类在模块 A 里定义,加载脚本只导入了模块 B,
globals()里没有该类 - 用了 lambda 或闭包函数作为 activation,无法被序列化/反序列化
- 自定义
train_step或call中引用了外部变量(如全局tf.Variable),加载时未重建
正确做法:用 custom_objects 参数显式注册
必须确保加载时,custom_objects 字典中键是保存时记录的类/函数名(原样),值是你当前 Python 环境中可用的对象引用。
立即学习“Python免费学习笔记(深入)”;
例如,若模型中用了自定义层 MyDense 和激活函数 swish:
import tensorflow as tf
<h1>✅ 必须重新定义(或 import)这些对象</h1><p>class MyDense(tf.keras.layers.Layer):
def <strong>init</strong>(self, units, <strong>kwargs):
super().<strong>init</strong>(</strong>kwargs)
self.units = units
self.dense = tf.keras.layers.Dense(units)</p><pre class="brush:php;toolbar:false;">def call(self, x):
return self.dense(x)def swish(x): return x * tf.nn.sigmoid(x)
✅ 加载时显式传入
loaded_model = tf.keras.models.load_model( 'my_old_model.h5', custom_objects={ 'MyDense': MyDense, # 键名必须和保存时完全一致(大小写、下划线) 'swish': swish # 同样,不能写成 'activation': swish } )
注意:
- 键名不是你随便起的,而是模型保存时写进文件的名称 —— 如果不确定,可先用
h5py打开 .h5 文件查看/model_config字段 - 如果类在另一个文件里定义,务必
from my_module import MyDense,而不是只写import my_module -
custom_objects不支持嵌套模块路径(如'my_module.MyDense'),只认顶层变量名
更安全的替代:改用 SavedModel + 显式 call 构建
如果你反复被 custom_objects 坑到,建议放弃 HDF5,改用 SavedModel 格式保存,并在加载后手动调用 call() 触发完整构建 —— 它不依赖类名反查,而是靠图结构和变量名匹配。
前提是:旧模型保存时用了 model.save('path', save_format='tf')(即无 .h5 后缀)。
加载后若需继续训练,注意检查变量是否全被识别:
# 加载 SavedModel
loaded_model = tf.keras.models.load_model('my_model')
<h1>✅ 强制触发一次 call,确保所有子层完成 build & 变量注册</h1><p>_ = loaded_model(tf.zeros((1, 28, 28, 3)))</p><h1>检查是否所有变量都到位(避免后续 train_step 报 "expected 0 variables")</h1><p>print(len(loaded_model.trainable_variables)) # 应 > 0
这种做法绕过了 JSON 反序列化环节,天然规避了 Unknown layer 类错误,但要求原始模型能用 eager mode 正常运行一次。
最易被忽略的一点:自定义对象的 __init__ 和 get_config() 必须严格匹配。哪怕只是多一个默认参数、少一个 super().__init__(),都会导致 SavedModel 加载后 call() 失败或变量缺失 —— 这类问题不会在 load_model() 阶段报错,而是在第一次前向传播时才暴露。


















