TensorFlow Serving + Docker + Tornado机器学习模型部署

训练模型

使用Titanic数据集搭建模型,预测乘客在Titanic号撞击冰山沉没后能否生存

  1. 数据的准备
    Preparing Data
    Survived:0代表死亡,1代表存活【y标签】
    Pclass:乘客所持票类,有三种值(1,2,3) 【转换成onehot编码】
    Name:乘客姓名 【舍去】
    Sex:乘客性别 【转换成bool特征】
    Age:乘客年龄(有缺失) 【数值特征,添加“年龄是否缺失”作为辅助特征】
    SibSp:乘客兄弟姐妹/配偶的个数(整数值) 【数值特征】
    Parch:乘客父母/孩子的个数(整数值)【数值特征】
    Ticket:票号(字符串)【舍去】
    Fare:乘客所持票的价格(浮点数,0-500不等) 【数值特征】
    Cabin:乘客所在船舱(有缺失) 【添加“所在船舱是否缺失”作为辅助特征】
    Embarked:乘客登船港口:S、C、Q(有缺失)【转换成onehot编码,四维度 S,C,Q,nan】
import pandas as pd
# 结构化数据一般会使用Pandas中的DataFrame进行预处理
dftrain_raw = pd.read_csv('data/train.csv')
dftest_raw = pd.read_csv('data/test.csv')
# print(dftrain_raw.head(10))

# 预处理函数
def preprocessing(dfdata):
    # 1、将Pclass乘客所持票类,(1,2,3),转换成onehot编码
    dfresult=pd.DataFrame()
    dfPclass=pd.get_dummies(dfdata["Pclass"])  # get_dummies 是利用pandas实现one hot encode
    # print(dfPclass)  #  1  2  3
    # 设置列名
    dfPclass.columns =['Pclass_'+str(x) for x in dfPclass.columns]
    dfresult = pd.concat([dfresult, dfPclass], axis=1)
    # print(dfresult) # Pclass_1  Pclass_2  Pclass_3

    # 2、将Sex转换为bool特征
    dfSex = pd.get_dummies(dfdata['Sex'])
    dfresult = pd.concat([dfresult, dfSex], axis=1)
    # print(dfresult)  # female  male

    # 3、Age用0填充Age列缺失值,并重新定义一列Age_null用来标记缺失值的位置
    dfresult['Age'] =dfdata['Age'].fillna(0)  # 将缺失值用0填充
    dfresult['Age_null'] = pd.isna(dftrain_raw['Age']).astype('int32')  # 增加一列Age_null,不为0的记0,为0的记1,就是标记出现0的位置
    # print(dfresult)

    # 4、直接拼接SibSp、Parch、Fare不动
    dfresult['SibSp'] = dfdata['SibSp']
    dfresult['Parch'] = dfdata['Parch']
    dfresult['Fare'] = dfdata['Fare']
    # print(dfresult)  # Pclass_1  Pclass_2  Pclass_3  female  ...  Age_null  SibSp  Parch     Fare

    # 5、Cabin:乘客所在船舱(有缺失) 【添加“所在船舱是否缺失”作为辅助特征】
    dfresult['Cabin_null'] = pd.isna(dfdata['Cabin']).astype('int32')
    # print(dfresult)

    # 6、Embarked乘客登船港口:S、C、Q(有缺失)【转换成onehot编码,四维度 S,C,Q,nan】
    dfEmbarked = pd.get_dummies(dfdata['Embarked'], dummy_na=True)  # 需要注意的参数是dummy_na=True,将缺失值另外标记出来
    dfEmbarked.columns = ['Embarked_' + str(x) for x in dfEmbarked.columns]
    dfresult = pd.concat([dfresult, dfEmbarked], axis=1)
    # print(dfresult)  # Pclass_1  Pclass_2  Pclass_3  ...  Embarked_Q  Embarked_S  Embarked_nan
    return (dfresult)


