这篇文章介绍了一种快速的方法,将经过训练的机器学习模型部署到生产中。
请阅读以下内容:如果您已经成功地使用诸如Tensorflow或Caffe这样的ML框架来培训ML模型,那么您最好先做一个演示,最好早一点而不是晚一点,而且您更喜欢更便捷的解决方案,而不是将整个技术堆栈都打包。
阅读时间:10-15分钟
TL;DR:检查报告
检查您的tensorflow安装
从stdin运行在线分类
在本地主机上运行在线分类
将分类器放在硬编码代理之后
将分类器置于具有服务发现的代理之后
使用伪DNS调用分类器
当我们第一次进入Hive的机器学习空间时,我们已经拥有了数百万个标记为图像的地面实况,这使得我们可以在一个星期内训练一个最先进的深度卷积图像分类模型(即随机权重),专门用于我们的用例。 然而,更典型的ML使用案例通常是数百幅图像的数量级,为此我建议微调现有的模型。例如,https://www.tensorflow.org/tutorials/image_retraining有一个很好的教程关于如何微调Imagenet模型(训练1.2图片,1000类)来对花卉样本数据(3647图片,5类)进行分类。
对于连接Tensorflow教程的快速tl;dr,在安装bazel和tensorflow之后,您需要运行以下代码,这需要大约30分钟的时间来搭建和5分钟的时间来训练:
(
cd "$HOME" && \
curl -O http://download.tensorflow.org/example_images/flower_photos.tgz && \
tar xzf flower_photos.tgz ;
) && \
bazel build tensorflow/examples/image_retraining:retrain \
tensorflow/examples/image_retraining:label_image \
&& \
bazel-bin/tensorflow/examples/image_retraining/retrain \
--image_dir "$HOME"/flower_photos \
--how_many_training_steps=200
&& \
bazel-bin/tensorflow/examples/image_retraining/label_image \
--graph=/tmp/output_graph.pb \
--labels=/tmp/output_labels.txt \
--output_layer=final_result:0 \
--image=$HOME/flower_photos/daisy/21652746_cc379e0eea_m.jpg
或者,如果您安装了Docker,则可以使用以下预构建的Docker镜像:
sudo docker run -it --net=host liubowei/simple-ml-serving:latest /bin/bash
>>> cat test.sh && bash test.sh
在容器内的交互式shell中运行上述命令;如果您愿意,您也可以跟着这个文章的其他部分在容器内操作。
现在,tensorflow已经将模型信息保存到/tmp/output_graph.pb和/tmp/output_labels.txt中,这些作为命令行参数传递给label_image.py脚本。 Google的image_recognition教程也链接到另一个推理脚本,但是现在我们将坚持使用label_image.py。
如果我们只想接受来自标准输入的文件名,每行一个,我们就可以很容易地进行“在线”推断:
while read line ; do
bazel-bin/tensorflow/examples/image_retraining/label_image \
--graph=/tmp/output_graph.pb --labels=/tmp/output_labels.txt \
--output_layer=final_result:0 \
--image="$line" ;
done
从性能的角度来看,这很糟糕——我们正在重新加载神经网络、权重、整个Tensorflow框架和python本身,对于每个输入示例都是如此!
我们可以做得更好。 让我们开始编辑label_image.py脚本——对于我来说,它位于bazel-bin / tensorflow / examples / image_retraining / label_image.runfiles / org_tensorflow / tensorflow / examples / image_retraining / label_image.py中。
让我们改变行
141: run_graph(image_data, labels, FLAGS.input_layer, FLAGS.output_layer,
142: FLAGS.num_top_predictions)
到
141: for line in sys.stdin:
142: run_graph(load_image(line), labels, FLAGS.input_layer, FLAGS.output_layer,
142: FLAGS.num_top_predictions)
这确实快了很多,但是这还不是我们能做的最好的!
原因是tf.Session()在第100行上的sess构造。Tensorflow基本上每次调用run_graph时都将所有的计算加载到内存中。当您开始尝试在GPU上进行推理时,这一点就变得很明显了——您可以看到GPU内存随着Tensorflow在GPU上加载和卸载模型参数而上下移动。据我所知,这种结构并不存在于其他ML框架中,比如Caffe或Pytorch。
然后解决方案是拉出with语句,并传递一个sess变量到run_graph:
(在https://github.com/hiveml/simple-ml-serving/blob/master/label_image.py上看代码)
如果你运行这个程序,你会发现每张图片加载大约需要0.1秒,对于在线使用来说足够快。
Caffe使用它的net.forward代码,这个代码很容易放入可调用的框架中:请参阅http://nbviewer.jupyter.org/github/BVLC/caffe/blob/master/examples/00-classification.ipynb
Mxnet也是非常独特的:它实际上具有开源的随时可用的推理服务器代码:https://github.com/awslabs/mxnet-model-server。
计划是将这些代码包装在Flask应用程序中。 如果您还没有听说过它,Flask是一个非常轻量级的Python Web框架,它允许您用最少的工作来创建一个http api服务器。
作为一个快速的参考,这是一个flask应用程序,它接收来自多个表单数据的POST请求:
这是对应的flask应用程序连接到run_graph上面:
这看起来很好,除了flask和tensorflow都是完全同步的——按照接收到的顺序一次处理一个请求,而Tensorflow在进行图像分类时完全占用线程。
正如它所写的那样,速度瓶颈可能还在实际的计算工作中,所以升级Flask包装代码没有太多的意义。现在,也许这段代码足够处理您的负载了。
有两种很明显的方法可以提高请求的通用性:通过增加worker的数量来横向放大,这在下一节将会介绍,或者通过使用GPU和批处理逻辑来纵向扩展。实现后者需要一个能够同时处理多个挂起请求的web服务器,并决定是否继续等待更大的批处理,或者将其发送到Tensorflow图形线程,以便进行分类,而这对于Flask应用程序是非常不适合。有两种可能性,使用Twisted + Klein来保存Python代码,或者如果您更喜欢第一个类的事件循环支持,并且能够连接到非Python ML框架(如Torch),则可以使用Node.js + ZeroMQ。
好的,现在我们有一台服务器来服务我们的模型,但也许它太慢了,或者我们的负载变得太高了。我们想要启动更多的这些服务器——我们如何在每个服务器上分配请求?
普通的方法是添加一个代理层,可能是haproxy或nginx,它平衡后端服务器之间的负载,同时向客户机提供一个统一的接口。为了在本节稍后使用,以下是运行基本Node.js负载均衡器http代理的一些示例代码:
为了自动检测后端服务器的数量和位置,人们通常会使用“服务发现”工具,该工具可能与负载平衡器捆绑在一起或者是单独的。一些知名的是Consul和Zookeeper。设置和学习如何使用它不在本文的讨论范围之内,所以我使用了node.js服务发现包seaport来包含一个非常基本的代理。
代理代码:
Worker 代码:
但是,对于ML,这个设置会遇到带宽问题。
在任何地方,每秒几十到几百张图像,系统就会被网络带宽阻塞。在目前的设置中,所有的数据都必须通过我们的单个seaport 主站,这是呈现给客户端的单个端点。
为了解决这个问题,我们要求我们的客户端不在http://127.0.0.1:12480点击单个端点,而是在后端服务器之间自动轮换点击。如果你知道一些网络,这听起来就像DNS的工作!
但是,设置自定义的DNS服务器已经超出了本文的范围。 相反,通过更改客户端来遵循两步“手动DNS”协议,我们可以重新使用我们的基本seaport代理来实现客户端直接连接到其服务器的“点对点”协议:
代理代码:
(worker代码与上述相同)
客户端代码:
即将到来!上面的Flask版本被ZeroMQ替换。
在这一点上,你应该有一些在生产中工作的东西,但它肯定不是未来的保障。本指南中没有涉及到的几个重要的主题:
本文由北邮@爱可可-爱生活老师推荐,阿里云云栖社区组织翻译。
文章原标题《Step-by-step Guide to Deploying Deep Learning Models》
作者:Bowei 译者:董昭男 审核:袁虎
文章为简译,更为详细内容,请查看原文