手把手:我的深度学习模型训练好了,然后要做啥?

简介:

本文讲的是如何快速而不求完美地部署一个训练好的机器学习模型并应用到实际中。如果你已经成功地使用诸如Tensorflow或Caffe这样的框架训练好了一个机器学习模型,现在你正在试图让这个模型能够快速的演示,那么读这篇文章就对了。

阅读时长: 10-15分钟

使用前检查清单

  • 检查tensorflow的安装
  • 从 stdin 运行在线分类
  • 在本地运行分类
  • 把分类器放到硬编码(hardcoded)的代理
  • 把分类器放到有服务发现(service discovery)的代理
  • 用一个伪DNS调用分类器

机器学习的实际应用

当我们第一次进入Hive的机器学习空间时,针对我们的实际应用场景,我们已经拥有了数百万张准确标记的图像,这些图像使我们能够在一周之内,从头开始训练最先进的深度卷积神经网络图像分类模型(即随机权重)。然而,在更典型的应用场景中,图像的数量级通常只有数百幅,这种情况下,我建议微调现有的模型。比如,https://www.tensorflow.org/tutorials/image_retraining有一个关于如何微调Imagenet模型(在1.2M图像上训练1000个类别)以对花进行分类的样本数据集(3647个图像, 5个类别)。

上面的Tensorflow教程简要而言,是在安装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。

将本地运行转换为在线运行(Tensorflow)

如果我们只想接受来自标准输入的文件名,每行一个,我们就可以很容易地进行“在线”运行:

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 脚本。对我而言,这个脚本的位置在:

in 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:

修改后马上快了很多,但这还不是最好。

141:  for line in sys.stdin:
142:    run_graph(load_image(line), labels, FLAGS.input_layer, FLAGS.output_layer,
142:        FLAGS.num_top_predictions)

原因在于用with tf.Session()构建对话。Tensorflow本质上是在每次调用run_graph时将所有的计算加载到内存中。一旦开始尝试在GPU上进行运算,这一点就会变得很明显——可以看到GPU内存使用随着Tensorflow加载和卸载GPU的模型参数而上下波动。据我所知,这种结构并不存在于Caffe或Pytorch框架中。

解决方法是把with命令去掉,传递一个sess变量到run_graph:

def run_graph(image_data, labels, input_layer_name, output_layer_name,

             num_top_predictions, sess):

   # Feed the image_data as input to the graph.

   #   predictions will contain a two-dimensional array, where one

   #   dimension represents the input image count, and the other has

   #   predictions per class

   softmax_tensor = sess.graph.get_tensor_by_name(output_layer_name)

   predictions, = sess.run(softmax_tensor, {input_layer_name: image_data})

   # Sort to show labels in order of confidence

   top_k = predictions.argsort()[-num_top_predictions:][::-1]

   for node_id in top_k:

     human_string = labels[node_id]

     score = predictions[node_id]

     print('%s (score = %.5f)' % (human_string, score))

   return [ (labels[node_id], predictions[node_id].item()) for node_id in top_k ] # numpy floats are not json serializable, have to run item



...



 with tf.Session() as sess:

   for line in sys.stdin:

     run_graph(load_image(line), labels, FLAGS.input_layer, FLAGS.output_layer,

         FLAGS.num_top_predictions, sess)

如果你运行完这一段,你会发现每张图只需要大约0.1秒,对于在线应用来说已经够快了。

将本地运行转换为在线运行(其他ML框架)

Caffe使用net.forward代码,很容易被放入一个可调用的框架中:see http://nbviewer.jupyter.org/github/BVLC/caffe/blob/master/examples/00-classification.ipynb

Mxnet也是非常独特的:它实际上已经准备好了面向大众的服务器代码。

部署

我们的计划是,将这些代码包装到一个Flask应用程序中。如果你没有听说Flask,简单解释一下,Flask是一个非常轻量级的Python Web框架,它允许你以最少的工作启动一个http api服务器。

作为一个快速参考,这里是一个Flask应用程序,它接收包含多部分表单数据的POST请求:

#!/usr/bin/env python

# usage: python echo.py to launch the server ; and then in another session, do