# if __name__ == '__main__':
#     x_train = preprocessing(dftrain_raw)
#     y_train = dftrain_raw['Survived'].values
#     print("x_train.shape =", x_train.shape)
#
#     x_test = preprocessing(dftest_raw)
#     y_test = dftest_raw['Survived'].values
#     print("x_test.shape =",x_test.shape)



  1. 模型的训练和保存
    采用sequential模型搭建方法
from prepare_data import preprocessing
import pandas as pd
import tensorflow as tf
from tensorflow.keras import models, layers
from keras import models, layers
import matplotlib.pyplot as plt
from keras.callbacks import TensorBoard
import tempfile
import os
import keras
dftrain_raw = pd.read_csv('data/train.csv')
dftest_raw = pd.read_csv('data/test.csv')
x_train = preprocessing(dftrain_raw)
y_train = dftrain_raw['Survived'].values
print("x_train.shape =", x_train.shape)

x_test = preprocessing(dftest_raw)
y_test = dftest_raw['Survived'].values
print("x_test.shape =", x_test.shape)

# Sequential,按层顺序模型
def create_model():
    model = models.Sequential()
    model.add(layers.Dense(20, activation='relu', input_shape=(15,)))
    model.add(layers.Dense(10, activation='relu'))
    model.add(layers.Dense(10, activation='relu'))
    model.add(layers.Dense(1, activation='sigmoid'))  # 最后一层sigmoid输出二分类概率
    return model
# model.summary()  # 计算参数

# 二分类问题选择二元交叉熵损失函数
def compile_model(model):
    model.compile(optimizer='adam',
                  loss='binary_crossentropy',
                  metrics=['AUC'])
    return model

# 模型训练
# 使用TensorBoard查看训练过程,在keras里面只需要在fit的时候加上一个callback让他产生日志
def train_model(model,x_train, y_train,batch_size=64,epochs=300):
    # 构造一个Tensorboard类的对象
    tbCallBack = TensorBoard(log_dir="./logs")

    # fit函数均返回History对象
    # history是一个字典,包含训练过程中所有数据,字典包含4个条目,对应训练过程和验证过程中监控指标
    history = model.fit(x_train, y_train,
                        batch_size=batch_size,
                        epochs=epochs,
                        shuffle=True,
                        verbose=2,
                        validation_split=0.2,  # 分割一部分训练数据用于验证
                        callbacks=[tbCallBack])
    return history, model

# 评估模型
def plot_metric(history, metric):
    train_metrics = history.history[metric]
    val_metrics = history.history['val_' + metric]
    epochs = range(1, len(train_metrics) + 1)
    plt.plot(epochs, train_metrics, 'bo--')
    plt.plot(epochs, val_metrics, 'ro-')
    plt.title('Training and validation ' + metric)
    plt.xlabel("Epochs")
    plt.ylabel(metric)
    plt.legend(["train_" + metric, 'val_' + metric])
    plt.show()

if __name__=="__main__":
    model = create_model()
    model = compile_model(model)
    history, model = train_model(model, x_train, y_train)
    plot_metric(history, "loss")
    plot_metric(history, "auc")

    # 测试集上的效果
    model.evaluate(x=x_test, y=y_test)  # 用于评估已经训练过的模型。返回模型的损失值和指标值

    # 使用模型
    print(model.predict(x_test[0:10]))
    print(model.predict_classes(x_test[0:10]))  # 预测类别

    # 保存模型
    # 可以使用Keras方式保存模型,也可以使用TensorFlow原生方式保存。前者仅仅适合使用Python环境恢复模型,后者则可以跨平台进行模型部署

    # 1、Keras方式保存
    # 保存模型结构及权重
    #
    tf.saved_model.save(model, "./model/1")
    del model  # 删除现有模型
    # identical to the previous one
    model = models.load_model('./model/1')
    model.evaluate(x_test, y_test)

    # 2、TensorFlow原生方式保存
    # 保存权重,该方式仅仅保存权重张量
    # model.save_weights('./model/tf_model_weights.ckpt', save_format="tf")
    # # 保存模型结构与模型参数到文件,该方式保存的模型具有跨平台性便于部署
    # model.save('./model/1', save_format="tf")
    # print('export saved model.')



  1. 模型的预测
from Sequential_model import x_test, y_test
import tensorflow as tf

