43. 机器学习模型推理与部署#

43.1. 介绍#

机器学习工程会利用本地训练的模型进行推理,并在有必要的时候将其部署到云端。本次实验将学会如何对 scikit-learn 构建的模型进行保存,部署和推理。

43.2. 知识点#

  • 模型保存

  • 模型部署

  • 模型推理

到目前为止,相信你已经对机器学习过程非常熟悉。一般情况下,我们会使用训练数据构建模型,然后再用验证数据或测试数据来评估模型。实际上,评估模型的过程在前面也称为预测和评价。

43.3. 模型推理#

实际上,用训练好的模型对新数据进行预测,在机器学习工程上有一个更专业的名词叫做「推理 Inference」。下图详细说明了神经网络模型训练和推理的过程。通过训练集构建的神经网络对新输入数据进行预测,就是推理。

https://cdn.huhuhang.com/hands-on-ai/images/document-uid214893labid7506timestamp1553237955368.png

一般情况下,推理又分为:静态推理与动态推理。

静态推理很好理解,我们通过集中对批量数据进行推理,并将结果存放在数据表或数据库中。当有需要的时候,再直接通过查询来获得推理结果。

而动态推理一般表示我们将模型部署到服务器中。当有需要时,通过向服务器发送请求来获得模型返回的预测结果。与静态推理不同的是,动态推理的过程是实时计算的,而静态推理是提前批量处理好的。

当然,静态和动态推理各有优缺点。静态推理适合于对大批量数据进行处理,因为动态推理面对大数据量时非常耗时。但是静态推理无法实时更新,而动态推理的结果是即时计算结果。

静态推理相信大家都很熟悉了,因为前面的内容中,我们对新数据预测实际上就类似于静态推理的过程。你只需要使用 scikit-learn 提供的 predict 操作即可完成。接下来,我们重点讨论动态推理的过程,并教你使用 RESTful API 的方式部署 scikit-learn 模型并完成动态推理。

43.4. 模型部署#

想要部署 scikit-learn 模型,当然需要先完成模型训练。

下面,我们训练一个泰坦尼克号生存推理模型。数据集通过 seaborn 进行加载,并完成预览。

from seaborn import load_dataset
import pandas as pd
import numpy as np
import warnings

warnings.filterwarnings("ignore")  # 忽略模块变动警告

df = load_dataset("titanic")  # 加载泰坦尼克数据集
df
survived pclass sex age sibsp parch fare embarked class who adult_male deck embark_town alive alone
0 0 3 male 22.0 1 0 7.2500 S Third man True NaN Southampton no False
1 1 1 female 38.0 1 0 71.2833 C First woman False C Cherbourg yes False
2 1 3 female 26.0 0 0 7.9250 S Third woman False NaN Southampton yes True
3 1 1 female 35.0 1 0 53.1000 S First woman False C Southampton yes False
4 0 3 male 35.0 0 0 8.0500 S Third man True NaN Southampton no True
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
886 0 2 male 27.0 0 0 13.0000 S Second man True NaN Southampton no True
887 1 1 female 19.0 0 0 30.0000 S First woman False B Southampton yes True
888 0 3 female NaN 1 2 23.4500 S Third woman False NaN Southampton no False
889 1 1 male 26.0 0 0 30.0000 C First man True C Cherbourg yes True
890 0 3 male 32.0 0 0 7.7500 Q Third man True NaN Queenstown no True

891 rows × 15 columns

可以看的,数据集一共包含 15 列共 891 个样本。我们选择乘客仓位 pclass,性别 sex,登船港口 embarked 等 3 个特征,并使用 alive 是否存活作为目标值。

X = df[["pclass", "sex", "embarked"]]  # 特征
y = df["alive"]  # 目标

训练之前,我们先对特征数据进行独热编码,独热编码的方法前面已经介绍过了。

X = pd.get_dummies(X)  # 独热编码
X.head()
pclass sex_female sex_male embarked_C embarked_Q embarked_S
0 3 False True False False True
1 1 True False True False False
2 3 True False False False True
3 1 True False False False True
4 3 False True False False True