# curl -v -XPOST 127.0.0.1:12480 -F "data=@./image.jpg"

from flask import Flask, request

app = Flask(__name__)

@app.route('/', methods=['POST'])

def classify():

   try:

       data = request.files.get('data').read()

       print repr(data)[:1000]

       return data, 200

   except Exception as e:

       return repr(e), 500

app.run(host='127.0.0.1',port=12480)

这里是如何将相应的FLASK应用程序连接到上面的run_graph:

And here is the corresponding flask app hooked up to run_graph above:



#!/usr/bin/env python

# usage: bash tf_classify_server.sh

from flask import Flask, request

import tensorflow as tf

import label_image as tf_classify

import json

app = Flask(__name__)

FLAGS, unparsed = tf_classify.parser.parse_known_args()

labels = tf_classify.load_labels(FLAGS.labels)

tf_classify.load_graph(FLAGS.graph)

sess = tf.Session()

@app.route('/', methods=['POST'])

def classify():

   try:

       data = request.files.get('data').read()

       result = tf_classify.run_graph(data, labels, FLAGS.input_layer, FLAGS.output_layer, FLAGS.num_top_predictions, sess)

       return json.dumps(result), 200

   except Exception as e:

       return repr(e), 500

app.run(host='127.0.0.1',port=12480)

模型部署至此看起来还是相当不错的。除了一点——需要FlASK和Tensorflow完全同步——Flask按照接收的顺序一次处理一个请求,并且Tensorflow在进行图像分类时完全占用线程。

速度瓶颈可能还是在实际的计算工作中,所以升级Flask包装代码没有太多的意义。现在,也许这个代码足以处理你的负载。

有两种显而易见的方法可以扩大请求的通量:通过增加工人数量来横向放大,这在下一节将会介绍,或者通过使用GPU和批处理逻辑来纵向扩展。实现后者需要一个能够一次处理多个待处理请求的web服务器,并决定是否继续等待更大的批处理或将其发送到Tensorflow图形线程进行分类,对于这个Flask应用程序是非常不适合的。有两种可能性:使用Twisted + Klein来保留Python代码,或者如果你更喜欢一流的事件循环支持,并且能够连接到非Python ML框架(如Torch),则可以使用Node.js + ZeroMQ。

扩展:负载平衡和服务发现

那么,假设现在你只有一台服务器来部署模型,由于它太慢了,或者我们的负载变得太高了,此时你想要启动更多服务器——如何在每个服务器上分配请求?

常规的方法是添加一个代理层,也许是haproxy或nginx,它能够平衡后端服务器之间的负载,同时向客户端呈现一个统一的接口。为了在本节稍后使用,以下是运行基本Node.js负载均衡器http代理的一些示例代码:

// Usage : node basic_proxy.js WORKER_PORT_0,WORKER_PORT_1,...

const worker_ports = process.argv[2].split(',')

if (worker_ports.length === 0) { console.err('missing worker ports') ; process.exit(1) }



const proxy = require('http-proxy').createProxyServer({})

proxy.on('error', () => console.log('proxy error'))



let i = 0

require('http').createServer((req, res) => {

 proxy.web(req,res, {target: 'http://localhost:' + worker_ports[ (i++) % worker_ports.length ]})

}).listen(12480)

console.log(`Proxying localhost:${12480} to [${worker_ports.toString()}]`)



// spin up the ML workers

const { exec } = require('child_process')

worker_ports.map(port => exec(`/bin/bash ./tf_classify_server.sh ${port}`))

为了自动检测后端服务器的数量和位置,人们通常使用“服务发现”工具,该工具可能与负载平衡器捆绑在一起,或者是分开的。一些知名例子的是Consul和Zookeeper。设置和学习使用它们不在本文的讨论范围之内,所以我使用了一个非常基本的,通过node.js服务发现包seport实现的代理。

Proxy代码:

// Usage : node seaport_proxy.js

const seaportServer = require('seaport').createServer()

seaportServer.listen(12481)

const proxy = require('http-proxy').createProxyServer({})

proxy.on('error', () => console.log('proxy error'))



let i = 0

