[Kubeflow系列]番外:TensorServing例子

缘起

最近做产品的时候, 由算法团队写完AI算法模型, 由我们产品团队完成对工程化的改造, 然后再把API上线.

但是这个时候遇到沟通成本很高, AI算法大多是探索式的, 输入是比较少边的, 但是输出可能会多次变化, 这样对产品化人员又很麻烦, 本来就是很简单的代码, 就是需要不停的修改, 用来适配整体的API.

这里有个前提: 尽量让算法的人员减少对产品的代码的感知, 毕竟他们不是专业的

因此, 我查了一下开源社区对这个问题的处理方式, 最后还真查到了TF Serving这个工程, 决定调研一下是否能够解决我的问题.

TF ServingTensorFlow Extended工程的一个子工程, TFX是一个端到端的AI平台, 简单理解就是:

他的关注点在于AI机器学习的工程化部分

TFX总共有4个模块:

  1. TensorFlow Data Validation: TensorFlow Data Validation (TFDV) 能够帮助开发者大规模地理解、验证和监控机器学习数据。Google 每天都使用 TFDV 分析和验证 PB 级的数据,并且在帮助 TFX 用户维护机器学习流水线正常运行方面,TFDV 一贯表现良好
  2. TensorFlow Transform: 在将机器学习应用于现实世界的数据集时,需要投入很多精力才能将数据预处理为合适的格式,其中包括在各种格式之间进行转换、对文本进行标记化和词干化、创建词汇表、执行归一化等各种数字操作。您可以使用 tf.Transform 完成所有这些操作
  3. TensorFlow Model Analysis: TensorFlow Model Analysis (TFMA) 让开发者能够计算和可视化模型的评估指标。在部署任何机器学习 (ML) 模型之前,机器学习开发者需要评估模型的性能,以确保其达到特定的质量阈值,并且能够针对所有相关数据切片展示出与预期相符的行为。例如,模型针对整个评估数据集的 AUC 可能是可接受的,但针对特定切片却表现不佳。TFMA 为开发者提供了深入了解其模型性能的工具
  4. TensorFlow Serving: 机器学习 (ML) 应用系统必须支持模型版本控制(用于实现包含回滚选项的模型更新)和多个模型(用于实现通过 A/B 测试进行的实验),同时还要确保并发模型能够在硬件加速器(GPU 和 TPU)上以较低的延迟实现较高的吞吐量。TensorFlow Serving 每秒能为 Google 处理数千万次推断

这4个模块都是很有用的点, 但这次我的重点在TensorFlow Serving

TensorFlow Serving

在网上的教程之中, 就一个提供模型章节详细介绍了TF Serving的一个例子

按照Notebook的教程运行一下这个例子(不需要使用GPU, 使用CPU的速度就可以了), 我们简单的分解一下这个过程

直接在本地安装一个Notebook就可以运行, 除了安装tensorflow-model-server可能需要翻墙, 需要手动设置一起, 其他的可以直接运行出来

训练过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 定义模型
model = keras.Sequential([
keras.layers.Conv2D(input_shape=(28,28,1), filters=8, kernel_size=3,
strides=2, activation='relu', name='Conv1'),
keras.layers.Flatten(),
keras.layers.Dense(10, activation=tf.nn.softmax, name='Softmax')
])
model.summary()

testing = False
epochs = 5

model.compile(optimizer=tf.train.AdamOptimizer(),
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
# 开始训练
model.fit(train_images, train_labels, epochs=epochs)

# 使用测试集推理
test_loss, test_acc = model.evaluate(test_images, test_labels)
print('\nTest accuracy: {}'.format(test_acc))

实际上在模型定义的步骤之中, 模型的输入输出实际上已经定了, 但是这里的输入应该只能是tensor类型的

使用工具, 查看查看模型输入输出:

1
!saved_model_cli show --dir {export_path} --all
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

MetaGraphDef with tag-set: 'serve' contains the following SignatureDefs:

signature_def['serving_default']:
The given SavedModel SignatureDef contains the following input(s):
inputs['input_image'] tensor_info:
dtype: DT_FLOAT
shape: (-1, 28, 28, 1)
name: Conv1_input:0
The given SavedModel SignatureDef contains the following output(s):
outputs['Softmax/Softmax:0'] tensor_info:
dtype: DT_FLOAT
shape: (-1, 10)
name: Softmax/Softmax:0
Method name is: tensorflow/serving/predict

创建Serving时候, 参数只要指定模型的路径即可

1
2
3
4
nohup tensorflow_model_server \
--rest_api_port=8501 \
--model_name=fashion_model \
--model_base_path="${MODEL_DIR}" >server.log 2>&1

使用客户端, 给8501端口发送请求, 地址为http://localhost:8501/v1/models/fashion_model:predict

1
2
3
4
5
6
7
import requests
headers = {"content-type": "application/json"}
json_response = requests.post('http://localhost:8501/v1/models/fashion_model:predict', data=data, headers=headers)
predictions = json.loads(json_response.text)['predictions']

show(0, 'The model thought this was a {} (class {}), and it was actually a {} (class {})'.format(
class_names[np.argmax(predictions[0])], test_labels[0], class_names[np.argmax(predictions[0])], test_labels[0]))

除了HTTP请求, TFServing还支持GRPC协议, 端口默认开在8500端口

GRPC的例子可以看github上的例子

总结

从这个例子上来看, TF Serving并不能完全解决上面的问题, 因为模型的输入输出的定义肯定是矩阵模式, 而实际产品之中的输入是非常多样化的, 可以是一个npp文件也可以是一张图片, 显然还需要一个预处理的步骤,才能达到Serving的步骤.

TF Serving也是用的:

  1. 不需要写模型推理代码了, 只要统一使用了TensorFlow框架, 推理就简单了
  2. 它有简单的模型版本控制, 用于不同版本之间的升级测试等问题(实际上就是一个简单的版本编号而已)

那么回过头来, 如果想要解决产品和算法团队之间的GAP呢?

我想到的方式是, 使用Kubeflow的Pipeline + TFServing:

  1. 将常见的输入输出算法转化为component, 直接可以使用
  2. 允许算法人员自己定义component, 使用方式和本地变成类似
  3. Pipeline能够可视化的将各个component连接在一起
  4. 模型推理使用TFServing发布