无法加载含自定义Lambda组合损失函数的Keras模型求解
嘿,我明白你遇到的问题了——用lambda把多个自定义损失拼起来训练模型,保存后再加载就卡壳了对吧?其实核心原因是Keras不会自动保存自定义函数的定义,咱们得手动告诉它这些函数的存在才行。下面给你两种可行的解决办法:
方法一:提前把Lambda损失改成具名函数(推荐)
当初你用匿名lambda写损失虽然方便,但序列化的时候很麻烦,不如改成一个有名字的函数,后续加载省心很多。
第一步:重构损失函数
把原来的lambda逻辑包装成一个具名函数,比如叫combined_loss:
def combined_loss(y_true, y_pred): return tf.keras.losses.binary_crossentropy(y_true, y_pred) + \ pixelLevelLoss_weight * pixelLevelLoss(y_true, y_pred) + \ totalVariationLoss_weight * totalVariationLoss(y_true, y_pred) + \ featureLevelLoss_weight * featureLevelLoss(y_true, y_pred)
然后编译模型的时候直接用这个函数代替原来的lambda:
model.compile(loss=combined_loss, optimizer=opt)
第二步:加载模型时传入自定义函数
加载模型前,确保你在当前代码环境里已经定义了所有用到的自定义损失函数(包括combined_loss和那三个子损失),然后用custom_objects参数把它们传给load_model:
from tensorflow.keras.models import load_model # 先把所有自定义损失的原实现搬过来 def pixelLevelLoss(y_true, y_pred): # 这里是你原来的像素级损失代码 pass def totalVariationLoss(y_true, y_pred): # 这里是你原来的总变差损失代码 pass def featureLevelLoss(y_true, y_pred): # 这里是你原来的特征级损失代码 pass def combined_loss(y_true, y_pred): return tf.keras.losses.binary_crossentropy(y_true, y_pred) + \ pixelLevelLoss_weight * pixelLevelLoss(y_true, y_pred) + \ totalVariationLoss_weight * totalVariationLoss(y_true, y_pred) + \ featureLevelLoss_weight * featureLevelLoss(y_true, y_pred) # 加载模型,把所有自定义函数注册进去 model = load_model('你的模型路径.h5', custom_objects={ 'combined_loss': combined_loss, 'pixelLevelLoss': pixelLevelLoss, 'totalVariationLoss': totalVariationLoss, 'featureLevelLoss': featureLevelLoss })
⚠️ 注意:pixelLevelLoss_weight这些权重变量,必须和你当初训练时的取值完全一致,不然损失计算会跑偏,影响后续训练效果。
方法二:如果已经用Lambda保存了模型(不想重构)
要是你已经用原来的lambda损失保存了模型,不想回头改训练代码,那也能解决——先找到模型保存时给lambda分配的名字,再映射到咱们重新定义的损失函数。
第一步:查看Lambda损失的名称
先加载模型的配置文件(或者直接读.h5文件),看看Keras给那个匿名lambda起了啥名字:
import h5py with h5py.File('你的模型路径.h5', 'r') as f: print(f.attrs.get('training_config'))
在输出的字符串里,你能找到类似"loss": "<lambda>"或者"loss": "lambda_1"这样的内容,那个引号里的就是lambda的名字。
第二步:加载时映射名称到损失函数
把查到的lambda名称和咱们重新定义的组合损失(逻辑和原来的lambda完全一样)绑定,再加上三个子损失,一起传入custom_objects:
from tensorflow.keras.models import load_model # 同样先定义所有自定义损失 def pixelLevelLoss(y_true, y_pred): # 原实现代码 pass # ... 其他两个损失函数定义 ... def combined_loss(y_true, y_pred): return tf.keras.losses.binary_crossentropy(y_true, y_pred) + \ pixelLevelLoss_weight * pixelLevelLoss(y_true, y_pred) + \ totalVariationLoss_weight * totalVariationLoss(y_true, y_pred) + \ featureLevelLoss_weight * featureLevelLoss(y_true, y_pred) # 加载模型,把lambda名称映射到combined_loss model = load_model('你的模型路径.h5', custom_objects={ '<lambda>': combined_loss, # 这里填你查到的lambda名字 'pixelLevelLoss': pixelLevelLoss, 'totalVariationLoss': totalVariationLoss, 'featureLevelLoss': featureLevelLoss })
加载后继续训练
搞定加载后,就可以像平时一样继续训练了:
# 用你的新数据继续训练 model.fit([new_sketch_inp, new_color_inp], new_disc_outputs, epochs=10, batch_size=32)
额外提醒
- 所有自定义损失的代码必须和训练时完全一致,哪怕是微小的改动都会导致模型加载失败或者损失计算错误。
- 要是你用了自定义的优化器或者其他自定义层,也要用同样的方法在
custom_objects里注册它们。
内容的提问来源于stack exchange,提问作者mohamed