require('http').createServer((req, res) => {

 seaportServer.get('tf_classify_server', worker_ports => {

   const this_port = worker_ports[ (i++) % worker_ports.length ].port

   proxy.web(req,res, {target: 'http://localhost:' + this_port })

 })

}).listen(12480)

console.log(`Seaport proxy listening on ${12480} to '${'tf_classify_server'}' servers registered to ${12481}`)

Worker代码:

// Usage : node tf_classify_server.js

const port = require('seaport').connect(12481).register('tf_classify_server')

console.log(`Launching tf classify worker on ${port}`)

require('child_process').exec(`/bin/bash ./tf_classify_server.sh ${port}`)

然而,当应用于机器学习时,这个设置遇到了带宽问题。

每秒几十到几百张图像,这个系统就会成为网络带宽的瓶颈。在目前的设置中,所有的数据都必须通过我们的单个seaport 主节点,这也是呈现给客户端的端点。

为了解决这个问题,我们需要我们的客户端不要访问http://127.0.0.1:12480这个端点,而是要在后端服务器之间通过自动轮换来访问。如果你懂网络,一定会想:这不就是DNS干的活嘛!

但是,设置自定义的DNS服务器已经超出了本文的范围。相反,通过更改客户端以遵循两步“手动DNS”协议,我们可以重新使用我们的基础版的seaport 代理来实现客户端直接连接到其服务器的“点对点”协议:

Proxy代码:

// Usage : node p2p_proxy.js

const seaportServer = require('seaport').createServer()

seaportServer.listen(12481)



let i = 0

require('http').createServer((req, res) => {

 seaportServer.get('tf_classify_server', worker_ports => {

   const this_port = worker_ports[ (i++) % worker_ports.length ].port

   res.end(`${this_port}

`)

 })

}).listen(12480)

console.log(`P2P seaport proxy listening on ${12480} to 'tf_classify_server' servers registered to ${12481}`)
(Worker 代码同上)

Client代码:

curl -v -XPOST localhost:`curl localhost:12480` -F"data=@$HOME/flower_photos/daisy/21652746_cc379e0eea_m.jpg"

 结论和进一步阅读

至此你的系统应该可以进入实际应用了,但它总是要发展的。本指南中未涉及几个重要的主题:

新硬件上的自动部署和设置。

  • 值得注意的工具包括Openstack / VMware(如果您使用的是自己的硬件),Chef / Puppet(用于安装Docker并处理网络路由)以及Docker(用于安装Tensorflow,Python等)。
  • 如果你在云端,Kubernetes或Marathon / Mesos也很棒

模型版本管理

  •  一开始手动管理不难。
  • Tensorflow Serving是一个很好的工具,可以非常彻底地处理这个问题,以及批处理和整体部署。 缺点是设置和编写客户端代码有点难,另外不支持Caffe / PyTorch。

如何将机器学习代码从Matlab中迁移出来。

  •  在生产阶段不要用Matlab

GPU驱动,Cuda,CUDNN

  • 使用nvidia-docker,试试其它的在线Dockfiles。

后处理层。

  • 一旦你在生产中得到了一些不同的ML模型,你可能会开始想要混合和匹配不同的用例——只有在模型B不确定的情况下才运行模型A,在Caffe中运行模型C并将结果传递给模型D在Tensorflow 等等。



原文发布时间为:2018-02-7

本文作者:文摘菌

本文来自云栖社区合作伙伴“大数据文摘”,了解相关信息可以关注“大数据文摘”微信公众号

