FastAPI+Uvicorn加载序列化FunctionTransformer出现NameError问题
NameError: name 'np' is not defined when loading pickled FunctionTransformer in FastAPI/Uvicorn
问题背景
用FastAPI搭建微服务,加载通过dill序列化的sklearn FunctionTransformer(内部依赖lambda调用numpy)时,本地测试正常,但Uvicorn启动后(尤其是reload=True模式,复杂项目中甚至非reload模式)触发NameError: name 'np' is not defined。Transformer定义如下:
my_transformer = sklearn.preprocessing.FunctionTransformer( func = lambda y: -np.log(y / 100), inverse_func = lambda y_transformed: np.exp(-y_transformed) * 100, )
问题根源
pickle(含dill)序列化lambda函数时,不会保存lambda依赖的外部全局变量(比如np),仅保存变量名称字符串。在Uvicorn环境中加载时:
- 启用
reload=True时,Uvicorn会创建子进程重新加载应用模块,此时lambda的闭包作用域和序列化时的环境不一致,子进程全局命名空间中np的引用无法被lambda正确绑定。 - 复杂项目中,模块结构、import顺序或Uvicorn多进程启动方式,会导致加载transformer的进程全局命名空间里
np的名称无法被lambda识别,即便模块已import numpy。
解决方法
1. 替换lambda为具名函数(推荐)
lambda序列化天生脆弱,改用明确的具名函数可让dill正确捕获依赖:
import numpy as np from sklearn.preprocessing import FunctionTransformer def forward_transform(y): return -np.log(y / 100) def inverse_transform(y_transformed): return np.exp(-y_transformed) * 100 my_transformer = FunctionTransformer(func=forward_transform, inverse_func=inverse_transform)
重新序列化该transformer后,FastAPI服务加载时只要环境安装numpy,即可正常运行,不受Uvicorn启动方式影响。
2. 确保加载环境全局绑定np
若必须保留lambda,在FastAPI入口模块顶部明确import numpy并绑定为np:
# 应用入口文件最顶部 import numpy as np # 其余import代码...
但该方法稳定性差,复杂项目中若模块import顺序或进程隔离导致np不在加载transformer的作用域内,仍会报错,仅适用于简单场景。
3. 用cloudpickle替代dill序列化
cloudpickle对lambda和外部依赖的序列化支持更完善,可尝试替换序列化工具:
import cloudpickle # 序列化时 with open("my_transformer.cloudpickle", "wb") as f: cloudpickle.dump(my_transformer, f) # FastAPI中加载 with open("my_transformer.cloudpickle", "rb") as f: my_transformer = cloudpickle.load(f)
但仍优先推荐具名函数方案,lambda的可读性和可维护性均逊于具名函数。
内容的提问来源于stack exchange,提问作者bernie
相关产品推荐
相关产品推荐

