本文收录于专栏 《经典机器学习实战》
摘要:模型训好了怎么给别人用?用 Flask + pickle 把 UFO 目击分类器(准确率 0.97)包成 Web 应用:GET 返回页面、POST 返回预测,五行核心代码跑通模型上线的最小闭环。
关键字:Flask,模型部署,pickle,Web应用,机器学习上线,UFO数据集,REST接口,模型序列化
阶段 2 · 回归 + Web 应用 | 来源:【原·09】| 知识更新至 2026-10
概念讲解

UFO 数据集 80,332 行,美国占绝对多数------这也是后面模型预测 us 占比高的数据根源。
5.1 训练完然后呢:模型交付的四个决策
前几课的模型都活在 notebook 里。真实业务中,模型要被人用------交付形态决定训练方式。动手前先问四个问题:
- Web 应用还是移动应用? 移动端或 IoT 场景可能要用 TensorFlow Lite 这类端侧方案。
- 模型放哪? 云端服务还是本地?云端便于更新,本地零依赖。
- 要支持离线吗? 离线场景模型必须打包进应用。
- 用什么技术训练的? 这决定导出格式:PyTorch/TensorFlow 系常用 ONNX(第 9 课会走这条路);scikit-learn 模型最直接的交付物是 pickle 文件。
本课的场景最朴素:模型和数据都在本地,用 Python 写个网页表单给人玩------Flask + pickle 是最短路径。
5.2 两件工具
Pickle :Python 的对象序列化模块,把训练好的模型对象"压扁"存成 .pkl 文件,另一段代码 load 回来就能直接 predict------不需要重新训练,不需要数据。
⚠️ pickle 不校验内容,不要加载来路不明的 .pkl 文件(可能执行恶意代码)。自己训练、自己保存、自己加载,或只收可信来源。
Flask:Python 的"微框架"------不搞全家桶,只给路由(哪个 URL 交给哪个函数)和模板引擎(HTML 里插变量),几十行代码就能起一个 Web 应用。
5.3 本课数据:八万条 UFO 目击记录
练习数据换成 NUFORC(美国国家 UFO 报告中心)的目击数据库,80,332 条记录,字段包括城市、州、国家、目击物形状、经纬度和持续时间。描述文本很有娱乐性:"一束光打在草地上,一个人走出来跑向德州仪器的停车场"、"那些光追着我们跑"。
建模问题:给定持续秒数、纬度、经度,预测这次目击发生在哪个国家。坦白说这不"智能"------经纬度本来就能定国家------但它是绝佳的交付流程练习:清洗 → 训练 → 导出 → 网页调用,一步不少。
案例实战:从 CSV 到网页表单
实测环境:conda env
ml-course(scikit-learn 1.9.1 / flask)完整脚本与产物:
stage2/assets/05/(含可运行的 web-app/)
第 1 步:清洗
python
ufos = pd.read_csv('ufos.csv')
ufos = pd.DataFrame({'Seconds': ufos['duration (seconds)'], 'Country': ufos['country'],
'Latitude': ufos['latitude'], 'Longitude': ufos['longitude']})
ufos.dropna(inplace=True)
ufos = ufos[(ufos['Seconds'] >= 1) & (ufos['Seconds'] <= 60)] # 只留 1-60 秒的目击
ufos['Country'] = LabelEncoder().fit_transform(ufos['Country'])
输出:原始行数 = 80332 → 过滤后行数 = 25863;Country 取值 us/gb/ca/au/de 五个,LabelEncoder 按字母序编码为 0-4。
第 2 步:训练
python
X = ufos[['Seconds', 'Latitude', 'Longitude']]
y = ufos['Country']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)
model = LogisticRegression(max_iter=1000).fit(X_train, y_train)
输出:Accuracy: 0.9700------经纬度与国家的强对应关系让任务本身不难,97% 合理。
第 3 步:pickle 导出与回读验证
python
import pickle
pickle.dump(model, open('ufo-model.pkl', 'wb'))
model = pickle.load(open('ufo-model.pkl', 'rb'))
print(model.predict([[50, 44, -12]])) # 50 秒,北纬 44°,西经 12°
输出:predict([50,44,-12]) = 3------国家码 3 = UK。这条坐标落在英国附近,模型答对了。
小坑说明:回读后 predict 时 sklearn 会提示
X does not have valid feature names------因为训练用的是带列名的 DataFrame、预测给的是 numpy 数组。这是无害警告;要消掉它,预测时也传 DataFrame 即可。
第 4 步:Flask 应用
目录结构:
web-app/
app.py # 路由 + 模型调用
templates/index.html # 表单页面
static/css/styles.css # 样式
../ufo-model.pkl # 上一步导出的模型
app.py 的核心------加载模型、两个路由:
python
import numpy as np
from flask import Flask, request, render_template
import pickle
app = Flask(__name__)
model = pickle.load(open("../ufo-model.pkl", "rb"))
@app.route("/")
def home():
return render_template("index.html")
@app.route("/predict", methods=["POST"])
def predict():
int_features = [int(float(x)) for x in request.form.values()]
prediction = model.predict([np.array(int_features)])
countries = ["Australia", "Canada", "Germany", "UK", "US"]
return render_template("index.html",
prediction_text="Likely country: {}".format(countries[prediction[0]]))
if __name__ == "__main__":
app.run(debug=True, port=5050)
index.html 里是一个三输入表单(秒数/纬度/经度),提交到 /predict,返回的 prediction_text 用模板语法 {``{ prediction_text }} 渲染。
第 5 步:起服务,真实请求验证
bash
cd web-app && python app.py # 起在 localhost:5050
实测三组请求:
GET / → 200(页面正常)
POST /predict (50, 44, -12) → "Likely country: UK"
POST /predict (30, 35, -80) → "Likely country: US"
第一组复现了 notebook 里的 UK 预测(一致性验证通过);第二组坐标在美国东南部,返回 US。从 CSV 到网页表单的全链路至此闭环。
这条链路里最容易错的点:数据形状。模型训练时吃 3 个特征(Seconds, Latitude, Longitude),网页输入就必须恰好这 3 个、恰好这个顺序。真实团队里,训练模型的人和写网页的人往往不是同一个------训练时把特征清单写进 README 或模型元数据,是避免扯皮的基本功。
debug=True 的提醒 :开发时改动代码自动重载很方便,生产环境绝不要开 debug(会暴露调试控制台)。本课程全在本机跑,无此风险。
两种模型上线方式对比:
| Flask + pickle(本课) | ONNX + 浏览器(第 9 课) | |
|---|---|---|
| 运行环境 | Python 服务端 | 任意静态服务器 |
| 前置条件 | Flask + 模型文件 | onnxruntime-web |
| 是否需后端 | 是 | 否(可离线) |
| 适用 | 全栈 Python 团队 | 纯前端分发 |
小结
- 交付模型先定四件事:web/移动、云/本地、离线与否、训练技术栈;scikit-learn + Flask + pickle 是最短路径。
- pickle 一行存模型、一行读回来直接 predict;不加载来路不明的 .pkl。
- 实测全链路:UFO 80332→25863 行,accuracy 0.9700,pickle 预测 UK 正确;Flask 服务 GET 200、两组 POST 分别返回 UK / US。
- 交付的最大坑是数据形状:训练 3 特征,调用就必须 3 特征同顺序------特征清单要随模型走。
- 下一阶段进入分类专题:参见第 6 课《分类入门:数据清洗与平衡(菜系)》;另一种导出格式 ONNX 参见第 9 课《菜系推荐 Web 应用》。
扩展阅读
- 原课第 5 课:https://github.com/microsoft/ML-For-Beginners/tree/main/3-Web-App/1-Web-App
- Flask 官方文档:https://palletsprojects.com/p/flask/
- Python pickle 文档(含安全提示):https://docs.python.org/3/library/pickle.html
- NUFORC 目击数据库:https://nuforc.org
系列导航
- 上一篇:第 04 课《逻辑回归:预测类别(南瓜)》
- 下一篇:第 06 课《分类入门:数据清洗与平衡(菜系)》
更多专栏:
| 蛋白 / 抗体 / 多肽 / 核酸 | 分子模拟 / 动力学 / 对接 | 药物 / 设计 / 案例 | AI / Agent / 大模型 |
|---|---|---|---|
| 开源蛋白结构预测 | 分子模拟基础 | 小分子药物设计案例 | AI Agent系列 |
| 开源蛋白生成方法实践 | 分子动力学模拟-Amber | 蛋白药物设计案例 | 化学大模型 |
| 开源多肽设计模型 | 分子动力学模拟-Gromacs | 多肽药物设计案例 | 《AI Agent原理与实战》 |
| 开源多肽性质预测 | 分子动力学模拟-OpenMM | 开源小分子生成设计 | CADD中的机器学习模型 |
| DNA/RNA药物设计 | 結合自由能 | 高效计算配置 | 《MCP 入门实战》 |
| siRNA药物设计模型 | UCSF DOCK系列 | 我胡师兄说药 | 《AI入门实战2026》 |
| ASO药物设计模型 | rDock系列 LeDock系列 gnina系列 | 《经典机器学习实战》 |