【工程】深度学习模型部署的那些事儿

2020-02-20 12:17:09 浏览数 (1)

以下文章来源于AI实战派 ,作者AI实战派

当我们千辛万苦完成了前面的数据获取、数据清洗、模型训练、模型评估等等步骤之后,终于等到老大说“上线”啦。想到辛苦训练出来的模型要被调用还有点小激动呢,可是真当下手的时候就有点懵了:模型要怎么部署?部署在哪里?有什么限制或要求?

本文基于以上常见的部署困惑,介绍一下深度学习中TensorFlow(Keras)模型部署时需要考虑的问题以及常用部署方法,并通过实际的模型部署来进行方案比较。

01

问题分析

笔者较为推荐的思考路径是从问题和限制出发,思考好了这些限制条件之后再去搜索对应的解决方案,这样就可以避免限于方案过多挑不过来的窘境,并且若是挑不到完美的方案,还可以根据条件对现有方案进行改造。

现在就来看看部署模型我们通常需要考虑什么问题。

首先是模型的使用场景,根据使用场景就可以获取到对应的性能等要求。举个例子来说,如果是要上线给用户使用的,哦吼,这块最起码速度要求就很高,毕竟用户可不像自己人,没有多少耐心一直等你的加载圈转呀转,具体的指标根据场景和应用而定,不过一般会在数毫秒 最多可以到数十毫秒每条的样子。

此外,如果是基于网络调用那还好说,机器在自己手上,土豪就可以各显神通了,如果是离线的手机APP,那就还有大小的要求,毕竟动不动让你下个几百M的app看着也烦呀。

接下来,如果是基于网络调用的,还要看看自己手里的资源够不够,是壕到有专门的GPU用于部署呢,还是只能苦逼地使用CPU呢,如果是GPU部署,那可以偷笑了,GPU部署服务方便的同时性能又好,毕竟各种各样的优化都是苦于资源不够,对于大伙儿来说可以躺赚谁愿意坐着赚钱啊。

好吧,CPU就CPU,然而CPU服务器里头也是可以区别的,是选择有装好例如Python、TensorFlow等环境的呢,还是赤条条的要么自己装要么另寻出路的。

02

方案介绍

接下来,本文会根据几种常见的限制组合来列出对应的解决方案,需要部署到手机端的话TensorFlow已经给出了TensorFlow Lite的解决方案,笔者暂未接触到手机部署相关的需求,就不深入介绍了。接下来就以最常见的服务提供方部署模型,通过网络调用方式通信介绍两套方案。

基于预测改造的方案

模型在测试集上进行测试知道吧?读取文件后调用模型对每条case进行预测,最后将所有预测结果和正确答案对比,计算出各种指标。整个流程如下图:

而在提供服务时,输入是由外部传入的,因此不需要前面的读取文件操作,最后提供预测结果即可,也不需要最后的对比、计算指标的步骤。为了处理接收外部输入、返回预测结果,并做一些业务逻辑相关的处理,需要引入一个处理中心,这个处理中心通常是web框架 如 flask、tornado、Django等,其作用是搭建http服务,将外部输入传给模型,将模型预测结果返回。流程如下图:

再来梳理一下流程,在同一台机器上通过web框架如flask等搭建好对外服务,初始化时加载好模型。当外部应用服务例如搜索应用的输入到来时,例如传来一句话,或者上传了一张图片,对外服务就将输入传给预处理函数,将输入处理成可以给模型预测的样子,例如做标准化、去噪等等,随后模型进行预测,并将结果返回给对外服务,服务将结果包装一下返回给搜索应用回去。

一个搜索应用会有很多个模型和子模块构成,那么对于每个模型都需要有一个对外服务来对模型进行封装并提供通信接口。

整个过程并没有多少难点,搭建http服务使用简单的demo即可,然后将预测函数改造改造就可以啦,最终的性能视乎部署的设备及模型深度而定。

基于TF-Serving的方案

如果在要部署的服务器上不愿或无法装和训练一样的环境,也可以使用TensorFlow推出的Serving工具,此处对小白来说更推荐基于docker的安装方式,只需要安好对应镜像即可,否则得自己编译安装。基于docker安装的话需要提前想好是用cpu版本还是gpu版,根据版本拉取对应的镜像。

那这个TF-Serving是什么样的呢?先来看看基于TF-Serving后最终整个流程是怎么工作的:

如图,在TF-Serving流程上一般会用到两台机器(或更多),其中一台作为TF-Serving的服务器,专门给模型用来部署并预测,应用服务放在另外的服务器,跟其他服务共享环境。

我们将模型部署在TF-Serving的服务器上,TF-Serving会自动根据传入的端口和模型路径进行部署,模型所在的服务器不需要python环境(以python训练为例),随后应用服务直接对模型所在服务器发起服务调用,调用可以通过java或python的grpc进行调用,具体调用方式参考当前使用版本的手册。

不过TF-Serving一个很大的坑在于:数据预处理的部分需要交给应用服务做,TF-Serving只接收张量输入,如文本分类的模型,它就只接收序列的id,而非句子本身,并且输出返回的是id而非文本化的标签,因此也需要额外一层转换。

然后来看看怎么将模型部署到TF-Serving上:

流程比较简单,首先在GPU服务器上训练好模型后,将模型保存好,再根据网上的转换脚本转换成TF-Serving接受的格式,不论使用Tensorflow还是Keras都不影响(pytorch麻烦出门右转),只是保存的函数不同而已,查好具体用哪个函数即可,最后将生成出来的文件上传到安装了TF-Serving的服务器,启动服务即可。