model_loaded = tf.keras.models.load_model('./model/tf_model_savedmodel')
model_loaded.evaluate(x_test, y_test)
# 使用模型
print(model_loaded.predict_classes(x_test[0:10]))  # 预测类别

部署简介

当我们训练完一个tensorflow或keras模型后,需要把它做成一个服务,让使用者通过某种方式来调用你的模型,需要把模型部署到服务器上。常用的做法如使用flask、Django、tornado等web框架创建一个服务器app,这个app在启动后就会一直挂在后台,然后等待用户使用客户端POST一个请求上来,app检测到有请求,接着调用你的模型,得到推理结果后以json的格式把结果返回给用户。
对于使用tensorflow、keras框架进行算法开发的用户来说,TensorFlow Serving能够把模型挂在服务器后台,只需要写一个客户端把请求发过去,它就会把运算后的结果返回给你。而TensorFlow Serving的最佳使用方式就是使用一个已经编译好TensorFlow Serving功能的docker,你所要做的只是简单的运行这个docker即可。
TensorFlow Serving还支持同时挂载多个模型或者多个版本的模型,只需简单地指定模型名称即可调用相应的模型,无需多写几份代码、运行多个后台服务。

使用tfserving进行模型部署

TensorFlow Serving只需要一个导出的tensorflow或keras模型文件,这个模型文件定义了整个模型的计算图,因此我们首先把一个训练好的模型进行导出,导出的文件结构如下,导出的模型需要在外层加一层目录命名为 1 ,后续tfserving会以此作为模型的版本标识。目录结构如下:
在这里插入图片描述
导出以后,我们在终端执行以下命令,查看模型的signature、input、output的名称,后面要用到:saved_model_cli show --dir {export_path+str(version)} --all
在这里插入图片描述
可以看到,signature name为“serving_default”,input name为“dense_input”,output name为“dense_3”。记下来,一会用到。

Docker

  1. 安装docker
    TensorFlow Serving的安装推荐使用docker,所以必须先安装docker,如果你运行安装测试时看到以下语句,证明安装成功:
    在这里插入图片描述
  2. 拉取TensorFlow Serving镜像docker pull tensorflow/serving:latest-devel
    ( 拉取tensorflow serving镜像默认会拉去最新的latest版本docker pull tensorflow/serving
    端口8500暴露于gRPC
    端口8501暴露给REST API
    可选环境变量MODEL_NAME(默认为model)
    可选环境变量MODEL_BASE_PATH(默认为/models)
    要使用Docker,您需要:主机上的开放端口 要保存的SavedModel 客户将引用的模型名称
    你要做的是 运行Docker容器, 将 容器的端口发布到主机的端口,并将主机的路径安装到容器所需模型的SavedModel。)
  3. 使用命令查看安装好的镜像,tensorflow/serving为本步安装好的镜像docker images

进入devel版镜像的容器内部启动

如果你使用的是devel版本,希望进入容器内部的终端配置自己想要的环境,我们使用以下命令进入容器:docker run -it -p 8500:8500 tensorflow/serving:latest-devel。如下图,说明已经进入docker
在这里插入图片描述
-it的意思是以交互的方式进入容器内部,当你想把本地文件夹里面的文件复制到容器内部的某个文件夹内,可以使用docker cp命令,需要另开一个终端,记住不要在docker容器里面,将自己的模型文件拷贝到容器中,container id查询命令为docker ps

docker cp D:/tf_model_savedmodel   78ae6493ed3a:/

在这里插入图片描述

在容器中运行tensorflow_model_server服务

 tensorflow_model_server --port=8500 --model_name=titanic --model_base_path=/tf_model_savedmodel

在这里插入图片描述

镜像保存

默认情况下,镜像退出后,所有操作都将清零,可以使用commit命令进行对现有镜像进行新的构建。使用exit命令退出现有的docker。注意以下命令不要在容器内部的shell执行,新开一个命令行。构建新的镜像docker commit 78ae6493ed3a tensorflow/titanic78ae6493ed3a为上文退出的镜像的 container ID (docker ps)。tensorflow/titanic为目标镜像仓库、镜像名。可自行设定。进入镜像,即为新构建的镜像,之前做的修改均可复用。
有时候执行了一些挂在后台的tensorflow serving服务,即使你exit退出容器或者ctrl+c都不会杀死这个服务,如果你想杀死不想再用的后台应用,输入docker ps来查看正在运行的容器,然后docker kill IMAGE_NAME就可以杀死服务。

