You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

从Redis加载Keras模型时遇未知度量函数错误求助

问题出在哪

报错Unknown metric function: 'function',核心原因有两个:

  1. 只处理了自定义损失,没管自定义度量:你的模型训练时大概率用了自定义度量函数,但加载和缓存逻辑里只注册了损失函数,反序列化时找不到度量函数就报错了。
  2. pickle反序列化的时机错了:现在你是先把缓存里的内容用pickle.loads解析出来,再进custom_object_scope。但Keras模型对象在被pickle反序列化的那一刻就会尝试解析内部的自定义对象,这时候作用域还没生效,自然找不到对应的度量。
怎么修复

1. 补上自定义度量的处理逻辑

首先确认你的MLModel模型有没有存储度量相关的字段(比如metric_type和metric_code),如果有,先修改批量加载模型的代码,把度量也一起处理:

# 批量加载模型时新增度量函数处理
loss_type = model_entry.loss_type
loss_code = model_entry.loss
# 新增:获取度量相关字段
metric_type = model_entry.metric_type
metric_code = model_entry.metric_code

loss_function = load_loss(loss_type, loss_code)
# 新增:加载自定义度量函数
metric_function = load_metric(metric_type, metric_code)

# 加载模型时同时注册损失和度量
model = tf.keras.models.load_model(
    local_model_path, 
    custom_objects={loss_type: loss_function, metric_type: metric_function}
)

# 保存模型详情时把度量信息也加上
model_details = {
    'name': model_name, 
    'model': model,
    'list_of_words': list_of_words, 
    'ml_utils_code': ml_utils_code,
    'loss_type': loss_type,
    'loss_code': loss_code,
    'metric_type': metric_type,  # 新增
    'metric_code': metric_code   # 新增
}

如果MLModel里没存度量信息,得先补上字段,或者加载模型后用model.metrics_names提取相关信息。

2. 调整反序列化顺序,先开作用域再解析pickle

现在的逻辑是先解析再开作用域,完全搞反了。必须先把自定义对象注册到作用域里,再去解析缓存里的模型数据,这里有两种方案:

方案一:拆分缓存存储(推荐)

把模型的元数据(名称、损失/度量类型等)和模型对象分开存到Redis,先加载元数据拿到自定义对象类型,再开作用域加载模型:

批量加载时修改存储逻辑:

# 拆分元数据和模型对象
meta_details = {
    'name': model_name,
    'list_of_words': list_of_words,
    'ml_utils_code': ml_utils_code,
    'loss_type': loss_type,
    'loss_code': loss_code,
    'metric_type': metric_type,
    'metric_code': metric_code
}
# 存元数据
redis_client.set(f"model_meta:{model_title}", pickle.dumps(meta_details))
# 存模型对象
redis_client.set(f"model_obj:{model_title}", pickle.dumps(model))

修改get_loaded_model函数:

def get_loaded_model(title):
    try:
        # 先加载元数据
        meta_data = redis_client.get(f"model_meta:{title}")
        if not meta_data:
            logger.warning(f"模型 '{title}' 的元数据找不到")
            return None
        meta_details = pickle.loads(meta_data)
        
        # 加载自定义损失和度量
        loss_type = meta_details['loss_type']
        loss_code = meta_details['loss_code']
        metric_type = meta_details['metric_type']
        metric_code = meta_details['metric_code']
        
        loss_function = load_loss(loss_type, loss_code)
        metric_function = load_metric(metric_type, metric_code)
        
        # 先开作用域,再加载模型对象
        with custom_object_scope({loss_type: loss_function, metric_type: metric_function}):
            model_data = redis_client.get(f"model_obj:{title}")
            if not model_data:
                logger.warning(f"模型 '{title}' 的对象找不到")
                return None
            model = pickle.loads(model_data)
        
        # 组装返回的模型详情
        model_details = meta_details.copy()
        ml_utils_code = model_details['ml_utils_code']
        ml_utils = load_ml_utils(ml_utils_code)
        model_details['ml_utils'] = ml_utils
        
        return model_details, model
    except Exception as e:
        logger.error(f"加载模型 '{title}' 出错: {e}")
        return None

方案二:临时应急(不修改存储逻辑)

如果暂时改不了存储逻辑,可以直接从数据库里查模型的元数据,再开作用域解析缓存:

def get_loaded_model(title):
    try:
        model_data = redis_client.get(f"model:{title}")
        if model_data:
            # 从数据库查模型的元数据
            model_entry = MLModel.objects.get(title=title)
            loss_type = model_entry.loss_type
            loss_code = model_entry.loss
            metric_type = model_entry.metric_type
            metric_code = model_entry.metric_code
            
            # 加载自定义函数
            loss_function = load_loss(loss_type, loss_code)
            metric_function = load_metric(metric_type, metric_code)
            
            # 先开作用域,再解析整个缓存内容
            with custom_object_scope({loss_type: loss_function, metric_type: metric_function}):
                model_details = pickle.loads(model_data)
            
            # 加载工具类
            ml_utils_code = model_details['ml_utils_code']
            ml_utils = load_ml_utils(ml_utils_code)
            model_details['ml_utils'] = ml_utils
            
            return model_details, model_details['model']
        else:
            logger.warning(f"模型 '{title}' 不在缓存里")
            return None
    except Exception as e:
        logger.error(f"加载模型 '{title}' 出错: {e}")
        return None
踩坑提醒
  • Keras模型的反序列化(不管是pickle还是load_model)必须在custom_object_scope生效的时候做,晚了没用。
  • 只要是自定义的东西(损失、度量、自定义层),都得注册到作用域里,漏一个就报错。
  • 直接pickle整个Keras模型容易出兼容性问题,比如TensorFlow版本变更后可能加载失败,推荐用SavedModel格式存模型,缓存模型路径会更稳妥。

内容的提问来源于stack exchange,提问作者Saif

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 21:05:11