应统一用joblib保存和加载模型,路径用path(__file__).parent避免失效,自定义类需可导入,输入需转为二维数组,预测逻辑用run_in_executor包裹,启动时验证模型可用性。

用 joblib 加载模型前先确认路径和格式是否匹配
FastAPI 启动时加载模型,最常见错误是路径不对或模型文件损坏。Scikit-learn 模型通常用 joblib.dump() 保存,但如果你用 pickle 保存,加载时会报 AttributeError: 'bytes' object has no attribute 'read'。务必统一用 joblib 保存和加载。
- 保存模型:运行训练脚本后执行
joblib.dump(model, "model.joblib") - FastAPI 服务中加载:用
joblib.load("model.joblib"),路径建议用Path(__file__).parent / "model.joblib"避免相对路径失效 - 如果模型含自定义类(如继承
BaseEstimator的预处理器),确保该类定义在 FastAPI 进程可 import 的模块里,否则反序列化失败
FastAPI 的 POST 接口接收数据要严格对应模型输入结构
Scikit-learn 模型一般接受二维数组(np.ndarray 或 pd.DataFrame),而 FastAPI 默认把 JSON 解析为 Python 字典或列表。直接传 {"feature_1": 0.5, "feature_2": 1.2} 会导致 ValueError: Expected 2D array, got 1D array instead。
- 推荐做法:定义 Pydantic 模型约束输入,例如
class PredictRequest(BaseModel): features: List[float],然后在路由中转成np.array([request.features]) - 若需列名对齐(比如训练时用了
pd.DataFrame),可在加载模型时一并保存列名:joblib.dump({"model": model, "feature_names": X_train.columns.tolist()}, "model_with_meta.joblib"),推理时校验字段顺序 - 避免在每次请求里做
pd.DataFrame.from_dict()—— 构造开销大,尤其高并发时;优先用纯 NumPy 路径
模型预测逻辑必须放在 async def 外部,用 run_in_executor 包裹
Scikit-learn 的 predict() 和 predict_proba() 是纯 CPU 同步操作,直接写在 async def 路由里会阻塞事件循环。不加处理就上线,QPS 上不去,延迟毛刺明显。
- 正确方式:用
loop.run_in_executor(None, model.predict, X),其中X是已准备好的二维 NumPy 数组 - 别用
ThreadPoolExecutor手动管理线程池——FastAPI 的run_in_executor已复用默认线程池,更轻量 - 如果模型本身支持
n_jobs > 1(如RandomForestClassifier),注意与线程池嵌套可能引发死锁,建议设n_jobs=1并靠 FastAPI 并发调度
启动时验证模型可用性,别等第一个请求才暴露问题
FastAPI 的 @app.on_event("startup") 是唯一可靠的地方做模型健康检查。否则服务看似启动成功,但首次请求才抛出 ModuleNotFoundError 或 ValueError,监控难捕获。
- 在 startup 里调用一次
model.predict(np.zeros((1, n_features))),确保能跑通 - 检查
model.n_features_in_是否与预期一致,防止训练/部署特征维度 mismatch - 别只打印日志——失败时显式调用
sys.exit(1),让容器编排系统(如 Kubernetes)知道该重启
真正麻烦的不是怎么写接口,而是模型版本、特征工程代码、数据分布漂移这三者没对齐。哪怕 API 返回 200,预测结果也可能是错的。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