03

方案对比

接下来本文先以一个实际的BERT-base Fasttext文本二分类模型为例子,分别用以上两个方案用GPU和CPU进行部署,看看他们性能之间的差异,最后在总结一下不同方案的适用场景和差异。

部署性能对比

GPU 基于 模型预测改造方案

首先上GPU:

Tesla V100 训练起来很给力,那么预测起来时间怎么样呢?笔者以1500个句子作为输入,在服务器上搭建好http服务后远程调用,最终平均时间是0.0102s/条,10毫秒每条,对于大多数线上使用来说算是还可以了,毕竟BERT的体积之大和推理之慢但效果之好是大家有目共睹的。

CPU 基于 模型预测改造方案

那么把这个模型移到CPU上又会怎么样呢?在Intel(R) Xeon(R) Gold 61xx CPU 2*32 上采用同样的改造和服务搭建方式,同样的1.5k句子跑起来,最终平均时间是0.390s/条。

看到这个结果,笔者一开始瞪大了眼睛,仔细确认了好几遍数量级,最终无可奈何的接受了这个现实,390ms/条的速度,拿给内部自己人调用都要嫌慢吧。然后回头去某东上搜了下V100,好吧。。人家快是有道理的。

CPU 基于 TF-Serving 方案

那TensorFlow官方强推的Serving工具表现怎么样呢?或许经过推理优化之后运行速度或许会快一些吧?实验结果也证实了这个假设,cpu版本的TF-Serving平均耗时为0.292 s/条, 比直接调用足足快了100ms,看来强推还是有道理的。

部署优化

虽然知道业界对BERT推理过慢广泛诟病,但在GPU和CPU下差距如此之大还是有些出乎意料。吃惊归吃惊,还得看看有没有优化的方法。如果我们把CPU下耗时极大的BERT放到GPU会怎么样?将原始的BERT作为服务放到GPU上,这样平时训练和线上预测取embedding都从这里取,这样我们只需要部署接在BERT后面的小模型即可,哪怕是用CPU也扛得住吧?虽然会牺牲微调BERT带来的提升,但是毕竟可以上线呀,比因性能太差而放弃要好多了。

在GPU上部署BERT服务,这里就要用到笔者非常喜欢的 bert-as-service了,它是python的一个bert库,能够让bert作为服务,接收句子输入返回bert编码的句子向量。

部署之后测试一下性能,不使用batch的单条预测下平均耗时是0.0073s/条,7ms,可以接受。那分离开来的fasttext模型呢?部署在CPU的TF-Serving后平均耗时是0.0028s/条,3ms,也可以接受,总耗时竟然跟整个模型放在GPU上是一样的,瓶颈果然还是在BERT呀。

这套方案同样可以迁移到其他场景,例如将用户的各种信息训练好Embedding后作为公共资源由模型各自调用,或者可以将耗内存较大的word2vec embedding文件放到有较多内存的机器上,再通过网络进行数据传输。

04

方案总结

现在来总结一下上边的实验,先看一下以上三种方案的流程图(每个黑色框代表一台机器):

然后来看一下方案分别的耗时:

首先要说的是,能上GPU一定要上GPU!钞能力节省非常多的时间和精力。然后我们挨个方案来说说优缺点。

第一套方案就是简单地将测试模式改造成连续预测模式,通过web框架进行转发和包装。

好处:修改简单易于上手。

坏处:

  1. 推理性能为三种方案之内最慢的。
  2. 推理服务器需要有python环境。

第二套方案利用TF-Serving进行部署,TF-Serving自动会进行部署和端口监听。

好处:

  1. 速度较方案一快。
  2. 不需要python环境。

坏处:

  1. 安装较麻烦,最好使用docker。
  2. 输入输出都需要转换和处理。

第三套方案是在方案二上的优化,将耗费资源较多的部分放到性能较好的机器上,可以作为公共资源给多个小网络共同使用。

好处(其他同方案二):

  1. 速度最快。

坏处(其他同方案二):

  1. 需要有性能好的机器存放耗资源的网络,这块需要协调。
  2. 多一次网络通信,需要承受网络波动的影响,如果TF-Serving和GPU机器属于不同网络环境则更为麻烦,需要借助V**等手段。
  3. 每个对外服务调用模型需要配置专门的逻辑:从GPU服务器取Embedding,作为输入给TF-Serving,才能得到TF-Serving的输出。

纵观所有方案,发现其实我们可以做一些小融合,以方案三(或方案二)为基础,结合方案一的包装,来去除TF-Serving输入输出需要转换,以及方案三中每个服务需要配置专门逻辑的缺点,流程如下图:

以方案三为例,在TF-Serving的服务器上再增加一层中介,主要做输入输出的转换,再承担中转的作用。

当外部输入到来时,对外服务接收请求,处理后传给GPU机器,得到embedding,而后将embedding传给TF-Serving的模型,得到预测结果后转换成外部需要的格式,最后打包结果返回给其他服务。

这样,方案四既拥有了方案三速度最快的优点,也避免了TF-Serving需要做输出输出转换,以及在TF-Serving与GPU Embedding服务器中来回跑的缺点。

05

最后

本文列举了基于网络调用的几个模型部署的方案,并且根据实际场景进行优化和变型,如果要在此之上要追求极致的性能,就只能自己动手丰衣足食了,例如用C 或Go重写推理部分,或者利用大公司自研的框架进行部署和推理。

The End

0 人点赞