第 87 课 - Flask 构建模型推理 API
所属模块: 工程化与部署
难度: Intermediate
标签: deployment, flask
上一课: 模型持久化:Pickle、Joblib 与 ONNX
下一课: FastAPI 高性能模型服务
1. 学习目标
完成本课学习后,你将能够:
- 理解 Flask 框架在机器学习模型部署中的核心作用。
- 掌握使用 Flask 创建 RESTful API 以提供模型推理服务的完整流程。
- 学会接收 JSON 格式的输入数据,调用已加载的模型进行预测,并返回格式化的结果。
- 了解在生产环境中部署此类服务的基本注意事项。
2. 核心概念
Flask:轻量级的Web服务窗口
Flask 是一个用 Python 编写的轻量级 Web 应用框架。我们可以把它想象成一个“服务窗口”。客户端(比如一个前端网页或另一个后端程序)通过发送请求到这个窗口,窗口后面的服务程序(我们的模型代码)处理请求,然后将结果(比如预测值)送回给客户端。
模型推理 API:模型的“接客”接口
在上一课,我们学习了如何将训练好的模型保存到文件中。现在,我们需要让这个“大脑”能够工作。构建一个 API(应用程序接口)就是为这个大脑创建一个标准化的“接客”接口。其他程序只需按照约定(例如,发送一个包含房屋面积的JSON数据),就能通过这个接口获得预测结果(例如,房屋价格)。
核心交互流程
- 启动服务:加载训练好的模型,并启动一个 Web 服务器,监听特定端口。
- 接收请求:客户端发送一个 HTTP POST 请求到我们定义的 URL(例如
/predict),请求体中携带待预测的数据(通常是 JSON 格式)。 - 处理数据:服务器解析 JSON 数据,将其转换为模型所需的格式(如 NumPy 数组或 Pandas DataFrame)。
- 模型推理:调用加载的模型进行预测。
- 返回结果:将预测结果包装成 JSON 格式返回给客户端。
3. 代码示例
假设我们有一个训练好的房价预测线性回归模型,并已使用 joblib 保存为 house_price_model.joblib。
第一步:准备模型(如果需要,可先运行此代码生成一个示例模型)
# train_and_save_model.py
from sklearn.linear_model import LinearRegression
import joblib
import numpy as np
# 模拟训练数据:面积 -> 价格
X_train = np.array([[50], [80], [100], [120], [150]])
y_train = np.array([150, 240, 300, 360, 450])
# 训练一个简单的线性回归模型
model = LinearRegression()
model.fit(X_train, y_train)
# 保存模型
joblib.dump(model, 'house_price_model.joblib')
print(“模型已保存!”)
第二步:构建 Flask API 服务
# flask_model_serving.py
from flask import Flask, request, jsonify
import joblib
import numpy as np
# 1. 创建 Flask 应用实例
app = Flask(__name__)
# 2. 加载模型
# 使用一个全局变量存储模型,避免每次请求都加载
model = joblib.load('house_price_model.joblib')
print(“模型加载完成!”)
# 3. 定义健康检查接口(可选,但推荐)
@app.route('/health', methods=['GET'])
def health_check():
"""用于监控服务是否存活"""
return jsonify({'status': 'healthy'})
# 4. 定义预测接口
@app.route('/predict', methods=['POST'])
def predict():
"""接收面积,返回预测价格"""
# 获取请求的 JSON 数据
data = request.get_json(force=True)
# 输入验证(非常重要!)
if 'area' not in data:
return jsonify({'error': '缺少面积参数 area'}), 400
try:
area = float(data['area'])
except (ValueError, TypeError):
return jsonify({'error': '面积必须是数字'}), 400
# 将输入数据转换为模型期望的格式(2D 数组)
input_data = np.array([[area]])
# 进行预测
predicted_price = model.predict(input_data)[0]
# 构造并返回响应
response = {
'area': area,
'predicted_price': round(float(predicted_price), 2) # 保留两位小数
}
return jsonify(response)
# 5. 启动应用
if __name__ == '__main__':
# debug=True 在开发时使用,便于调试和热重载
# 生产环境应关闭 debug,并使用生产级 WSGI 服务器(如 Gunicorn)
app.run(host='0.0.0.0', port=5000, debug=True)
如何使用此 API:
- 先运行
train_and_save_model.py生成模型文件。 - 运行
flask_model_serving.py启动服务器。 - 使用
curl或任何 HTTP 客户端发送请求测试:预期输出:curl -X POST http://localhost:5000/predict \ -H "Content-Type: application/json" \ -d '{"area": 110}'{ "area": 110.0, "predicted_price": 330.0 } - 测试健康检查:
curl http://localhost:5000/health
4. 实践练习
练习 1:基础扩展
在现有 API 的基础上,增加一个 /model_info 接点(GET 方法),返回模型的类型(LinearRegression)和它被加载的时间。
要求:
- 使用 Python 的
datetime模块记录模型加载时间。 - 返回的 JSON 应包含
model_type和loaded_at字段。
练习 2:增强输入验证
修改 /predict 接口,使其能够接收一个包含多个房屋面积的列表 areas,并返回对应的预测价格列表。
要求:
- 输入示例:
{"areas": [100, 150, 200]} - 如果
areas不是列表,或者列表中包含非数字,返回明确的错误信息。 - 预期输出:
{"areas": [100, 150, 200], "predicted_prices": [300.0, 450.0, 600.0]}
练习 3:数据预处理集成
假设你的模型需要两个特征:area(面积)和 rooms(房间数)。修改 API,使其能接收这两个字段,并将它们合并后输入模型。模型需要你重新训练(可以使用随机数据)。
要求:
- 创建一个新的模型,使用两个特征进行训练。
- 修改
/predict接口,接收area和rooms。 - 验证两个字段都存在且为数字。
- 预期输入:
{"area": 110, "rooms": 3} - 预期输出:
{"area": 110.0, "rooms": 3, "predicted_price": [一个合理数值]}
5. 常见错误
- 忽略输入验证:直接信任客户端发送的数据是危险的。务必检查必需字段是否存在、数据类型是否正确(如是否为数字),并为可能的转换错误(如将字符串转为浮点数)提供友好的错误提示。
- 模型输入形状不匹配:
scikit-learn模型的predict方法通常需要一个二维数组(即使只有一个样本)。常见错误是传入一维数组,导致ValueError。解决方法是使用np.array([[data]])进行包装。 - 在请求处理函数内加载模型:如果将
model = joblib.load(...)放在predict()函数内部,会导致每个请求都重新加载模型文件,效率极低。应将其放在全局作用域或应用初始化阶段。 - 生产环境使用 Flask 内置服务器:Flask 自带的
app.run()服务器仅适用于开发和测试。它单线程、无安全加固,无法应对生产环境的并发压力。应使用 Gunicorn 或 uWSGI 等专业的 WSGI 服务器来部署。 - CORS(跨域资源共享)问题:当前端网页(如运行在
localhost:3000)直接调用运行在localhost:5000的 Flask API 时,浏览器会因同源策略阻止请求。在开发时,可安装flask-cors扩展轻松解决。
6. 小结
- Flask 的角色:Flask 是构建机器学习模型推理 API 的轻量级、灵活的工具,它充当了客户端与模型之间的桥梁。
- API 设计核心:一个清晰的 RESTful API 设计应包含明确的端点(如
/predict)、合适的 HTTP 方法(如 POST)、结构化的请求/响应(通常使用 JSON),以及充分的错误处理。 - 关键步骤:构建服务的流程是 加载模型 -> 定义路由 -> 处理请求(验证、预处理、推理、格式化) -> 返回响应。
- 生产考量:本课使用的是开发服务器,真正的生产部署需要考虑使用生产级 WSGI 服务器、添加认证、监控日志、容器化(Docker)以及设置合理的超时和并发策略。
在下一课中,我们将学习如何使用 FastAPI,一个现代的、高性能的 Web 框架来构建同样的服务,它在类型提示、自动生成文档和异步支持方面有显著优势。