如何解决pickle序列化含自定义函数的Python对象跨环境加载报错问题
解决方案
方案1:使用dill替代pickle完成序列化(最快适配现有代码)
dill扩展了pickle的序列化能力,支持直接序列化函数实现、闭包环境,不需要改动原有函数定义:
首先安装dill:pip install dill
保存端代码:
import shap, dill import tensorflow as tf model = ... # 你的tensorflow模型 def f(X): ... return model.predict(...).flatten() explainer = shap.KernelExplainer(f, X.iloc[:50, :]) # 保存逻辑 with open(f"/tmp/{file}.pkl", 'wb') as fil: dill.dump(explainer, fil)
加载端代码:
import dill with open(f"/tmp/{file}.pkl", 'rb') as fil: explainer = dill.load(fil)
注意:保存和加载环境的dill、shap、tensorflow版本要尽可能保持一致,避免版本兼容问题
方案2:将自定义函数封装为可调用类(兼容性最好,原生pickle支持)
pickle可以正常序列化自定义类的实例,把函数逻辑和依赖的模型封装到类中即可:
保存端代码:
import shap, pickle import tensorflow as tf class ShapPredictWrapper: def __init__(self, model): self.model = model def __call__(self, X): # 把原f函数的逻辑写在这里 return self.model.predict(...).flatten() model = ... # 你的tensorflow模型 f = ShapPredictWrapper(model) explainer = shap.KernelExplainer(f, X.iloc[:50, :]) with open(f"/tmp/{file}.pkl", 'wb') as fil: pickle.dump(explainer, fil)
加载端代码:必须在加载前先定义完全相同的ShapPredictWrapper类,或者将该类放到公共模块中,两端都从公共模块导入
import pickle # 先导入/定义ShapPredictWrapper类,要和保存端完全一致 from xxx import ShapPredictWrapper with open(f"/tmp/{file}.pkl", 'rb') as fil: explainer = pickle.load(fil)
方案3:将自定义函数放到公共模块中
如果你的保存和加载脚本在同一个项目中,可以把f函数和模型加载逻辑都放到单独的公共模块比如shap_utils.py里,保存和加载时都从shap_utils导入f,不要把f定义在if __name__ == '__main__'包裹的代码块中,pickle无法序列化主脚本内的顶层函数。
术语表述优化建议
- 描述问题时可以明确标注「序列化/反序列化」「跨进程/跨脚本加载」「自定义顶层函数/闭包函数」这些关键词,更容易匹配到对应解决方案
- 报错信息可以贴完整的Traceback,而不是仅贴最终报错提示,方便定位是序列化阶段出错还是反序列化阶段出错
- 可以补充说明保存和加载两端的运行环境差异,比如是否是同一个项目、依赖版本是否一致、函数定义是否在两端都存在
内容的提问来源于stack exchange,提问作者Rory Donovan
相关产品推荐
相关产品推荐

