本文的想法是快速轻松地构建 Docker 容器,Python 以使用 Flask 实现机器学习模型执行在线预测 API 。我们将使用 Docker 和 Flask-RESTful 实现线性判别分析和多层感知器神经网络模型的实时预测。
项目包括的文件有:Dockerfile,train.py,api.py,requirements.txt, train.csv,test.json。
Dockerfile 将被用来构建 Docker 镜像
train.py 使用规范化的 EEG 数据训练两个分类模型(线性判别分析和多层感知器神经网络模型)
api.py 是将被调用执行使用 REST API 的在线预测接口脚本
requirements.txt(flask, flask-restful, joblib)是 Python 依赖
train.csv 用于训练模型的数据
test.json 是一个 json 文件,包含待预测的数据
Python 构建 Flask-restful API
第一步先思考我们需要构建哪些接口,输入和输出分别是什么。在这个例子里,我们将使用 包含 1300 行,160 列特征的 EGG 数据 test.json 文件。我们想要实现的 API 包括:
查询数据
请求方式:GET
请求示例:http://0.0.0.0:5000/line/232
参数说明:line, 必须, 行号
返回结果:
{"Line": "232", "# Letter": "4", ...}
获取预测结果 请求方式:GET
请求示例:http://0.0.0.0:5000/prediction/232
参数说明:line, 必须, 行号
返回结果:
{
"prediction LDA": "21",
"prediction Neural Network": 8
}
结果说明:
获取模型效果评分 请求方式:GET
请求示例:http://0.0.0.0:5000/score
返回结果:
{
"Score LDA": 0.17846,
"Score Neural Network": 0.596923
}
结果说明:
最后,我们能通过 HTTP 请求来获取结果,该 api.py 文件实现如下:
# We now need the json library so we can load and export json data
import json
import os
import numpy as np
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from sklearn.neural_network import MLPClassifier
import pandas as pd
from joblib import load
from sklearn import preprocessing
from flask import Flask
# Set environnment variables
MODEL_DIR = os.environ["MODEL_DIR"]
MODEL_FILE_LDA = os.environ["MODEL_FILE_LDA"]
MODEL_FILE_NN = os.environ["MODEL_FILE_NN"]
MODEL_PATH_LDA = os.path.join(MODEL_DIR, MODEL_FILE_LDA)
MODEL_PATH_NN = os.path.join(MODEL_DIR, MODEL_FILE_NN)
# Loading LDA model
print("Loading model from: {}".format(MODEL_PATH_LDA))
inference_lda = load(MODEL_PATH_LDA)
# loading Neural Network model
print("Loading model from: {}".format(MODEL_PATH_NN))
inference_NN = load(MODEL_PATH_NN)
# Creation of the Flask app
app = Flask(__name__)
#API 1
# Flask route so that we can serve HTTP traffic on that route
@app.route('/line/<Line>')
# Get data from json and return the requested row defined by the variable Line
def line(Line):
with open('./test.json', 'r') as jsonfile:
file_data = json.loads(jsonfile.read())
# We can then find the data for the requested row and send it back as json
return json.dumps(file_data[Line])
#API 2
# Flask route so that we can serve HTTP traffic on that route
@app.route('/prediction/<int:Line>',methods=['POST', 'GET'])
# Return prediction for both Neural Network and LDA inference model with the requested row as input
def prediction(Line):
data = pd.read_json('./test.json')
data_test = data.transpose()
X = data_test.drop(data_test.loc[:, 'Line':'# Letter'].columns, axis = 1)
X_test = X.iloc[Line,:].values.reshape(1, -1)
clf_lda = load(MODEL_PATH_LDA)
prediction_lda = clf_lda.predict(X_test)
clf_nn = load(MODEL_PATH_NN)
prediction_nn = clf_nn.predict(X_test)
return {'prediction LDA': int(prediction_lda), 'prediction Neural Network': int(prediction_nn)}
#API 3
# Flask route so that we can serve HTTP traffic on that route
@app.route('/score',methods=['POST', 'GET'])
# Return classification score for both Neural Network and LDA inference model from the all dataset provided
def score():
data = pd.read_json('./test.json')
data_test = data.transpose()
y_test = data_test['# Letter'].values
X_test = data_test.drop(data_test.loc[:, 'Line':'# Letter'].columns, axis = 1)
clf_lda = load(MODEL_PATH_LDA)
score_lda = clf_lda.score(X_test, y_test)
clf_nn = load(MODEL_PATH_NN)
score_nn = clf_nn.score(X_test, y_test)
return {'Score LDA': score_lda, 'Score Neural Network': score_nn}
if __name__ == "__main__":
app.run(debug=True, host='0.0.0.0')
首先,导入依赖的包和相关的环境变量,该环境变量设置在 Dockerfile 文件中。然后载入线性判别分析模型和多层感知器神经网络模型,这两个模型是在 train.py 文件中训练并保存的。最后我们通过 app = Flask(name) 构建 Flask 应用,构建了三个 Flask 路由可以来通过 HTTP 请求访问。
http://0.0.0.0:5000/line/250:从 test.json 文件获取数据,返回请求的数据行,本案例中提取第 250 行数据
http://0.0.0.0:5000/prediction/51: 返回输入的对应行号数据的模型预测结果,本案例为第 51 行数据的预测结果
http://0.0.0.0:5000/score: 返回所有数据测试得到的分类模型评估效果
Flask 路由可以通过修改 URL(http://0.0.0.0:5000) 中的路由变量来请求我们需要的接口数据(/line/,/prediction/<int:Line>,/score)。
机器学习模型
train.py 利用 train.csv 数据训练两个分类模型。该脚本最后保存了两个模型:线性判别分析(clf_lda)和神经网络多层感知器(clf_NN):
import platform; print(platform.platform())
import sys; print("Python", sys.version)
import numpy; print("NumPy", numpy.__version__)
import scipy; print("SciPy", scipy.__version__)
import os
import numpy as np
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from sklearn.neural_network import MLPClassifier
import pandas as pd
from joblib import dump
from sklearn import preprocessing
def train():
# Load directory paths for persisting model
MODEL_DIR = os.environ["MODEL_DIR"]
MODEL_FILE_LDA = os.environ["MODEL_FILE_LDA"]
MODEL_FILE_NN = os.environ["MODEL_FILE_NN"]
MODEL_PATH_LDA = os.path.join(MODEL_DIR, MODEL_FILE_LDA)
MODEL_PATH_NN = os.path.join(MODEL_DIR, MODEL_FILE_NN)
# Load, read and normalize training data
training = "./train.csv"
data_train = pd.read_csv(training)
y_train = data_train['# Letter'].values
X_train = data_train.drop(data_train.loc[:, 'Line':'# Letter'].columns, axis = 1)
print("Shape of the training data")
print(X_train.shape)
print(y_train.shape)
# Data normalization (0,1)
X_train = preprocessing.normalize(X_train, norm='l2')
# Models training
# Linear Discrimant Analysis (Default parameters)
clf_lda = LinearDiscriminantAnalysis()
clf_lda.fit(X_train, y_train)
# Serialize model
from joblib import dump
dump(clf_lda, MODEL_PATH_LDA)
# Neural Networks multi-layer perceptron (MLP) algorithm
clf_NN = MLPClassifier(solver='adam', activation='relu', alpha=0.0001, hidden_layer_sizes=(500,), random_state=0, max_iter=1000)
clf_NN.fit(X_train, y_train)
# Serialize model
from joblib import dump, load
dump(clf_NN, MODEL_PATH_NN)
if __name__ == '__main__':
train()
构建 Docker 镜像
先使用 jupyter/scipy-notebook 作为我们的基础镜像
创建 my-model 文件夹,并设置环境变量,载入训练的模型
将镜像的依赖文件复制到镜像中,并安装相关包
我们复制 train.csv, test.json, train.py and api.py 文件到镜像中
运行 python3 来执行 train.py 文件,该文件将训练模型并保存到之前设置好的环境路径中
FROM jupyter/scipy-notebook
RUN mkdir my-model
ENV MODEL_DIR=/home/jovyan/my-model
ENV MODEL_FILE_LDA=clf_lda.joblib
ENV MODEL_FILE_NN=clf_nn.joblib
COPY requirements.txt ./requirements.txt
RUN pip install -r requirements.txt
COPY train.csv ./train.csv
COPY test.json ./test.json
COPY train.py ./train.py
COPY api.py ./api.py
RUN python3 train.py
通过以下命令行构建镜像,这里 -f 加文件名表示以当前路径下的 Dockerfile 构建镜像,注意这里文件名参数一定要与你要构建镜像的文件名一致。
docker build -t my-docker-api -f Dockerfile .
命令行执行后看到以下的输出才表示成功:
运行 Docker 容器
构建好以上的镜像后,我们来基于该镜像启动容器,启动容器后将通过 python3 来执行 api.py 文件:
这里 -it 是两个参数,一个是 -i:交互式操作,一个是 -t 终端。我们这里打算用 python 执行 api.py 文件并查看返回结果,因此我们需要交互式终端
-p 5000:5000:表示映射端口,冒号前为宿主机端口,冒号后为镜像端口
my-docker-api:表示以镜像 my-docker-api 启动容器
python3 api.py:表示启动容器后,用 python3 执行 api.py 文件
docker run -it -p 5000:5000 my-docker-api python3 api.py
我们可以看到通过访问 http://0.0.0.0:5000/line/232 就可以得到第 232 行数据:
curl http://0.0.0.0:5000/line/232
通过访问 http://0.0.0.0:5000/prediction/232 就可以得到第 232 行数据的预测结果,其中 LDA 模型预测分类结果为 21,而神经网络模型为 8,那么哪个结果更接近真实结果呢?
curl http://0.0.0.0:5000/prediction/232
通过访问 http://0.0.0.0:5000/score 得到两个模型的分类效果,其中 LDA 模型效果指标为 0.178,而神经网络为 0.570,因此神经网络的模型结果更接近真实值。
curl http://0.0.0.0:5000/score
源码:https://github.com/Serena-TT/DataSciencePipline
选择 E1 即可。