当前位置: 首页 > 科技观察

动手:我的深度学习模型训练好了,接下来我该做什么?

时间:2023-03-15 23:16:12 科技观察

大数据文摘编译:江凡波、云舟本文讲述的是如何快速部署一个训练好的机器学习模型,并将其应用到实践中。如果您已经使用Tensorflow或Caffe等框架成功地训练了机器学习模型,并且现在您正在尝试让模型快速演示,那么这篇文章是适合阅读的。从stdin检查tensorflow安装从stdin运行在线分类在本地运行分类将分类器放在硬编码代理上将分类器放在具有服务发现的代理上使用假DNS调用分类器机器学习的实际应用当我们第一次进入Hive的机器学习空间时,我们已经为我们的实际应用场景提供了数百万张准确标记的图像,这使我们能够在一周内从头开始。开始训练最先进的深度卷积神经网络图像分类模型(即随机权重)。但是,在更典型的应用场景中,图像通常在数百个数量级,我建议对现有模型进行微调。例如,https://www.tensorflow.org/tutorials/image_retraining有一个示例数据集,介绍如何微调Imagenet模型(在1.2M图像上训练的1000个类别)以对花卉(3647张图像,5个类别)进行分类。简单来说,上面的Tensorflow教程就是在安装bazel和tensorflow之后,需要运行下面的代码,建模大概需要30分钟,训练需要5分钟:(cd"$HOME"&&\curl-Ohttp://下载.tensorflow.org/example_images/flower_photos.tgz&&\tarxzfflower_photos.tgz;)&&\bazelbuildtensorflow/examples/image_retraining:retrain\tensorflow/examples/image_retraining:label_image\&&\bazel-bin/tensorflow/examples/intraining\image--re_trainimage_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映像:sudodockerrun-it--net=hostliuweiwei/simple-ml-serving:latest/bin/bash>>>cattest.sh&&bashtest.sh这将进入容器内的交互式shell并运行以上命令;如果您愿意,也可以跟随容器内部的其余部分。现在,tensorflow已经将模型信息保存到/tmp/output_graph.pb和/tmp/output_labels.txt中,作为命令行参数传递给label_image.py脚本。Google的image_recognition教程也链接到另一个脚本,但我们仍将在这里使用label_image.py。将本地运行转换为在线运行(Tensorflow)如果我们只想接受来自标准输入的文件名,每行一个,我们可以轻松地进行“在线”运行:whilereadline;dobazel-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脚本。对我来说,这个脚本的位置是:inbazel-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:forlineinsys.stdin:修改后马上就快多了,不过这个还不行***。141:run_graph(image_data,labels,FLAGS.input_layer,FLAGS.output_layer,142:FLAGS.num_top_predictions)141:forlineinsys.stdin:原因是与tf.Session()一起使用来构建对话。每次调用run_graph时,Tensorflow基本上都会将所有计算加载到内存中。一旦您开始尝试在GPU上进行计算,这一点就会变得很明显——您可以看到GPU内存使用随着Tensorflow从GPU加载和卸载模型参数而上下波动。据我所知,这种结构在Caffe或Pytorch框架中是不存在的。解决办法是去掉with命令,传一个sess变量给run_graph:=sess.run(softmax_tensor,{input_layer_name:image_data})#Sorttoshowlabelsinorderofconfidencetop_k=predictions.argsort()[-num_top_predictions:][::-1]fornode_idintop_k:human_stringid=labels[=preditionnode_id]print('%s(score=%.5f)'%(human_string,score))return[(labels[node_id],predictions[node_id].item())fornode_idintop_k]#numpyfloatsarenotjsonserializable,havetorunitem...withtf.Session()assess:forlineinsys.stdin:run_graph(load_image(line),labels,FLAGS.input_layer,FLAGS.output_layer,FLAGS.num_top_predictions,sess)如果你运行这个部分,你会发现每张图只需要大约0.1秒,这对于在线应用程序来说已经足够快了。将本地运行转换为在线运行(其他ML框架)Caffe使用net.forward代码,可以轻松将其放入可调用框架:参见http://nbviewer.jupyter.org/github/BVLC/caffe/blob/master/examples/00-classification.ipynbMxnet也很独特:它实际上为公众准备了服务器代码。部署我们的计划是将此代码包装到Flask应用程序中。如果你还没有听说过Flask,简单的解释一下,Flask是一个非常轻量级的Pythonweb框架,它允许你以最小的努力启动一个httpapi服务器。作为快速参考,这是一个接受带有多部分表单数据的POST请求的Flask应用程序:#!/usr/bin/envpython#usage:pythonecho.pytolaunchtheserver;andtheninanothersession,do#curl-v-XPOST127.0.0。1:12480-F"data=@./image.jpg"fromflaskiimportFlask,requestapp=Flask(__name__)@app.route('/',methods=['POST'])defclassify():try:data=request。files.get('data').read()printrepr(data)[:1000]returndata,200exceptExceptionase:returnrepr(e),500app.run(host='127.0.0.1',port=12480)这里是如何转换thecorresponding的FLASK应用程序连接到上面的run_graph:Andhereisthecorrespondingflaskapphookeduptorun_graphabove:#!/usr/bin/envpython#usage:bashtf_classify_server.shfromflaskimportFlask,requestimporttensorflowastfimportlabel_imageastf_classifyimportjsonapp=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'])defclassify():try:data=request.files。get('数据').read()result=tf_classify.run_graph(数据、标签、FLAGS.input_layer,FLAGS.output_layer,FLAGS.num_top_predictions,sess)returnjson.dumps(result),200exceptExceptionase:returnrepr(e),500app.run(host='127.0.0.1',port=12480)模型部署到此为止看起来不错,除了一件事-要求Flask和Tensorflow完全同步-Flask按照接收请求的顺序一次处理一个请求,而Tensorflow在进行图像分类时完全占用线程。速度瓶颈可能还是在实际的计算工作中,所以升级Flask打包代码意义不大。现在,也许这段代码足以处理您的负载。有两种明显的方法可以扩展请求的吞吐量:通过增加工作人员的数量来横向扩展,这将在下一节中介绍,或者通过使用GPU和批处理逻辑来扩展。实现后者需要一个web服务器能够同时处理多个挂起的请求,并决定是继续等待更大的batch还是将其发送到Tensorflowgraph线程进行分类,这对于这个Flask应用来说是非常不合适的。有两种可能性:使用Twisted+Klein来保留Python代码,或者如果您更喜欢最新的事件循环支持以及连接到非PythonML框架(如Torch)的能力,则使用Node.js+ZeroMQ。缩放:负载平衡和服务发现因此,假设您只有一台服务器可以部署您的模型,并且因为它太慢,或者我们的负载太高,您想要启动更多服务器-如何在多个服务器上分发请求?传统的方法是添加一个代理层,可能是haproxy或nginx,它可以平衡后端服务器之间的负载,同时为客户端提供统一的接口。为了在本节后面使用,下面是一些运行基本Node.js负载均衡器http代理的示例代码://Usage:nodebasic_proxy.jsWORKER_PORT_0,WORKER_PORT_1,...constworker_ports=process.argv[2].split(',')if(worker_ports.length===0){console.err('missingworkerports');process.exit(1)}constproxy=require('http-proxy').createProxyServer({})proxy.on('error',()=>console.log('proxyerror'))leti=0require('http').createServer((req,res)=>{proxy.web(req,res,{target:'http://localhost:'+worker_ports[(i++)%worker_ports.length]})}).listen(12480)console.log(`Proxyinglocalhost:${12480}to[${worker_ports.toString()}]`)//spinuptheMLworkersconst{exec}=require('child_process')worker_ports.map(port=>exec(`/bin/bash./tf_classify_server.sh${port}`))为了自动检测后端服务器的数量和位置,people通常使用“服务发现”工具,它可以与负载均衡器捆绑在一起,也可以单独使用。一些著名的例子是Consul和Zookeeper。设置和学习使用它们超出了本文的范围,因此我使用了一个非常基本的代理,该代理是通过node.js服务发现包seport实现的。代理代码://Usage:nodeseaport_proxy.jsconstseaportServer=require('seaport').createServer()seaportServer.listen(12481)constproxy=require('http-proxy').createProxyServer({})proxy.on('error',()=>console.log('proxyerror'))leti=0require('http').createServer((req,res)=>{seaportServer.get('tf_classify_server',worker_ports=>{constthis_port=worker_ports[(i++)%worker_ports.length].portproxy.web(req,res,{target:'http://localhost:'+this_port})})}).listen(12480)console.log(`Seaportproxylisteningon${12480}to'${'tf_classify_server'}'serversregisteredto${12481}`)Workercode://Usage:nodetf_classify_server.jsconstport=require('seaport').connect(12481).register('tf_classify_server')console.log(`Launchingtfclassifyworkeron${port}`)require('child_process').exec(`/bin/bash./tf_classify_server.sh${port}`)但是,此设置在应用于机器学习时遇到了带宽问题。每秒数十到数百张图像,该系统成为网络带宽的瓶颈。在当前设置中,所有数据都必须通过我们的单一海港主节点,这也是呈现给客户的端点。为了解决这个问题,我们需要我们的客户端不访问http://127.0.0.1:12480端点,而是通过后端服务器之间的自动轮换来访问它。如果你了解互联网,你一定会想:DNS不就是干这个的吗!但是,设置自定义DNS服务器超出了本文的范围。相反,通过更改客户端以遵循两步“手动DNS”协议,我们可以重新使用我们的基本海港代理来实现客户端直接连接到其服务器的“点对点”协议:代理代码://用法:nodep2p_proxy.jsconstseaportServer=require('seaport').createServer()seaportServer.listen(12481)leti=0require('http').createServer((req,res)=>{seaportServer.get('tf_classify_server',worker_ports=>{constthis_port=worker_ports[(i++)%worker_ports.length].portres.end(`${this_port}`)})}).listen(12480)console.log(`P2Pseaportproxylisteningon${12480}to'tf_classify_server'serversregisteredto${12481}`)(Worker代码同上)客户端代码:curl-v-XPOSTlocalhost:`curllocalhost:12480`-F"data=@$HOME/flower_photos/daisy/21652746_cc379e0eea_m.jpg"结论和进一步阅读到目前为止你的系统应该可以进入实际应用程序,但它总是要开发。本指南中未涵盖几个重要主题:1.在新硬件上自动部署和设置。著名的工具包括Openstack/VMware(如果您使用自己的硬件)、Chef/Puppet(用于安装Docker和处理网络路由)和Docker(用于安装Tensorflow、Python等)。如果你在云端,Kubernetes或Marathon/Mesos也很棒2.模型版本管理一开始手动管理不难。TensorflowServing是一个很棒的工具,可以非常彻底地处理这个问题,以及批处理和整体部署。缺点是客户端的设置和编码有点困难,而且它不支持Caffe/PyTorch。3.如何从Matlab迁移机器学习代码。不要使用Matlab4。GPU驱动、Cuda、CUDNN在生产阶段使用nvidia-docker,尝试其他在线Dockfiles。5.后处理层。一旦您在生产中获得了几个不同的ML模型,您可能会开始想要混合和匹配不同的用例-仅当模型B不确定时运行模型A,在Caffe中运行模型C并报告结果传递给Tensorflow中的模型D等来源:https://thehive.ai/blog/simple-ml-serving?utm_campaign=Revue%20newsletter&utm_medium=Newsletter&utm_source=The%20Wild%20Week%20in%20AI《大数据文摘(id:BigDataDigest)》]点击在这里可以看到作者更多的好文章