停止容器

docker stop 要停止容器的 CONTAINER ID

进入容器

docker ps -ls #查看后台运行的容器
docker exec -it 容器ID/容器名 /bin/bash # 进入容器

启动一个已经停止的容器

docker start 容器ID或容器名

  1. 先查看已经暂停的容器实例信息 docker ps -ls
  2. 通过docker start 容器ID或容器名 启动容器
  3. 通过docker ps 查看当前启动的容器
docker ps -ls
docker exec -it 63c48599081e  /bin/bash
docker start 63c48599081e
docker exec -it 63c48599081e  /bin/bash

在这里插入图片描述
将D盘下面的titanic模型复制到docker容器中
docker cp D:/model 63c48599081e:/在这里插入图片描述
在容器中运行tensorflow_model_server服务

docker exec -it 0fbe56f03c4f /bin/bash
tensorflow_model_server --port=8501 --model_name=titanic --model_base_path=/model

在dockers中直接运行服务

docker run -p 8500:8500 -p 8501:8501 --name titanic  --mount type=bind,source=D:/model,target=/models/titanic  -e MODEL_NAME=titanic -t tensorflow/serving

在这里插入图片描述
–mount: 表示要进行挂载
source: 指定要运行部署的模型地址, 也就是挂载的源,这个是在宿主机上的servable模型目录(pb格式模型而不是checkpoint模型)
target: 这个是要挂载的目标位置,也就是挂载到docker容器中的哪个位置,这是docker容器中的目录,模型默认挂在/models/目录下,如果改变路径会出现找不到model的错误
-t: 指定的是挂载到哪个容器
-d: 后台运行
-p: 指定主机到docker容器的端口映射
-e: 环境变量
-v: docker数据卷
–name: 指定容器name,后续使用比用container_id更方便

调用服务

curl http://localhost:8501/v1/models/titanic
curl -d "{\"instances\": [[0, 0, 1, 1, 0, 0.0, 0, 8, 2, 69.55, 1, 0, 0, 1, 0]]}"  -X POST http://localhost:8501/v1/models/titanic:predict

在这里插入图片描述

python客户端调用服务

TensorFlow Serving启动后,我们需要用一个客户端来发送预测请求,跟以往请求不同的是,TensorFlow Serving使用的是http协议,传入的数据格式需要json。

  1. 查看模型信息
    在pycharm的terminal端输入下面命令
saved_model_cli show --dir model/1/ --all

在这里插入图片描述

可以看到,signature name为“serving_default”,input name为“dense_input”,output name为“dense_3”。记下来,一会用到。input的shape是 (-1, 15)二维矩阵,列数为15,行数不定。输入模型数据的形式要准确。输出是shape: (-1, 1)也是二维矩阵,列数为1,行数不定。

  1. client demo

TensorFlow Serving启动后,我们需要用一个客户端来发送预测请求,跟以往请求不同的是,TensorFlow Serving使用的是gRPC协议,我们的客户端需要安装使用gRPC的API,以特定的方式进行请求以及接收结果。

  1. 安装
pip install tensorflow-serving-api
  1. Client Demo
data='{"inputs":[[0,0,1,1,0,0.0,0,8,2,69.55,1,0,0,1,0]]}'

tfserving文档自带的例子

docker run -p 8501:8501 --mount type=bind,source=C:/Users/yangliu3/tmp/tfserving/serving/tensorflow_serving/servables/tensorflow/testdata/saved_model_half_plus_two_cpu,target=/models/half_plus_two -e MODEL_NAME=half_plus_two -t tensorflow/serving &

在这里插入图片描述

curl http://localhost:8501/v1/models/half_plus_two
curl http://localhost:8501/v1/models/half_plus_two

在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值