相关实践学习
在云上部署ChatGLM2-6B大模型(GPU版)
ChatGLM2-6B是由智谱AI及清华KEG实验室于2023年6月发布的中英双语对话开源大模型。通过本实验,可以学习如何配置AIGC开发环境,如何部署ChatGLM2-6B大模型。
相关文章
|
7月前
|
机器学习/深度学习 算法 定位技术
Baumer工业相机堡盟工业相机如何通过YoloV8深度学习模型实现裂缝的检测识别(C#代码UI界面版)
本项目基于YOLOv8模型与C#界面,结合Baumer工业相机,实现裂缝的高效检测识别。支持图像、视频及摄像头输入,具备高精度与实时性,适用于桥梁、路面、隧道等多种工业场景。
940 27
|
6月前
|
机器学习/深度学习 数据可视化 算法
深度学习模型结构复杂、参数众多,如何更直观地深入理解你的模型?
深度学习模型虽应用广泛,但其“黑箱”特性导致可解释性不足,尤其在金融、医疗等敏感领域,模型决策逻辑的透明性至关重要。本文聚焦深度学习可解释性中的可视化分析,介绍模型结构、特征、参数及输入激活的可视化方法,帮助理解模型行为、提升透明度,并推动其在关键领域的安全应用。
587 0
|
5月前
|
机器学习/深度学习 存储 PyTorch
Neural ODE原理与PyTorch实现:深度学习模型的自适应深度调节
Neural ODE将神经网络与微分方程结合,用连续思维建模数据演化,突破传统离散层的限制,实现自适应深度与高效连续学习。
422 3
Neural ODE原理与PyTorch实现:深度学习模型的自适应深度调节
|
4月前
|
机器学习/深度学习 数据采集 人工智能
深度学习实战指南:从神经网络基础到模型优化的完整攻略
🌟 蒋星熠Jaxonic,AI探索者。深耕深度学习,从神经网络到Transformer,用代码践行智能革命。分享实战经验,助你构建CV、NLP模型,共赴二进制星辰大海。
|
9月前
|
机器学习/深度学习 人工智能 供应链
从概念到商业价值:AI、机器学习与深度学习全景指南
在这个科技飞速发展的时代🚀,人工智能正以惊人的速度渗透到我们的生活和工作中👀。但面对铺天盖地的AI术语和概念,很多人感到困惑不已😣。"AI"、"机器学习"、"深度学习"和"神经网络"到底有什么区别?它们如何相互关联?如何利用这些技术提升工作效率和创造价值?
495 0
|
7月前
|
机器学习/深度学习 人工智能 PyTorch
AI 基础知识从 0.2 到 0.3——构建你的第一个深度学习模型
本文以 MNIST 手写数字识别为切入点,介绍了深度学习的基本原理与实现流程,帮助读者建立起对神经网络建模过程的系统性理解。
745 15
AI 基础知识从 0.2 到 0.3——构建你的第一个深度学习模型
|
5月前
|
机器学习/深度学习 数据采集 传感器
【WOA-CNN-LSTM】基于鲸鱼算法优化深度学习预测模型的超参数研究(Matlab代码实现)
【WOA-CNN-LSTM】基于鲸鱼算法优化深度学习预测模型的超参数研究(Matlab代码实现)
366 0
|
7月前
|
机器学习/深度学习 人工智能 自然语言处理
AI 基础知识从 0.3 到 0.4——如何选对深度学习模型?
本系列文章从机器学习基础出发,逐步深入至深度学习与Transformer模型,探讨AI关键技术原理及应用。内容涵盖模型架构解析、典型模型对比、预训练与微调策略,并结合Hugging Face平台进行实战演示,适合初学者与开发者系统学习AI核心知识。
592 15
|
7月前
|
机器学习/深度学习 人工智能 自然语言处理
深度学习模型、算法与应用的全方位解析
深度学习,作为人工智能(AI)的一个重要分支,已经在多个领域产生了革命性的影响。从图像识别到自然语言处理,从语音识别到自动驾驶,深度学习无处不在。本篇博客将深入探讨深度学习的模型、算法及其在各个领域的应用。
1444 3
|
8月前
|
机器学习/深度学习 存储 PyTorch
PyTorch + MLFlow 实战:从零构建可追踪的深度学习模型训练系统
本文通过使用 Kaggle 数据集训练情感分析模型的实例,详细演示了如何将 PyTorch 与 MLFlow 进行深度集成,实现完整的实验跟踪、模型记录和结果可复现性管理。文章将系统性地介绍训练代码的核心组件,展示指标和工件的记录方法,并提供 MLFlow UI 的详细界面截图。
362 2
PyTorch + MLFlow 实战:从零构建可追踪的深度学习模型训练系统