接下来,就可以开始训练了。这里使用随机森林方法建模,并使用交叉验证来查看模型的表现。

from sklearn.model_selection import cross_val_score
from sklearn.ensemble import RandomForestClassifier

model = RandomForestClassifier()  # 随机森林
np.mean(cross_val_score(model, X, y, cv=5))  # 5 次交叉验证求平均
0.8114556525014125

交叉验证显示模型的分类准确度大约为 81%。

为了方便模型的部署,我们需要把训练好的模型存储下来。这里可以使用 scikit-learn 提供的 sklearn.externals.joblib,将模型存为 .pkl 二进制文件。保存模型的方法非常简单,直接阅读下面的代码。

import joblib

model.fit(X, y)  # 训练模型
joblib.dump(model, "titanic.pkl")  # 保存模型
['titanic.pkl']

现在,有了模型文件,那么就可以部署模型了。我们打算将模型部署到云端(本地测试),这里借助于 Flask 来实现。Flask 是 Python 著名的 Web 应用框架,可以用于构建一个 RESTful API。由于本课程未涉及 Flask 的内容,下面的代码大家需自行结合 官方文档 理解。

%writefile predict.py
from flask import Flask, request, jsonify
import joblib
import pandas as pd

app = Flask(__name__)


@app.route("/", methods=["POST"])  # 请求方法为 POST
def predict():
    json_ = request.json  # 解析请求数据
    query_df = pd.DataFrame(json_)  # 将 JSON 变为 DataFrame
    columns_onehot = [
        "pclass",
        "sex_female",
        "sex_male",
        "embarked_C",
        "embarked_Q",
        "embarked_S",
    ]  # 独热编码 DataFrame 列名
    query = pd.get_dummies(query_df).reindex(
        columns=columns_onehot, fill_value=0
    )  # 将请求数据 DataFrame 处理成独热编码样式
    clf = joblib.load("titanic.pkl")  # 加载模型
    prediction = clf.predict(query)  # 模型推理
    return jsonify({"prediction": list(prediction)})  # 返回推理结果
Overwriting predict.py

首先在终端执行 predict.py 启动 Flask app。

$ python predict.py

* Serving Flask app "predict" (lazy loading)
* Environment: production
WARNING: Do not use the development server in a production environment.
Use a production WSGI server instead.
* Debug mode: off
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

开启新终端向目的地址发送请求。

In [1]: import requests                                                                             

In [2]: sample = [{"pclass": 1, "sex": "male", "embarked": "C"}, {"pclass": 2, "sex": "female", "embarked": "S"}]                             
In [3]: requests.post(url='http://127.0.0.1:5000', json=sample).content                             
Out[3]: b'{"prediction":["no","yes"]}\n'

这里,我们给出实验示例模型部署在云端的测试地址:https://titanic-demo.onrender.com,你可以直接获得测试结果:

import requests

# 向服务器发送请求获得预测结果
sample = [
    {"pclass": 1, "sex": "male", "embarked": "C"},
    {"pclass": 2, "sex": "female", "embarked": "S"},
    {"pclass": 3, "sex": "male", "embarked": "Q"},
    {"pclass": 3, "sex": "female", "embarked": "S"},
]

# 稍等片刻,Render 线上服务存在冷却启动时间
requests.post(url="https://titanic-demo.onrender.com", json=sample).content
b'{"predict":["no","yes","no","no"]}\n'

你可以阅读并参考该项目的 源代码

43.5. 总结#

本次课程学习了 scikit-learn 模型的保存,部署与动态推理。由于涉及到 Flask 的使用,部分知识需要学员额外自行补充学习。不过,相信你从实验的内容中已经能够体会完整的过程。实际上,随着云技术的发展,线上部署模型变得更加方便,类似于 Google Cloud 推出的云端函数或者 AWS Lambda 功能,都能够在无服务器情况下实现机器学习模型快速部署,有兴趣可以自行搜索学习。